From d6cece1006aa3b2094f6b0080b10bb46a7b36600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 25 Feb 2023 18:16:20 +0100 Subject: [PATCH 01/32] Add max_depth and min_length as hyperparams --- CPPFImdlp.cpp | 28 ++++++++--- CPPFImdlp.h | 8 ++- tests/FImdlp_unittest.cpp | 103 ++++++++++++++++++++++++++++---------- tests/test | 2 +- 4 files changed, 105 insertions(+), 36 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 1990f95..acab4ea 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -8,7 +8,11 @@ namespace mdlp { - CPPFImdlp::CPPFImdlp(): indices(indices_t()), X(samples_t()), y(labels_t()), + CPPFImdlp::CPPFImdlp():depth(0), max_depth(numeric_limits::max()), min_length(3), indices(indices_t()), X(samples_t()), y(labels_t()), + metrics(Metrics(y, indices)) + { + } + CPPFImdlp::CPPFImdlp(int min_length_, int max_depth_): depth(0), max_depth(max_depth_), min_length(min_length_), indices(indices_t()), X(samples_t()), y(labels_t()), metrics(Metrics(y, indices)) { } @@ -25,9 +29,15 @@ namespace mdlp { if (X.empty() || y.empty()) { throw invalid_argument("X and y must have at least one element"); } + if (min_length < 3) { + throw invalid_argument("min_length must be greater than 2"); + } + if (max_depth < 1) { + throw invalid_argument("max_depth must be greater than 0"); + } indices = sortIndices(X_, y_); metrics.setData(y, indices); - computeCutPoints(0, X.size()); + computeCutPoints(0, X.size(), 1); return *this; } @@ -60,12 +70,14 @@ namespace mdlp { return { (actual + previous) / 2, cut }; } - void CPPFImdlp::computeCutPoints(size_t start, size_t end) + void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) { size_t cut; pair result; - if (end - start < 3) + // Check if the interval length and the depth are Ok + if (end - start < min_length || depth_ > max_depth) return; + depth = depth_ > depth ? depth_ : depth; cut = getCandidate(start, end); if (cut == numeric_limits::max()) return; @@ -73,8 +85,8 @@ namespace mdlp { result = valueCutPoint(start, cut, end); cut = result.second; cutPoints.push_back(result.first); - computeCutPoints(start, cut); - computeCutPoints(cut, end); + computeCutPoints(start, cut, depth_ + 1); + computeCutPoints(cut, end, depth_ + 1); } } @@ -158,4 +170,8 @@ namespace mdlp { sort(output.begin(), output.end()); return output; } + int CPPFImdlp::get_depth() + { + return depth; + } } diff --git a/CPPFImdlp.h b/CPPFImdlp.h index 6ff9d7e..d8a23db 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -10,19 +10,23 @@ namespace mdlp { indices_t indices; samples_t X; labels_t y; + int depth, max_depth; + size_t min_length; Metrics metrics; cutPoints_t cutPoints; static indices_t sortIndices(samples_t&, labels_t&); - void computeCutPoints(size_t, size_t); + void computeCutPoints(size_t, size_t, int); bool mdlp(size_t, size_t, size_t); size_t getCandidate(size_t, size_t); pair valueCutPoint(size_t, size_t, size_t); public: CPPFImdlp(); + CPPFImdlp(int, int); ~CPPFImdlp(); CPPFImdlp& fit(samples_t&, labels_t&); - samples_t getCutPoints(); + cutPoints_t getCutPoints(); + int get_depth(); inline string version() { return "1.1.1"; }; }; } diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index 32a9a50..f45d814 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -8,6 +8,7 @@ namespace mdlp { class TestFImdlp: public CPPFImdlp, public testing::Test { public: precision_t precision = 0.000001; + //precision_t precision = 0.000000000001; TestFImdlp(): CPPFImdlp() {} void SetUp() { @@ -25,18 +26,16 @@ namespace mdlp { prev = X[testSortedIndices[i]]; } } - void checkCutPoints(cutPoints_t& expected) + void checkCutPoints(cutPoints_t& computed, cutPoints_t& expected) { - int expectedSize = expected.size(); - EXPECT_EQ(cutPoints.size(), expectedSize); - for (unsigned long i = 0; i < cutPoints.size(); i++) { - EXPECT_NEAR(cutPoints[i], expected[i], precision); + EXPECT_EQ(computed.size(), expected.size()); + for (unsigned long i = 0; i < computed.size(); i++) { + EXPECT_NEAR(computed[i], expected[i], precision); } } template void checkVectors(std::vector const& expected, std::vector const& computed) { - EXPECT_EQ(expected.size(), computed.size()); ASSERT_EQ(expected.size(), computed.size()); for (auto i = 0; i < expected.size(); i++) { EXPECT_NEAR(expected[i], computed[i], precision); @@ -55,6 +54,20 @@ namespace mdlp { EXPECT_EQ(result.second, limit); return true; } + void test_dataset(CPPFImdlp& test, string filename, vector& expected, int depths[]) + { + ArffFiles file; + file.load("../datasets/" + filename, true); + vector& X = file.getX(); + labels_t& y = file.getY(); + auto attributes = file.getAttributes(); + for (auto feature = 0; feature < attributes.size(); feature++) { + test.fit(X[feature], y); + EXPECT_EQ(test.get_depth(), depths[feature]); + auto computed = test.getCutPoints(); + checkCutPoints(computed, expected[feature]); + } + } }; TEST_F(TestFImdlp, FitErrorEmptyDataset) { @@ -68,6 +81,15 @@ namespace mdlp { y = { 1, 2 }; EXPECT_THROW(fit(X, y), std::invalid_argument); } + TEST_F(TestFImdlp, FitErrorMinLengtMaxDepth) + { + auto testLength = CPPFImdlp(2, 10); + auto testDepth = CPPFImdlp(3, 0); + X = { 1, 2, 3 }; + y = { 1, 2, 3 }; + EXPECT_THROW(testLength.fit(X, y), invalid_argument); + EXPECT_THROW(testDepth.fit(X, y), invalid_argument); + } TEST_F(TestFImdlp, SortIndices) { X = { 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; @@ -114,7 +136,7 @@ namespace mdlp { TEST_F(TestFImdlp, TestArtificialDataset) { fit(X, y); - computeCutPoints(0, 20); + computeCutPoints(0, 20, 1); cutPoints_t expected = { 5.05 }; vector computed = getCutPoints(); computed = getCutPoints(); @@ -126,28 +148,15 @@ namespace mdlp { } TEST_F(TestFImdlp, TestIris) { - ArffFiles file; - string path = "../datasets/"; - - file.load(path + "iris.arff", true); - int items = file.getSize(); - vector& X = file.getX(); vector expected = { - { 5.4499998092651367, 5.75 }, + { 5.45, 5.75 }, { 2.75, 2.85, 2.95, 3.05, 3.35 }, - { 2.4500000476837158, 4.75, 5.0500001907348633 }, - { 0.80000001192092896, 1.75 } + { 2.45, 4.75, 5.05 }, + { 0.8, 1.75 } }; - labels_t& y = file.getY(); - auto attributes = file.getAttributes(); - for (auto feature = 0; feature < attributes.size(); feature++) { - fit(X[feature], y); - vector computed = getCutPoints(); - EXPECT_EQ(computed.size(), expected[feature].size()); - for (auto i = 0; i < computed.size(); i++) { - EXPECT_NEAR(computed[i], expected[feature][i], precision); - } - } + int depths[] = { 3, 5, 5, 5 }; + auto test = CPPFImdlp(); + test_dataset(test, "iris.arff", expected, depths); } TEST_F(TestFImdlp, ComputeCutPointsGCase) { @@ -156,7 +165,8 @@ namespace mdlp { samples_t X_ = { 0, 1, 2, 2, 2 }; labels_t y_ = { 1, 1, 1, 2, 2 }; fit(X_, y_); - checkCutPoints(expected); + auto computed = getCutPoints(); + checkCutPoints(computed, expected); } TEST_F(TestFImdlp, ValueCutPoint) { @@ -178,4 +188,43 @@ namespace mdlp { samples_t X4c = { 3.1, 3.2, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7 }; test_result(X4c, 4, 6.9 / 2, 2, "4c"); } + TEST_F(TestFImdlp, MaxDepth) + { + // Set max_depth to 2 + auto test = CPPFImdlp(3, 1); + vector expected = { + { 5.45 }, + { 3.35 }, + { 2.45 }, + {0.8 } + }; + int depths[] = { 1, 1, 1, 1 }; + test_dataset(test, "iris.arff", expected, depths); + } + TEST_F(TestFImdlp, MinLength) + { + // Set min_length to 75 + auto test = CPPFImdlp(75, 100); + vector expected = { + { 5.45, 5.75 }, + { 2.85, 3.35 }, + { 2.45, 4.75 }, + { 0.8, 1.75 } + }; + int depths[] = { 3, 3, 3, 3 }; + test_dataset(test, "iris.arff", expected, depths); + } + TEST_F(TestFImdlp, MinLengthMaxDepth) + { + // Set min_length to 75 + auto test = CPPFImdlp(75, 2); + vector expected = { + { 5.45, 5.75 }, + { 2.85, 3.35 }, + { 2.45, 4.75 }, + { 0.8, 1.75 } + }; + int depths[] = { 2, 2, 2, 2 }; + test_dataset(test, "iris.arff", expected, depths); + } } diff --git a/tests/test b/tests/test index e27cdde..5bacf1f 100755 --- a/tests/test +++ b/tests/test @@ -9,4 +9,4 @@ if test $? -ne 0; then exit 1 fi cd build -ctest --output-on-failure +ctest --output-on-failure|grep -v profiling From 964555de20175f4c1cd9a2d9525fa1bcca783322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 25 Feb 2023 18:31:57 +0100 Subject: [PATCH 02/32] Add echo total of cut points in sample --- sample/sample.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sample/sample.cpp b/sample/sample.cpp index 02ef84b..117caba 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -45,6 +45,7 @@ int main(int argc, char** argv) cout << y[i] << endl; } mdlp::CPPFImdlp test = mdlp::CPPFImdlp(); + auto total = 0; for (auto i = 0; i < attributes.size(); i++) { auto min_max = minmax_element(X[i].begin(), X[i].end()); cout << "Cut points for " << get<0>(attributes[i]) << endl; @@ -54,6 +55,8 @@ int main(int argc, char** argv) for (auto item : test.getCutPoints()) { cout << item << endl; } + total += test.getCutPoints().size(); } + cout << "Total cut points: " << total << endl; return 0; } From 4a9664c4aae58e853819b084265e2d0f7c21c626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 26 Feb 2023 11:26:37 +0100 Subject: [PATCH 03/32] Fix depth init in fit --- CPPFImdlp.cpp | 1 + CPPFImdlp.h | 2 +- tests/FImdlp_unittest.cpp | 29 +++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index acab4ea..007a789 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -22,6 +22,7 @@ namespace mdlp { { X = X_; y = y_; + depth = 0; cutPoints.clear(); if (X.size() != y.size()) { throw invalid_argument("X and y must have the same size"); diff --git a/CPPFImdlp.h b/CPPFImdlp.h index d8a23db..da7a42f 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -11,7 +11,7 @@ namespace mdlp { samples_t X; labels_t y; int depth, max_depth; - size_t min_length; + int min_length; Metrics metrics; cutPoints_t cutPoints; diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index f45d814..f8650f7 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -65,6 +65,10 @@ namespace mdlp { test.fit(X[feature], y); EXPECT_EQ(test.get_depth(), depths[feature]); auto computed = test.getCutPoints(); + cout << "Feature " << feature << ": "; + for (auto item : computed) + cout << item << " "; + cout << endl; checkCutPoints(computed, expected[feature]); } } @@ -156,7 +160,7 @@ namespace mdlp { }; int depths[] = { 3, 5, 5, 5 }; auto test = CPPFImdlp(); - test_dataset(test, "iris.arff", expected, depths); + //test_dataset(test, "iris.arff", expected, depths); } TEST_F(TestFImdlp, ComputeCutPointsGCase) { @@ -204,15 +208,32 @@ namespace mdlp { TEST_F(TestFImdlp, MinLength) { // Set min_length to 75 - auto test = CPPFImdlp(75, 100); vector expected = { { 5.45, 5.75 }, { 2.85, 3.35 }, { 2.45, 4.75 }, { 0.8, 1.75 } }; - int depths[] = { 3, 3, 3, 3 }; - test_dataset(test, "iris.arff", expected, depths); + int depths[] = { 2, 2, 2, 2 }; + //test_dataset(test, "iris", expected, depths); + ArffFiles file; + file.load("../datasets/iris.arff", true); + vector& X = file.getX(); + labels_t& y = file.getY(); + auto attributes = file.getAttributes(); + for (auto feature = 0; feature < attributes.size(); feature++) { + auto test = CPPFImdlp(75, 100); + test.fit(X[feature], y); + cout << "Feature: " << feature << " Depth: " << test.get_depth() << endl; + //EXPECT_EQ(test.get_depth(), depths[feature]); + auto computed = test.getCutPoints(); + for (auto item : test.getCutPoints()) { + cout << item << " "; + } + cout << endl; + //checkCutPoints(computed, expected[feature]); + } + FAIL(); } TEST_F(TestFImdlp, MinLengthMaxDepth) { From 552b03afc9bb1fd1611cd361ec4b0c3d0b630366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 26 Feb 2023 11:33:10 +0100 Subject: [PATCH 04/32] make public min_length for tests --- CPPFImdlp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPPFImdlp.h b/CPPFImdlp.h index da7a42f..406779a 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -11,7 +11,6 @@ namespace mdlp { samples_t X; labels_t y; int depth, max_depth; - int min_length; Metrics metrics; cutPoints_t cutPoints; @@ -21,6 +20,7 @@ namespace mdlp { size_t getCandidate(size_t, size_t); pair valueCutPoint(size_t, size_t, size_t); public: + int min_length; CPPFImdlp(); CPPFImdlp(int, int); ~CPPFImdlp(); From a7d13f602de3d347d1d5ad53bb654b6dedd4def1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 26 Feb 2023 12:07:52 +0100 Subject: [PATCH 05/32] set min_length as protected --- CPPFImdlp.cpp | 2 +- CPPFImdlp.h | 8 ++++---- tests/FImdlp_unittest.cpp | 29 ++++++----------------------- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 007a789..e0d2f95 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -12,7 +12,7 @@ namespace mdlp { metrics(Metrics(y, indices)) { } - CPPFImdlp::CPPFImdlp(int min_length_, int max_depth_): depth(0), max_depth(max_depth_), min_length(min_length_), indices(indices_t()), X(samples_t()), y(labels_t()), + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_): depth(0), max_depth(max_depth_), min_length(min_length_), indices(indices_t()), X(samples_t()), y(labels_t()), metrics(Metrics(y, indices)) { } diff --git a/CPPFImdlp.h b/CPPFImdlp.h index 406779a..ac57eb2 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -7,10 +7,11 @@ namespace mdlp { class CPPFImdlp { protected: - indices_t indices; + size_t min_length; + int depth, max_depth; samples_t X; labels_t y; - int depth, max_depth; + indices_t indices; Metrics metrics; cutPoints_t cutPoints; @@ -20,9 +21,8 @@ namespace mdlp { size_t getCandidate(size_t, size_t); pair valueCutPoint(size_t, size_t, size_t); public: - int min_length; CPPFImdlp(); - CPPFImdlp(int, int); + CPPFImdlp(size_t, int); ~CPPFImdlp(); CPPFImdlp& fit(samples_t&, labels_t&); cutPoints_t getCutPoints(); diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index f8650f7..51197db 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -57,7 +57,7 @@ namespace mdlp { void test_dataset(CPPFImdlp& test, string filename, vector& expected, int depths[]) { ArffFiles file; - file.load("../datasets/" + filename, true); + file.load("../datasets/" + filename + ".arff", true); vector& X = file.getX(); labels_t& y = file.getY(); auto attributes = file.getAttributes(); @@ -203,10 +203,11 @@ namespace mdlp { {0.8 } }; int depths[] = { 1, 1, 1, 1 }; - test_dataset(test, "iris.arff", expected, depths); + test_dataset(test, "iris", expected, depths); } TEST_F(TestFImdlp, MinLength) { + auto test = CPPFImdlp(75, 100); // Set min_length to 75 vector expected = { { 5.45, 5.75 }, @@ -214,26 +215,8 @@ namespace mdlp { { 2.45, 4.75 }, { 0.8, 1.75 } }; - int depths[] = { 2, 2, 2, 2 }; - //test_dataset(test, "iris", expected, depths); - ArffFiles file; - file.load("../datasets/iris.arff", true); - vector& X = file.getX(); - labels_t& y = file.getY(); - auto attributes = file.getAttributes(); - for (auto feature = 0; feature < attributes.size(); feature++) { - auto test = CPPFImdlp(75, 100); - test.fit(X[feature], y); - cout << "Feature: " << feature << " Depth: " << test.get_depth() << endl; - //EXPECT_EQ(test.get_depth(), depths[feature]); - auto computed = test.getCutPoints(); - for (auto item : test.getCutPoints()) { - cout << item << " "; - } - cout << endl; - //checkCutPoints(computed, expected[feature]); - } - FAIL(); + int depths[] = { 3, 2, 2, 2 }; + test_dataset(test, "iris", expected, depths); } TEST_F(TestFImdlp, MinLengthMaxDepth) { @@ -246,6 +229,6 @@ namespace mdlp { { 0.8, 1.75 } }; int depths[] = { 2, 2, 2, 2 }; - test_dataset(test, "iris.arff", expected, depths); + test_dataset(test, "iris", expected, depths); } } From 747f610ce9b1100c26e1a73d0435a7ce4f5ea5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 27 Feb 2023 00:53:00 +0100 Subject: [PATCH 06/32] Remove unneeded code in CPPFImdlp --- .github/workflows/build.yml | 8 ++++++-- .gitignore | 3 ++- CPPFImdlp.cpp | 3 --- tests/cover | 4 ---- tests/test | 22 +++++++++++++--------- 5 files changed, 21 insertions(+), 19 deletions(-) delete mode 100755 tests/cover diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f5982a..c5fbfd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ on: push: branches: - main - - test + - "*" pull_request: types: [opened, synchronize, reopened] jobs: @@ -13,11 +13,14 @@ jobs: env: BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.2.0 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper uses: SonarSource/sonarcloud-github-c-cpp@v1 + - name: Install gcovr + run: | + sudo apt-get install -y gcovr - name: Tests & build-wrapper run: | mkdir build @@ -29,6 +32,7 @@ jobs: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release cd build ctest -C Release --output-on-failure + gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 31a7b4d..9446e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ .idea cmake-* **/CMakeFiles -.vscode/* \ No newline at end of file +.vscode/* +**/gcovr-report \ No newline at end of file diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index e0d2f95..a2c17ce 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -128,9 +128,6 @@ namespace mdlp { precision_t ig, delta; precision_t ent, ent1, ent2; auto N = precision_t(end - start); - if (N < 2) { - return false; - } k = metrics.computeNumClasses(start, end); k1 = metrics.computeNumClasses(start, cut); k2 = metrics.computeNumClasses(cut, end); diff --git a/tests/cover b/tests/cover deleted file mode 100755 index 3f2aca6..0000000 --- a/tests/cover +++ /dev/null @@ -1,4 +0,0 @@ -rm -fr lcoverage/* -lcov --capture --directory ./ --output-file lcoverage/main_coverage.info -genhtml lcoverage/main_coverage.info --output-directory lcoverage -open lcoverage/index.html diff --git a/tests/test b/tests/test index 5bacf1f..fb44da9 100755 --- a/tests/test +++ b/tests/test @@ -1,12 +1,16 @@ +if [ -d build ] ; then + rm -fr build +fi cmake -S . -B build -Wno-dev -if test $? -ne 0; then - echo "Error in creating build commands." - exit 1 -fi cmake --build build -if test $? -ne 0; then - echo "Error in build command." - exit 1 -fi cd build -ctest --output-on-failure|grep -v profiling +ctest --output-on-failure +cd .. +if [ ! -d lcoverage ] ; then + mkdir lcoverage +fi +rm -fr lcoverage/* 2>/dev/null +#lcov --capture --directory ./ --output-file lcoverage/main_coverage.info +#lcov --remove lcoverage/main_coverage.info 'v1/*' '/Applications/*' '*/tests/*' --output-file lcoverage/main_coverage.info -q +#lcov --list lcoverage/main_coverage.info +gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=coverage.xml From 68751273944afe66e2c33de16febba2f435882fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 27 Feb 2023 01:01:24 +0100 Subject: [PATCH 07/32] Update Test coverage and build --- .github/workflows/build.yml | 4 +++- tests/test | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5fbfd8..2a1b05f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,10 +32,12 @@ jobs: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release cd build ctest -C Release --output-on-failure + cd .. gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" + sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ + --define sonar.coverageReportPaths=coverage.xml diff --git a/tests/test b/tests/test index fb44da9..ca7061f 100755 --- a/tests/test +++ b/tests/test @@ -6,11 +6,11 @@ cmake --build build cd build ctest --output-on-failure cd .. -if [ ! -d lcoverage ] ; then - mkdir lcoverage +if [ ! -d gcovr-report ] ; then + mkdir gcovr-report fi -rm -fr lcoverage/* 2>/dev/null +rm -fr gcovr-report/* 2>/dev/null #lcov --capture --directory ./ --output-file lcoverage/main_coverage.info #lcov --remove lcoverage/main_coverage.info 'v1/*' '/Applications/*' '*/tests/*' --output-file lcoverage/main_coverage.info -q #lcov --list lcoverage/main_coverage.info -gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=coverage.xml +gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=gcovr-report/coverage.xml From 0b63d9ace010fc25cf1dc4e8d3aa3600fb9def7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 27 Feb 2023 01:18:46 +0100 Subject: [PATCH 08/32] Update build --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a1b05f..404d5c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,9 +18,10 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install sonar-scanner and build-wrapper uses: SonarSource/sonarcloud-github-c-cpp@v1 - - name: Install gcovr + - name: Install lcov & gcovr run: | - sudo apt-get install -y gcovr + sudo apt-get -y install lcov + sudo apt-get -y install gcovr - name: Tests & build-wrapper run: | mkdir build @@ -40,4 +41,4 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ - --define sonar.coverageReportPaths=coverage.xml + --define sonar.coverageReportPaths=tests/coverage.xml From 90428218c2be4093f56e539c2ab2be7557ad2ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 28 Feb 2023 00:43:37 +0100 Subject: [PATCH 09/32] Add dataset to test and add hyperparameters to sample --- sample/sample.cpp | 104 ++++++-- tests/datasets/liver-disorders.arff | 399 ++++++++++++++++++++++++++++ 2 files changed, 487 insertions(+), 16 deletions(-) create mode 100755 tests/datasets/liver-disorders.arff diff --git a/sample/sample.cpp b/sample/sample.cpp index 117caba..93e87a9 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -1,31 +1,76 @@ #include #include #include +#include #include "../CPPFImdlp.h" #include "../tests/ArffFiles.h" using namespace std; using namespace mdlp; +/* print a description of all supported options */ +void usage(const char* path) +{ + /* take only the last portion of the path */ + const char* basename = strrchr(path, '/'); + basename = basename ? basename + 1 : path; -int main(int argc, char** argv) + cout << "usage: " << basename << "[OPTION]" << endl; + cout << " -h, --help\t\t Print this help and exit." << endl; + cout << " -f, --file[=FILENAME]\t {mfeat-factors, glass, iris, letter, kdd_JapaneseVowels, liver-disorders, test}." << endl; + cout << " -m, --max_depth=INT\t max_depth pased to discretizer. Default = MAX_INT" << endl; + cout << " -n, --min_length=INT\t interval min_length pased to discretizer. Default = 3" << endl; +} + +tuple parse_arguments(int argc, char** argv) +{ + string file_name; + int max_depth = numeric_limits::max(); + int min_length = 3; + static struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "file", required_argument, 0, 'f' }, + { "max_depth", required_argument, 0, 'm' }, + { "min_length", required_argument, 0, 'n' }, + { 0, 0, 0, 0 } + }; + while (1) { + auto c = getopt_long(argc, argv, "hf:m:n:", long_options, 0); + if (c == -1) + break; + switch (c) { + case 'h': + usage(argv[0]); + exit(0); + case 'f': + file_name = optarg; + break; + case 'm': + max_depth = atoi(optarg); + break; + case 'n': + min_length = atoi(optarg); + break; + case '?': + usage(argv[0]); + exit(1); + default: + abort(); + } + } + if (file_name.empty()) { + usage(argv[0]); + exit(1); + } + return make_tuple(file_name, max_depth, min_length); +} + +void process_file(string file_name, bool class_last, int max_depth, int min_length) { ArffFiles file; string path = "../../tests/datasets/"; - map datasets = { - {"mfeat-factors", true}, - {"iris", true}, - {"letter", true}, - {"glass", true}, - {"kdd_JapaneseVowels", false}, - {"test", true} - }; - if (argc != 2 || datasets.find(argv[1]) == datasets.end()) { - cout << "Usage: " << argv[0] << " {mfeat-factors, glass, iris, letter, kdd_JapaneseVowels, test}" << endl; - return 1; - } - file.load(path + argv[1] + ".arff", datasets[argv[1]]); + file.load(path + file_name + ".arff", class_last); auto attributes = file.getAttributes(); int items = file.getSize(); cout << "Number of lines: " << items << endl; @@ -44,7 +89,7 @@ int main(int argc, char** argv) } cout << y[i] << endl; } - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(); + mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth); auto total = 0; for (auto i = 0; i < attributes.size(); i++) { auto min_max = minmax_element(X[i].begin(), X[i].end()); @@ -57,6 +102,33 @@ int main(int argc, char** argv) } total += test.getCutPoints().size(); } - cout << "Total cut points: " << total << endl; + cout << "Total cut points ...: " << total << endl; + cout << "Total feature states: " << total + attributes.size() << endl; +} + + +int main(int argc, char** argv) +{ + map datasets = { + {"mfeat-factors", true}, + {"iris", true}, + {"letter", true}, + {"glass", true}, + {"kdd_JapaneseVowels", false}, + {"liver-disorders", true}, + {"test", true} + }; + string file_name; + int max_depth, min_length; + tie(file_name, max_depth, min_length) = parse_arguments(argc, argv); + if (datasets.find(file_name) == datasets.end()) { + cout << "Invalid file name: " << file_name << endl; + usage(argv[0]); + exit(1); + } + process_file(file_name, datasets[file_name], max_depth, min_length); + cout << "File name: " << file_name << endl; + cout << "Max depth: " << max_depth << endl; + cout << "Min length: " << min_length << endl; return 0; } diff --git a/tests/datasets/liver-disorders.arff b/tests/datasets/liver-disorders.arff new file mode 100755 index 0000000..fe967df --- /dev/null +++ b/tests/datasets/liver-disorders.arff @@ -0,0 +1,399 @@ +% 1. Title: BUPA liver disorders +% +% 2. Source information: +% -- Creators: BUPA Medical Research Ltd. +% -- Donor: Richard S. Forsyth +% 8 Grosvenor Avenue +% Mapperley Park +% Nottingham NG3 5DX +% 0602-621676 +% -- Date: 5/15/1990 +% +% 3. Past usage: +% -- None known other than what is shown in the PC/BEAGLE User's Guide +% (written by Richard S. Forsyth). +% +% 4. Relevant information: +% -- The first 5 variables are all blood tests which are thought +% to be sensitive to liver disorders that might arise from +% excessive alcohol consumption. Each line in the bupa.data file +% constitutes the record of a single male individual. +% -- It appears that drinks>5 is some sort of a selector on this database. +% See the PC/BEAGLE User's Guide for more information. +% +% 5. Number of instances: 345 +% +% 6. Number of attributes: 7 overall +% +% 7. Attribute information: +% 1. mcv mean corpuscular volume +% 2. alkphos alkaline phosphotase +% 3. sgpt alamine aminotransferase +% 4. sgot aspartate aminotransferase +% 5. gammagt gamma-glutamyl transpeptidase +% 6. drinks number of half-pint equivalents of alcoholic beverages +% drunk per day +% 7. selector field used to split data into two sets +% +% 8. Missing values: none% +% Information about the dataset +% CLASSTYPE: nominal +% CLASSINDEX: last +% + +@relation liver-disorders + +@attribute mcv INTEGER +@attribute alkphos INTEGER +@attribute sgpt INTEGER +@attribute sgot INTEGER +@attribute gammagt INTEGER +@attribute drinks REAL +@attribute selector {1,2} + +@data +85,92,45,27,31,0.0,1 +85,64,59,32,23,0.0,2 +86,54,33,16,54,0.0,2 +91,78,34,24,36,0.0,2 +87,70,12,28,10,0.0,2 +98,55,13,17,17,0.0,2 +88,62,20,17,9,0.5,1 +88,67,21,11,11,0.5,1 +92,54,22,20,7,0.5,1 +90,60,25,19,5,0.5,1 +89,52,13,24,15,0.5,1 +82,62,17,17,15,0.5,1 +90,64,61,32,13,0.5,1 +86,77,25,19,18,0.5,1 +96,67,29,20,11,0.5,1 +91,78,20,31,18,0.5,1 +89,67,23,16,10,0.5,1 +89,79,17,17,16,0.5,1 +91,107,20,20,56,0.5,1 +94,116,11,33,11,0.5,1 +92,59,35,13,19,0.5,1 +93,23,35,20,20,0.5,1 +90,60,23,27,5,0.5,1 +96,68,18,19,19,0.5,1 +84,80,47,33,97,0.5,1 +92,70,24,13,26,0.5,1 +90,47,28,15,18,0.5,1 +88,66,20,21,10,0.5,1 +91,102,17,13,19,0.5,1 +87,41,31,19,16,0.5,1 +86,79,28,16,17,0.5,1 +91,57,31,23,42,0.5,1 +93,77,32,18,29,0.5,1 +88,96,28,21,40,0.5,1 +94,65,22,18,11,0.5,1 +91,72,155,68,82,0.5,2 +85,54,47,33,22,0.5,2 +79,39,14,19,9,0.5,2 +85,85,25,26,30,0.5,2 +89,63,24,20,38,0.5,2 +84,92,68,37,44,0.5,2 +89,68,26,39,42,0.5,2 +89,101,18,25,13,0.5,2 +86,84,18,14,16,0.5,2 +85,65,25,14,18,0.5,2 +88,61,19,21,13,0.5,2 +92,56,14,16,10,0.5,2 +95,50,29,25,50,0.5,2 +91,75,24,22,11,0.5,2 +83,40,29,25,38,0.5,2 +89,74,19,23,16,0.5,2 +85,64,24,22,11,0.5,2 +92,57,64,36,90,0.5,2 +94,48,11,23,43,0.5,2 +87,52,21,19,30,0.5,2 +85,65,23,29,15,0.5,2 +84,82,21,21,19,0.5,2 +88,49,20,22,19,0.5,2 +96,67,26,26,36,0.5,2 +90,63,24,24,24,0.5,2 +90,45,33,34,27,0.5,2 +90,72,14,15,18,0.5,2 +91,55,4,8,13,0.5,2 +91,52,15,22,11,0.5,2 +87,71,32,19,27,1.0,1 +89,77,26,20,19,1.0,1 +89,67,5,17,14,1.0,2 +85,51,26,24,23,1.0,2 +103,75,19,30,13,1.0,2 +90,63,16,21,14,1.0,2 +90,63,29,23,57,2.0,1 +90,67,35,19,35,2.0,1 +87,66,27,22,9,2.0,1 +90,73,34,21,22,2.0,1 +86,54,20,21,16,2.0,1 +90,80,19,14,42,2.0,1 +87,90,43,28,156,2.0,2 +96,72,28,19,30,2.0,2 +91,55,9,25,16,2.0,2 +95,78,27,25,30,2.0,2 +92,101,34,30,64,2.0,2 +89,51,41,22,48,2.0,2 +91,99,42,33,16,2.0,2 +94,58,21,18,26,2.0,2 +92,60,30,27,297,2.0,2 +94,58,21,18,26,2.0,2 +88,47,33,26,29,2.0,2 +92,65,17,25,9,2.0,2 +92,79,22,20,11,3.0,1 +84,83,20,25,7,3.0,1 +88,68,27,21,26,3.0,1 +86,48,20,20,6,3.0,1 +99,69,45,32,30,3.0,1 +88,66,23,12,15,3.0,1 +89,62,42,30,20,3.0,1 +90,51,23,17,27,3.0,1 +81,61,32,37,53,3.0,2 +89,89,23,18,104,3.0,2 +89,65,26,18,36,3.0,2 +92,75,26,26,24,3.0,2 +85,59,25,20,25,3.0,2 +92,61,18,13,81,3.0,2 +89,63,22,27,10,4.0,1 +90,84,18,23,13,4.0,1 +88,95,25,19,14,4.0,1 +89,35,27,29,17,4.0,1 +91,80,37,23,27,4.0,1 +91,109,33,15,18,4.0,1 +91,65,17,5,7,4.0,1 +88,107,29,20,50,4.0,2 +87,76,22,55,9,4.0,2 +87,86,28,23,21,4.0,2 +87,42,26,23,17,4.0,2 +88,80,24,25,17,4.0,2 +90,96,34,49,169,4.0,2 +86,67,11,15,8,4.0,2 +92,40,19,20,21,4.0,2 +85,60,17,21,14,4.0,2 +89,90,15,17,25,4.0,2 +91,57,15,16,16,4.0,2 +96,55,48,39,42,4.0,2 +79,101,17,27,23,4.0,2 +90,134,14,20,14,4.0,2 +89,76,14,21,24,4.0,2 +88,93,29,27,31,4.0,2 +90,67,10,16,16,4.0,2 +92,73,24,21,48,4.0,2 +91,55,28,28,82,4.0,2 +83,45,19,21,13,4.0,2 +90,74,19,14,22,4.0,2 +92,66,21,16,33,5.0,1 +93,63,26,18,18,5.0,1 +86,78,47,39,107,5.0,2 +97,44,113,45,150,5.0,2 +87,59,15,19,12,5.0,2 +86,44,21,11,15,5.0,2 +87,64,16,20,24,5.0,2 +92,57,21,23,22,5.0,2 +90,70,25,23,112,5.0,2 +99,59,17,19,11,5.0,2 +92,80,10,26,20,6.0,1 +95,60,26,22,28,6.0,1 +91,63,25,26,15,6.0,1 +92,62,37,21,36,6.0,1 +95,50,13,14,15,6.0,1 +90,76,37,19,50,6.0,1 +96,70,70,26,36,6.0,1 +95,62,64,42,76,6.0,1 +92,62,20,23,20,6.0,1 +91,63,25,26,15,6.0,1 +82,56,67,38,92,6.0,2 +92,82,27,24,37,6.0,2 +90,63,12,26,21,6.0,2 +88,37,9,15,16,6.0,2 +100,60,29,23,76,6.0,2 +98,43,35,23,69,6.0,2 +91,74,87,50,67,6.0,2 +92,87,57,25,44,6.0,2 +93,99,36,34,48,6.0,2 +90,72,17,19,19,6.0,2 +97,93,21,20,68,6.0,2 +93,50,18,25,17,6.0,2 +90,57,20,26,33,6.0,2 +92,76,31,28,41,6.0,2 +88,55,19,17,14,6.0,2 +89,63,24,29,29,6.0,2 +92,79,70,32,84,7.0,1 +92,93,58,35,120,7.0,1 +93,84,58,47,62,7.0,2 +97,71,29,22,52,8.0,1 +84,99,33,19,26,8.0,1 +96,44,42,23,73,8.0,1 +90,62,22,21,21,8.0,1 +92,94,18,17,6,8.0,1 +90,67,77,39,114,8.0,1 +97,71,29,22,52,8.0,1 +91,69,25,25,66,8.0,2 +93,59,17,20,14,8.0,2 +92,95,85,48,200,8.0,2 +90,50,26,22,53,8.0,2 +91,62,59,47,60,8.0,2 +92,93,22,28,123,9.0,1 +92,77,86,41,31,10.0,1 +86,66,22,24,26,10.0,2 +98,57,31,34,73,10.0,2 +95,80,50,64,55,10.0,2 +92,108,53,33,94,12.0,2 +97,92,22,28,49,12.0,2 +93,77,39,37,108,16.0,1 +94,83,81,34,201,20.0,1 +87,75,25,21,14,0.0,1 +88,56,23,18,12,0.0,1 +84,97,41,20,32,0.0,2 +94,91,27,20,15,0.5,1 +97,62,17,13,5,0.5,1 +92,85,25,20,12,0.5,1 +82,48,27,15,12,0.5,1 +88,74,31,25,15,0.5,1 +95,77,30,14,21,0.5,1 +88,94,26,18,8,0.5,1 +91,70,19,19,22,0.5,1 +83,54,27,15,12,0.5,1 +91,105,40,26,56,0.5,1 +86,79,37,28,14,0.5,1 +91,96,35,22,135,0.5,1 +89,82,23,14,35,0.5,1 +90,73,24,23,11,0.5,1 +90,87,19,25,19,0.5,1 +89,82,33,32,18,0.5,1 +85,79,17,8,9,0.5,1 +85,119,30,26,17,0.5,1 +78,69,24,18,31,0.5,1 +88,107,34,21,27,0.5,1 +89,115,17,27,7,0.5,1 +92,67,23,15,12,0.5,1 +89,101,27,34,14,0.5,1 +91,84,11,12,10,0.5,1 +94,101,41,20,53,0.5,2 +88,46,29,22,18,0.5,2 +88,122,35,29,42,0.5,2 +84,88,28,25,35,0.5,2 +90,79,18,15,24,0.5,2 +87,69,22,26,11,0.5,2 +65,63,19,20,14,0.5,2 +90,64,12,17,14,0.5,2 +85,58,18,24,16,0.5,2 +88,81,41,27,36,0.5,2 +86,78,52,29,62,0.5,2 +82,74,38,28,48,0.5,2 +86,58,36,27,59,0.5,2 +94,56,30,18,27,0.5,2 +87,57,30,30,22,0.5,2 +98,74,148,75,159,0.5,2 +94,75,20,25,38,0.5,2 +83,68,17,20,71,0.5,2 +93,56,25,21,33,0.5,2 +101,65,18,21,22,0.5,2 +92,65,25,20,31,0.5,2 +92,58,14,16,13,0.5,2 +86,58,16,23,23,0.5,2 +85,62,15,13,22,0.5,2 +86,57,13,20,13,0.5,2 +86,54,26,30,13,0.5,2 +81,41,33,27,34,1.0,1 +91,67,32,26,13,1.0,1 +91,80,21,19,14,1.0,1 +92,60,23,15,19,1.0,1 +91,60,32,14,8,1.0,1 +93,65,28,22,10,1.0,1 +90,63,45,24,85,1.0,2 +87,92,21,22,37,1.0,2 +83,78,31,19,115,1.0,2 +95,62,24,23,14,1.0,2 +93,59,41,30,48,1.0,2 +84,82,43,32,38,2.0,1 +87,71,33,20,22,2.0,1 +86,44,24,15,18,2.0,1 +86,66,28,24,21,2.0,1 +88,58,31,17,17,2.0,1 +90,61,28,29,31,2.0,1 +88,69,70,24,64,2.0,1 +93,87,18,17,26,2.0,1 +98,58,33,21,28,2.0,1 +91,44,18,18,23,2.0,2 +87,75,37,19,70,2.0,2 +94,91,30,26,25,2.0,2 +88,85,14,15,10,2.0,2 +89,109,26,25,27,2.0,2 +87,59,37,27,34,2.0,2 +93,58,20,23,18,2.0,2 +88,57,9,15,16,2.0,2 +94,65,38,27,17,3.0,1 +91,71,12,22,11,3.0,1 +90,55,20,20,16,3.0,1 +91,64,21,17,26,3.0,2 +88,47,35,26,33,3.0,2 +82,72,31,20,84,3.0,2 +85,58,83,49,51,3.0,2 +91,54,25,22,35,4.0,1 +98,50,27,25,53,4.0,2 +86,62,29,21,26,4.0,2 +89,48,32,22,14,4.0,2 +82,68,20,22,9,4.0,2 +83,70,17,19,23,4.0,2 +96,70,21,26,21,4.0,2 +94,117,77,56,52,4.0,2 +93,45,11,14,21,4.0,2 +93,49,27,21,29,4.0,2 +84,73,46,32,39,4.0,2 +91,63,17,17,46,4.0,2 +90,57,31,18,37,4.0,2 +87,45,19,13,16,4.0,2 +91,68,14,20,19,4.0,2 +86,55,29,35,108,4.0,2 +91,86,52,47,52,4.0,2 +88,46,15,33,55,4.0,2 +85,52,22,23,34,4.0,2 +89,72,33,27,55,4.0,2 +95,59,23,18,19,4.0,2 +94,43,154,82,121,4.0,2 +96,56,38,26,23,5.0,2 +90,52,10,17,12,5.0,2 +94,45,20,16,12,5.0,2 +99,42,14,21,49,5.0,2 +93,102,47,23,37,5.0,2 +94,71,25,26,31,5.0,2 +92,73,33,34,115,5.0,2 +87,54,41,29,23,6.0,1 +92,67,15,14,14,6.0,1 +98,101,31,26,32,6.0,1 +92,53,51,33,92,6.0,1 +97,94,43,43,82,6.0,1 +93,43,11,16,54,6.0,1 +93,68,24,18,19,6.0,1 +95,36,38,19,15,6.0,1 +99,86,58,42,203,6.0,1 +98,66,103,57,114,6.0,1 +92,80,10,26,20,6.0,1 +96,74,27,25,43,6.0,2 +95,93,21,27,47,6.0,2 +86,109,16,22,28,6.0,2 +91,46,30,24,39,7.0,2 +102,82,34,78,203,7.0,2 +85,50,12,18,14,7.0,2 +91,57,33,23,12,8.0,1 +91,52,76,32,24,8.0,1 +93,70,46,30,33,8.0,1 +87,55,36,19,25,8.0,1 +98,123,28,24,31,8.0,1 +82,55,18,23,44,8.0,2 +95,73,20,25,225,8.0,2 +97,80,17,20,53,8.0,2 +100,83,25,24,28,8.0,2 +88,91,56,35,126,9.0,2 +91,138,45,21,48,10.0,1 +92,41,37,22,37,10.0,1 +86,123,20,25,23,10.0,2 +91,93,35,34,37,10.0,2 +87,87,15,23,11,10.0,2 +87,56,52,43,55,10.0,2 +99,75,26,24,41,12.0,1 +96,69,53,43,203,12.0,2 +98,77,55,35,89,15.0,1 +91,68,27,26,14,16.0,1 +98,99,57,45,65,20.0,1 From ce9ddb3be3131bb12d3486e9e40005939d0018bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 28 Feb 2023 00:50:12 +0100 Subject: [PATCH 10/32] Cosmetic refactor in unittest --- tests/FImdlp_unittest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index 51197db..e76609a 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -8,7 +8,6 @@ namespace mdlp { class TestFImdlp: public CPPFImdlp, public testing::Test { public: precision_t precision = 0.000001; - //precision_t precision = 0.000000000001; TestFImdlp(): CPPFImdlp() {} void SetUp() { @@ -194,7 +193,7 @@ namespace mdlp { } TEST_F(TestFImdlp, MaxDepth) { - // Set max_depth to 2 + // Set max_depth to 1 auto test = CPPFImdlp(3, 1); vector expected = { { 5.45 }, From 200015000cea5ec9736de9684739423d7ca5af7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 28 Feb 2023 10:28:23 +0100 Subject: [PATCH 11/32] Add all datasets to sample --- README.md | 3 ++- sample/sample.cpp | 55 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f5ba0fe..445a4dc 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ mkdir build cd build cmake .. make -./sample iris +./sample -f iris -m 2 +./sample -h ``` ## Test diff --git a/sample/sample.cpp b/sample/sample.cpp index 93e87a9..5d1ad71 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "../CPPFImdlp.h" #include "../tests/ArffFiles.h" @@ -8,6 +9,8 @@ using namespace std; using namespace mdlp; +const string PATH = "../../tests/datasets/"; + /* print a description of all supported options */ void usage(const char* path) { @@ -17,7 +20,7 @@ void usage(const char* path) cout << "usage: " << basename << "[OPTION]" << endl; cout << " -h, --help\t\t Print this help and exit." << endl; - cout << " -f, --file[=FILENAME]\t {mfeat-factors, glass, iris, letter, kdd_JapaneseVowels, liver-disorders, test}." << endl; + cout << " -f, --file[=FILENAME]\t {all, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors, test}." << endl; cout << " -m, --max_depth=INT\t max_depth pased to discretizer. Default = MAX_INT" << endl; cout << " -n, --min_length=INT\t interval min_length pased to discretizer. Default = 3" << endl; } @@ -68,9 +71,8 @@ tuple parse_arguments(int argc, char** argv) void process_file(string file_name, bool class_last, int max_depth, int min_length) { ArffFiles file; - string path = "../../tests/datasets/"; - file.load(path + file_name + ".arff", class_last); + file.load(PATH + file_name + ".arff", class_last); auto attributes = file.getAttributes(); int items = file.getSize(); cout << "Number of lines: " << items << endl; @@ -83,7 +85,7 @@ void process_file(string file_name, bool class_last, int max_depth, int min_leng cout << "Data: " << endl; vector& X = file.getX(); labels_t& y = file.getY(); - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 5; i++) { for (auto feature : X) { cout << fixed << setprecision(1) << feature[i] << " "; } @@ -106,29 +108,58 @@ void process_file(string file_name, bool class_last, int max_depth, int min_leng cout << "Total feature states: " << total + attributes.size() << endl; } +void process_all_files(map datasets, int max_depth, int min_length) +{ + cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << endl << endl; + printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(s)"); + printf("==================== ==== ==== =======\n"); + for (auto dataset : datasets) { + ArffFiles file; + file.load(PATH + dataset.first + ".arff", dataset.second); + auto attributes = file.getAttributes(); + vector& X = file.getX(); + labels_t& y = file.getY(); + float timing = 0; + int cut_points = 0; + for (auto i = 0; i < attributes.size(); i++) { + mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth); + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + test.fit(X[i], y); + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + timing += std::chrono::duration_cast(end - begin).count(); + cut_points += test.getCutPoints().size(); + } + printf("%-20s %4lu %4d %7.2f\n", dataset.first.c_str(), attributes.size(), cut_points, timing); + } +} + int main(int argc, char** argv) { map datasets = { - {"mfeat-factors", true}, - {"iris", true}, - {"letter", true}, {"glass", true}, + {"iris", true}, {"kdd_JapaneseVowels", false}, + {"letter", true}, {"liver-disorders", true}, + {"mfeat-factors", true}, {"test", true} }; string file_name; int max_depth, min_length; tie(file_name, max_depth, min_length) = parse_arguments(argc, argv); - if (datasets.find(file_name) == datasets.end()) { + if (datasets.find(file_name) == datasets.end() && file_name != "all") { cout << "Invalid file name: " << file_name << endl; usage(argv[0]); exit(1); } - process_file(file_name, datasets[file_name], max_depth, min_length); - cout << "File name: " << file_name << endl; - cout << "Max depth: " << max_depth << endl; - cout << "Min length: " << min_length << endl; + if (file_name == "all") + process_all_files(datasets, max_depth, min_length); + else { + process_file(file_name, datasets[file_name], max_depth, min_length); + cout << "File name: " << file_name << endl; + cout << "Max depth: " << max_depth << endl; + cout << "Min length: " << min_length << endl; + } return 0; } From c00b7a613cebfb7950166954eed628c4697617bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 28 Feb 2023 10:52:26 +0100 Subject: [PATCH 12/32] Add path argument to command line --- sample/sample.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/sample/sample.cpp b/sample/sample.cpp index 5d1ad71..cb0d8fb 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -21,24 +21,27 @@ void usage(const char* path) cout << "usage: " << basename << "[OPTION]" << endl; cout << " -h, --help\t\t Print this help and exit." << endl; cout << " -f, --file[=FILENAME]\t {all, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors, test}." << endl; + cout << " -p, --path[=FILENAME]\t folder where the arff dataset is located, default " << PATH << endl; cout << " -m, --max_depth=INT\t max_depth pased to discretizer. Default = MAX_INT" << endl; cout << " -n, --min_length=INT\t interval min_length pased to discretizer. Default = 3" << endl; } -tuple parse_arguments(int argc, char** argv) +tuple parse_arguments(int argc, char** argv) { string file_name; + string path = PATH; int max_depth = numeric_limits::max(); int min_length = 3; static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "file", required_argument, 0, 'f' }, + { "path", required_argument, 0, 'p' }, { "max_depth", required_argument, 0, 'm' }, { "min_length", required_argument, 0, 'n' }, { 0, 0, 0, 0 } }; while (1) { - auto c = getopt_long(argc, argv, "hf:m:n:", long_options, 0); + auto c = getopt_long(argc, argv, "hf:p:m:n:", long_options, 0); if (c == -1) break; switch (c) { @@ -54,6 +57,11 @@ tuple parse_arguments(int argc, char** argv) case 'n': min_length = atoi(optarg); break; + case 'p': + path = optarg; + if (path.back() != '/') + path += '/'; + break; case '?': usage(argv[0]); exit(1); @@ -65,14 +73,14 @@ tuple parse_arguments(int argc, char** argv) usage(argv[0]); exit(1); } - return make_tuple(file_name, max_depth, min_length); + return make_tuple(file_name, path, max_depth, min_length); } -void process_file(string file_name, bool class_last, int max_depth, int min_length) +void process_file(string path, string file_name, bool class_last, int max_depth, int min_length) { ArffFiles file; - file.load(PATH + file_name + ".arff", class_last); + file.load(path + file_name + ".arff", class_last); auto attributes = file.getAttributes(); int items = file.getSize(); cout << "Number of lines: " << items << endl; @@ -108,14 +116,14 @@ void process_file(string file_name, bool class_last, int max_depth, int min_leng cout << "Total feature states: " << total + attributes.size() << endl; } -void process_all_files(map datasets, int max_depth, int min_length) +void process_all_files(map datasets, string path, int max_depth, int min_length) { cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << endl << endl; printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(s)"); printf("==================== ==== ==== =======\n"); for (auto dataset : datasets) { ArffFiles file; - file.load(PATH + dataset.first + ".arff", dataset.second); + file.load(path + dataset.first + ".arff", dataset.second); auto attributes = file.getAttributes(); vector& X = file.getX(); labels_t& y = file.getY(); @@ -145,21 +153,21 @@ int main(int argc, char** argv) {"mfeat-factors", true}, {"test", true} }; - string file_name; + string file_name, path; int max_depth, min_length; - tie(file_name, max_depth, min_length) = parse_arguments(argc, argv); + tie(file_name, path, max_depth, min_length) = parse_arguments(argc, argv); if (datasets.find(file_name) == datasets.end() && file_name != "all") { cout << "Invalid file name: " << file_name << endl; usage(argv[0]); exit(1); } if (file_name == "all") - process_all_files(datasets, max_depth, min_length); + process_all_files(datasets, path, max_depth, min_length); else { - process_file(file_name, datasets[file_name], max_depth, min_length); + process_file(path, file_name, datasets[file_name], max_depth, min_length); cout << "File name: " << file_name << endl; cout << "Max depth: " << max_depth << endl; cout << "Min length: " << min_length << endl; } return 0; -} +} \ No newline at end of file From 4492252729f70a3b96d7e558120d6afbea64b11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 11 Mar 2023 22:45:34 +0100 Subject: [PATCH 13/32] Add headers needed in sample.cpp --- sample/sample.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sample/sample.cpp b/sample/sample.cpp index cb0d8fb..aab29f9 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include "../CPPFImdlp.h" #include "../tests/ArffFiles.h" From 083a56b311bc0e06e9349c333b35ecc9a5937b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 12 Mar 2023 11:27:02 +0100 Subject: [PATCH 14/32] Change seconds for milliseconds in sample change path of coverage report in build --- .github/workflows/build.yml | 2 +- .vscode/settings.json | 3 ++- sample/sample.cpp | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 404d5c2..d042490 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,4 +41,4 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ - --define sonar.coverageReportPaths=tests/coverage.xml + --define sonar.coverageReportPaths=coverage.xml diff --git a/.vscode/settings.json b/.vscode/settings.json index acd9b53..b89d404 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,6 @@ "sonarlint.connectedMode.project": { "connectionId": "sonarcloud", "projectKey": "rmontanana_mdlp" - } + }, + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" } \ No newline at end of file diff --git a/sample/sample.cpp b/sample/sample.cpp index aab29f9..d8c7b3f 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -121,25 +121,25 @@ void process_file(string path, string file_name, bool class_last, int max_depth, void process_all_files(map datasets, string path, int max_depth, int min_length) { cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << endl << endl; - printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(s)"); - printf("==================== ==== ==== =======\n"); + printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(ms)"); + printf("==================== ==== ==== ========\n"); for (auto dataset : datasets) { ArffFiles file; file.load(path + dataset.first + ".arff", dataset.second); auto attributes = file.getAttributes(); vector& X = file.getX(); labels_t& y = file.getY(); - float timing = 0; + size_t timing = 0; int cut_points = 0; for (auto i = 0; i < attributes.size(); i++) { mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); test.fit(X[i], y); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); - timing += std::chrono::duration_cast(end - begin).count(); + timing += std::chrono::duration_cast(end - begin).count(); cut_points += test.getCutPoints().size(); } - printf("%-20s %4lu %4d %7.2f\n", dataset.first.c_str(), attributes.size(), cut_points, timing); + printf("%-20s %4lu %4d %8zu\n", dataset.first.c_str(), attributes.size(), cut_points, timing); } } From 49e9dd3e127f74aae2e5ffa2bc8d2479203f421a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 12 Mar 2023 11:30:43 +0100 Subject: [PATCH 15/32] Update build --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d042490..7a65dd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,11 +34,11 @@ jobs: cd build ctest -C Release --output-on-failure cd .. - gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=coverage.xml + gcovr --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ - --define sonar.coverageReportPaths=coverage.xml + --define sonar.coverageReportPaths=tests/coverage.xml From ed784736ca33140e39d25ba4eee2a55c97b1edd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 12 Mar 2023 11:39:35 +0100 Subject: [PATCH 16/32] update build --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a65dd8..deea63f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,12 +33,12 @@ jobs: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release cd build ctest -C Release --output-on-failure - cd .. - gcovr --txt --sonarqube=coverage.xml + cd ../.. + gcovr -e "test/*" --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ - --define sonar.coverageReportPaths=tests/coverage.xml + --define sonar.coverageReportPaths=coverage.xml From ffb8df4d1cf6ed294072156acaaf55d3da456f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 13 Mar 2023 01:17:04 +0100 Subject: [PATCH 17/32] Add max_cutpoints Hyperparameter --- CPPFImdlp.cpp | 43 +++++++++++++++++++++------------- CPPFImdlp.h | 9 +++++--- sample/sample.cpp | 36 +++++++++++++++++++++-------- tests/ArffFiles.cpp | 7 +++--- tests/FImdlp_unittest.cpp | 47 ++++++++++++++++++++++++++++++++------ tests/Metrics_unittest.cpp | 1 + tests/test | 3 ++- 7 files changed, 105 insertions(+), 41 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index a2c17ce..7aa0beb 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -3,25 +3,42 @@ #include #include #include +#include #include "CPPFImdlp.h" #include "Metrics.h" - namespace mdlp { - CPPFImdlp::CPPFImdlp():depth(0), max_depth(numeric_limits::max()), min_length(3), indices(indices_t()), X(samples_t()), y(labels_t()), - metrics(Metrics(y, indices)) + CPPFImdlp::CPPFImdlp():min_length(3), depth(0), max_depth(numeric_limits::max()), proposed_cuts(0), + indices(indices_t()), X(samples_t()), y(labels_t()), + metrics(Metrics(y, indices)), num_cut_points(numeric_limits::max()) { } - CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_): depth(0), max_depth(max_depth_), min_length(min_length_), indices(indices_t()), X(samples_t()), y(labels_t()), - metrics(Metrics(y, indices)) + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), depth(0), + max_depth(max_depth_), proposed_cuts(proposed), indices(indices_t()), X(samples_t()), y(labels_t()), + metrics(Metrics(y, indices)), num_cut_points(numeric_limits::max()) { } CPPFImdlp::~CPPFImdlp() = default; - CPPFImdlp& CPPFImdlp::fit(samples_t& X_, labels_t& y_) + size_t CPPFImdlp::compute_max_num_cut_points() + { + // Set the actual maximum number of cut points as a number or as a percentage of the number of samples + if (proposed_cuts == 0) { + return numeric_limits::max(); + } + if (proposed_cuts < 0 || proposed_cuts > X.size()) { + throw invalid_argument("wrong proposed num_cuts value"); + } + if (proposed_cuts < 1) + return (int)round(X.size() * proposed_cuts); + return (int)proposed_cuts; + } + + void CPPFImdlp::fit(samples_t& X_, labels_t& y_) { X = X_; y = y_; + num_cut_points = compute_max_num_cut_points(); depth = 0; cutPoints.clear(); if (X.size() != y.size()) { @@ -39,7 +56,6 @@ namespace mdlp { indices = sortIndices(X_, y_); metrics.setData(y, indices); computeCutPoints(0, X.size(), 1); - return *this; } pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) @@ -75,6 +91,8 @@ namespace mdlp { { size_t cut; pair result; + if (cutPoints.size() == num_cut_points) + return; // Check if the interval length and the depth are Ok if (end - start < min_length || depth_ > max_depth) return; @@ -158,15 +176,8 @@ namespace mdlp { cutPoints_t CPPFImdlp::getCutPoints() { - // Remove duplicates and sort - cutPoints_t output(cutPoints.size()); - set s; - unsigned size = cutPoints.size(); - for (unsigned i = 0; i < size; i++) - s.insert(cutPoints[i]); - output.assign(s.begin(), s.end()); - sort(output.begin(), output.end()); - return output; + sort(cutPoints.begin(), cutPoints.end()); + return cutPoints; } int CPPFImdlp::get_depth() { diff --git a/CPPFImdlp.h b/CPPFImdlp.h index ac57eb2..a24ffaf 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -9,22 +9,25 @@ namespace mdlp { protected: size_t min_length; int depth, max_depth; + float proposed_cuts; + indices_t indices; samples_t X; labels_t y; - indices_t indices; Metrics metrics; cutPoints_t cutPoints; + size_t num_cut_points; static indices_t sortIndices(samples_t&, labels_t&); void computeCutPoints(size_t, size_t, int); bool mdlp(size_t, size_t, size_t); size_t getCandidate(size_t, size_t); + size_t compute_max_num_cut_points(); pair valueCutPoint(size_t, size_t, size_t); public: CPPFImdlp(); - CPPFImdlp(size_t, int); + CPPFImdlp(size_t, int, float); ~CPPFImdlp(); - CPPFImdlp& fit(samples_t&, labels_t&); + void fit(samples_t&, labels_t&); cutPoints_t getCutPoints(); int get_depth(); inline string version() { return "1.1.1"; }; diff --git a/sample/sample.cpp b/sample/sample.cpp index d8c7b3f..926d464 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -25,25 +25,28 @@ void usage(const char* path) cout << " -f, --file[=FILENAME]\t {all, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors, test}." << endl; cout << " -p, --path[=FILENAME]\t folder where the arff dataset is located, default " << PATH << endl; cout << " -m, --max_depth=INT\t max_depth pased to discretizer. Default = MAX_INT" << endl; + cout << " -c, --max_cutpoints=FLOAT\t percentage of lines expressed in decimal or integer number or cut points. Default = 0 = any" << endl; cout << " -n, --min_length=INT\t interval min_length pased to discretizer. Default = 3" << endl; } -tuple parse_arguments(int argc, char** argv) +tuple parse_arguments(int argc, char** argv) { string file_name; string path = PATH; int max_depth = numeric_limits::max(); int min_length = 3; + float max_cutpoints = 0; static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "file", required_argument, 0, 'f' }, { "path", required_argument, 0, 'p' }, { "max_depth", required_argument, 0, 'm' }, + { "max_cutpoints", required_argument, 0, 'c' }, { "min_length", required_argument, 0, 'n' }, { 0, 0, 0, 0 } }; while (1) { - auto c = getopt_long(argc, argv, "hf:p:m:n:", long_options, 0); + auto c = getopt_long(argc, argv, "hf:p:m:c:n:", long_options, 0); if (c == -1) break; switch (c) { @@ -59,6 +62,9 @@ tuple parse_arguments(int argc, char** argv) case 'n': min_length = atoi(optarg); break; + case 'c': + max_cutpoints = atof(optarg); + break; case 'p': path = optarg; if (path.back() != '/') @@ -75,10 +81,10 @@ tuple parse_arguments(int argc, char** argv) usage(argv[0]); exit(1); } - return make_tuple(file_name, path, max_depth, min_length); + return make_tuple(file_name, path, max_depth, min_length, max_cutpoints); } -void process_file(string path, string file_name, bool class_last, int max_depth, int min_length) +void process_file(string path, string file_name, bool class_last, int max_depth, int min_length, float max_cutpoints) { ArffFiles file; @@ -101,7 +107,7 @@ void process_file(string path, string file_name, bool class_last, int max_depth, } cout << y[i] << endl; } - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth); + mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); auto total = 0; for (auto i = 0; i < attributes.size(); i++) { auto min_max = minmax_element(X[i].begin(), X[i].end()); @@ -118,7 +124,7 @@ void process_file(string path, string file_name, bool class_last, int max_depth, cout << "Total feature states: " << total + attributes.size() << endl; } -void process_all_files(map datasets, string path, int max_depth, int min_length) +void process_all_files(map datasets, string path, int max_depth, int min_length, float max_cutpoints) { cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << endl << endl; printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(ms)"); @@ -132,7 +138,7 @@ void process_all_files(map datasets, string path, int max_depth, i size_t timing = 0; int cut_points = 0; for (auto i = 0; i < attributes.size(); i++) { - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth); + mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); test.fit(X[i], y); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); @@ -157,19 +163,29 @@ int main(int argc, char** argv) }; string file_name, path; int max_depth, min_length; - tie(file_name, path, max_depth, min_length) = parse_arguments(argc, argv); + float max_cutpoints; + tie(file_name, path, max_depth, min_length, max_cutpoints) = parse_arguments(argc, argv); if (datasets.find(file_name) == datasets.end() && file_name != "all") { cout << "Invalid file name: " << file_name << endl; usage(argv[0]); exit(1); } if (file_name == "all") - process_all_files(datasets, path, max_depth, min_length); + process_all_files(datasets, path, max_depth, min_length, max_cutpoints); else { - process_file(path, file_name, datasets[file_name], max_depth, min_length); + process_file(path, file_name, datasets[file_name], max_depth, min_length, max_cutpoints); cout << "File name: " << file_name << endl; cout << "Max depth: " << max_depth << endl; cout << "Min length: " << min_length << endl; } + mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); + samples_t X = { 4.7, 4.7, 4.7, 4.7, 4.8, 4.8, 4.8, 4.8, 4.9, 4.95, 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; + labels_t y = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + test.fit(X, y); + vector computed = test.getCutPoints(); + cout << "Computed cut points: " << endl; + for (auto item : computed) { + cout << item << endl; + } return 0; } \ No newline at end of file diff --git a/tests/ArffFiles.cpp b/tests/ArffFiles.cpp index 470f5fa..4fbca78 100644 --- a/tests/ArffFiles.cpp +++ b/tests/ArffFiles.cpp @@ -40,11 +40,10 @@ vector& ArffFiles::getY() void ArffFiles::load(string fileName, bool classLast) { ifstream file(fileName); - string keyword, attribute, type; if (file.is_open()) { - string line; + string line, keyword, attribute, type; while (getline(file, line)) { - if (line[0] == '%' || line.empty() || line == "\r" || line == " ") { + if (line.empty() || line[0] == '%' || line == "\r" || line == " ") { continue; } if (line.find("@attribute") != string::npos || line.find("@ATTRIBUTE") != string::npos) { @@ -79,7 +78,7 @@ void ArffFiles::generateDataset(bool classLast) X = vector>(attributes.size(), vector(lines.size())); vector yy = vector(lines.size(), ""); int labelIndex = classLast ? attributes.size() : 0; - for (int i = 0; i < lines.size(); i++) { + for (size_t i = 0; i < lines.size(); i++) { stringstream ss(lines[i]); string value; int pos = 0, xIndex = 0; diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index e76609a..059c592 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -86,13 +86,22 @@ namespace mdlp { } TEST_F(TestFImdlp, FitErrorMinLengtMaxDepth) { - auto testLength = CPPFImdlp(2, 10); - auto testDepth = CPPFImdlp(3, 0); + auto testLength = CPPFImdlp(2, 10, 0); + auto testDepth = CPPFImdlp(3, 0, 0); X = { 1, 2, 3 }; y = { 1, 2, 3 }; EXPECT_THROW(testLength.fit(X, y), invalid_argument); EXPECT_THROW(testDepth.fit(X, y), invalid_argument); } + TEST_F(TestFImdlp, FitErrorMaxCutPoints) + { + auto testmin = CPPFImdlp(2, 10, -1); + auto testmax = CPPFImdlp(3, 0, 200); + X = { 1, 2, 3 }; + y = { 1, 2, 3 }; + EXPECT_THROW(testmin.fit(X, y), invalid_argument); + EXPECT_THROW(testmax.fit(X, y), invalid_argument); + } TEST_F(TestFImdlp, SortIndices) { X = { 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; @@ -139,10 +148,8 @@ namespace mdlp { TEST_F(TestFImdlp, TestArtificialDataset) { fit(X, y); - computeCutPoints(0, 20, 1); cutPoints_t expected = { 5.05 }; vector computed = getCutPoints(); - computed = getCutPoints(); int expectedSize = expected.size(); EXPECT_EQ(computed.size(), expected.size()); for (unsigned long i = 0; i < computed.size(); i++) { @@ -194,7 +201,7 @@ namespace mdlp { TEST_F(TestFImdlp, MaxDepth) { // Set max_depth to 1 - auto test = CPPFImdlp(3, 1); + auto test = CPPFImdlp(3, 1, 0); vector expected = { { 5.45 }, { 3.35 }, @@ -206,7 +213,7 @@ namespace mdlp { } TEST_F(TestFImdlp, MinLength) { - auto test = CPPFImdlp(75, 100); + auto test = CPPFImdlp(75, 100, 0); // Set min_length to 75 vector expected = { { 5.45, 5.75 }, @@ -220,7 +227,33 @@ namespace mdlp { TEST_F(TestFImdlp, MinLengthMaxDepth) { // Set min_length to 75 - auto test = CPPFImdlp(75, 2); + auto test = CPPFImdlp(75, 2, 0); + vector expected = { + { 5.45, 5.75 }, + { 2.85, 3.35 }, + { 2.45, 4.75 }, + { 0.8, 1.75 } + }; + int depths[] = { 2, 2, 2, 2 }; + test_dataset(test, "iris", expected, depths); + } + TEST_F(TestFImdlp, MaxCutPointsInteger) + { + // Set min_length to 75 + auto test = CPPFImdlp(75, 2, 1); + vector expected = { + { 5.45 }, + { 3.35 }, + { 2.45 }, + { 0.8} + }; + int depths[] = { 1, 1, 1, 1 }; + test_dataset(test, "iris", expected, depths); + } + TEST_F(TestFImdlp, MaxCutPointsFloat) + { + // Set min_length to 75 + auto test = CPPFImdlp(75, 2, 0.2); vector expected = { { 5.45, 5.75 }, { 2.85, 3.35 }, diff --git a/tests/Metrics_unittest.cpp b/tests/Metrics_unittest.cpp index c6e3e56..e7d6f8b 100644 --- a/tests/Metrics_unittest.cpp +++ b/tests/Metrics_unittest.cpp @@ -36,6 +36,7 @@ namespace mdlp { TEST_F(TestMetrics, InformationGain) { ASSERT_NEAR(1, informationGain(0, 5, 10), precision); + ASSERT_NEAR(1, informationGain(0, 5, 10), precision); // For cache y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; setData(y, indices); ASSERT_NEAR(0.108032, informationGain(0, 5, 10), precision); diff --git a/tests/test b/tests/test index ca7061f..4180150 100755 --- a/tests/test +++ b/tests/test @@ -13,4 +13,5 @@ rm -fr gcovr-report/* 2>/dev/null #lcov --capture --directory ./ --output-file lcoverage/main_coverage.info #lcov --remove lcoverage/main_coverage.info 'v1/*' '/Applications/*' '*/tests/*' --output-file lcoverage/main_coverage.info -q #lcov --list lcoverage/main_coverage.info -gcovr --root .. --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=gcovr-report/coverage.xml +cd .. +gcovr --gcov-filter "CPPFImdlp.cpp" --gcov-filter "Metrics.cpp" --txt --sonarqube=tests/gcovr-report/coverage.xml From 7551b0d6692ebcf67a5789216fcf20d719ccd275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 13 Mar 2023 01:36:29 +0100 Subject: [PATCH 18/32] Refactor constructor --- CPPFImdlp.cpp | 11 +++-------- CPPFImdlp.h | 17 +++++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 7aa0beb..0cd833c 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -8,16 +8,11 @@ #include "Metrics.h" namespace mdlp { - CPPFImdlp::CPPFImdlp():min_length(3), depth(0), max_depth(numeric_limits::max()), proposed_cuts(0), - indices(indices_t()), X(samples_t()), y(labels_t()), - metrics(Metrics(y, indices)), num_cut_points(numeric_limits::max()) - { - } - CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), depth(0), - max_depth(max_depth_), proposed_cuts(proposed), indices(indices_t()), X(samples_t()), y(labels_t()), - metrics(Metrics(y, indices)), num_cut_points(numeric_limits::max()) + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), + max_depth(max_depth_), proposed_cuts(proposed) { } + CPPFImdlp::CPPFImdlp() = default; CPPFImdlp::~CPPFImdlp() = default; size_t CPPFImdlp::compute_max_num_cut_points() diff --git a/CPPFImdlp.h b/CPPFImdlp.h index a24ffaf..f7364c2 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -7,15 +7,16 @@ namespace mdlp { class CPPFImdlp { protected: - size_t min_length; - int depth, max_depth; - float proposed_cuts; - indices_t indices; - samples_t X; - labels_t y; - Metrics metrics; + size_t min_length = 3; + int depth = 0; + int max_depth = numeric_limits::max(); + float proposed_cuts = 0; + indices_t indices = indices_t(); + samples_t X = samples_t(); + labels_t y = labels_t(); + Metrics metrics = Metrics(y, indices); cutPoints_t cutPoints; - size_t num_cut_points; + size_t num_cut_points = numeric_limits::max(); static indices_t sortIndices(samples_t&, labels_t&); void computeCutPoints(size_t, size_t, int); From d9a6f528f69803de5228ef8c5ecb412821185dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 13 Mar 2023 16:56:09 +0100 Subject: [PATCH 19/32] Fix 2 code smell --- .vscode/settings.json | 6 ++++-- CPPFImdlp.cpp | 8 ++++---- CPPFImdlp.h | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b89d404..85f6c83 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,9 @@ { "sonarlint.connectedMode.project": { - "connectionId": "sonarcloud", + "connectionId": "rmontanana", "projectKey": "rmontanana_mdlp" }, - "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "cmake.configureOnOpen": true, + "sonarlint.pathToCompileCommands": "${workspaceFolder}/build/compile_commands.json" } \ No newline at end of file diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 0cd833c..b09af46 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -15,7 +15,7 @@ namespace mdlp { CPPFImdlp::CPPFImdlp() = default; CPPFImdlp::~CPPFImdlp() = default; - size_t CPPFImdlp::compute_max_num_cut_points() + size_t CPPFImdlp::compute_max_num_cut_points() const { // Set the actual maximum number of cut points as a number or as a percentage of the number of samples if (proposed_cuts == 0) { @@ -25,8 +25,8 @@ namespace mdlp { throw invalid_argument("wrong proposed num_cuts value"); } if (proposed_cuts < 1) - return (int)round(X.size() * proposed_cuts); - return (int)proposed_cuts; + return static_cast(round(X.size() * proposed_cuts)); + return static_cast(proposed_cuts); } void CPPFImdlp::fit(samples_t& X_, labels_t& y_) @@ -174,7 +174,7 @@ namespace mdlp { sort(cutPoints.begin(), cutPoints.end()); return cutPoints; } - int CPPFImdlp::get_depth() + int CPPFImdlp::get_depth() const { return depth; } diff --git a/CPPFImdlp.h b/CPPFImdlp.h index f7364c2..574d8cb 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -22,7 +22,7 @@ namespace mdlp { void computeCutPoints(size_t, size_t, int); bool mdlp(size_t, size_t, size_t); size_t getCandidate(size_t, size_t); - size_t compute_max_num_cut_points(); + size_t compute_max_num_cut_points() const; pair valueCutPoint(size_t, size_t, size_t); public: CPPFImdlp(); @@ -30,7 +30,7 @@ namespace mdlp { ~CPPFImdlp(); void fit(samples_t&, labels_t&); cutPoints_t getCutPoints(); - int get_depth(); + int get_depth() const; inline string version() { return "1.1.1"; }; }; } From 14860ea0b997dd78b79bddf82e4c96531751d949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 13 Mar 2023 17:17:31 +0100 Subject: [PATCH 20/32] Fix smell and add new test --- CPPFImdlp.cpp | 2 +- tests/FImdlp_unittest.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index b09af46..c7b4a08 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -25,7 +25,7 @@ namespace mdlp { throw invalid_argument("wrong proposed num_cuts value"); } if (proposed_cuts < 1) - return static_cast(round(X.size() * proposed_cuts)); + return static_cast(round(static_cast(X.size()) * proposed_cuts)); return static_cast(proposed_cuts); } diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index 059c592..2d40e3e 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -263,4 +263,15 @@ namespace mdlp { int depths[] = { 2, 2, 2, 2 }; test_dataset(test, "iris", expected, depths); } + TEST_F(TestFImdlp, ProposedCuts) + { + vector> proposed_list = { { 0.1, 2}, { 0.5, 10}, {0.07, 1}, {1, 1}, {2, 2} }; + size_t expected, computed; + for (auto proposed_item : proposed_list) { + tie(proposed_cuts, expected) = proposed_item; + computed = compute_max_num_cut_points(); + ASSERT_EQ(expected, computed); + } + + } } From ed7433672d98745115fb5f0bc49fcbd7bf035427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 13 Mar 2023 17:45:06 +0100 Subject: [PATCH 21/32] Add checked strings in exceptions --- tests/FImdlp_unittest.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index 2d40e3e..ac805bd 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -3,6 +3,14 @@ #include "../CPPFImdlp.h" #include "ArffFiles.h" #include +#define EXPECT_THROW_WITH_MESSAGE(stmt, etype, whatstring) EXPECT_THROW( \ +try { \ +stmt; \ +} catch (const etype& ex) { \ +EXPECT_EQ(whatstring, std::string(ex.what())); \ +throw; \ +} \ +, etype) namespace mdlp { class TestFImdlp: public CPPFImdlp, public testing::Test { @@ -76,13 +84,13 @@ namespace mdlp { { X = samples_t(); y = labels_t(); - EXPECT_THROW(fit(X, y), std::invalid_argument); + EXPECT_THROW_WITH_MESSAGE(fit(X, y), invalid_argument, "X and y must have at least one element"); } TEST_F(TestFImdlp, FitErrorDifferentSize) { X = { 1, 2, 3 }; y = { 1, 2 }; - EXPECT_THROW(fit(X, y), std::invalid_argument); + EXPECT_THROW_WITH_MESSAGE(fit(X, y), invalid_argument, "X and y must have the same size"); } TEST_F(TestFImdlp, FitErrorMinLengtMaxDepth) { @@ -90,8 +98,8 @@ namespace mdlp { auto testDepth = CPPFImdlp(3, 0, 0); X = { 1, 2, 3 }; y = { 1, 2, 3 }; - EXPECT_THROW(testLength.fit(X, y), invalid_argument); - EXPECT_THROW(testDepth.fit(X, y), invalid_argument); + EXPECT_THROW_WITH_MESSAGE(testLength.fit(X, y), invalid_argument, "min_length must be greater than 2"); + EXPECT_THROW_WITH_MESSAGE(testDepth.fit(X, y), invalid_argument, "max_depth must be greater than 0"); } TEST_F(TestFImdlp, FitErrorMaxCutPoints) { @@ -99,8 +107,8 @@ namespace mdlp { auto testmax = CPPFImdlp(3, 0, 200); X = { 1, 2, 3 }; y = { 1, 2, 3 }; - EXPECT_THROW(testmin.fit(X, y), invalid_argument); - EXPECT_THROW(testmax.fit(X, y), invalid_argument); + EXPECT_THROW_WITH_MESSAGE(testmin.fit(X, y), invalid_argument, "wrong proposed num_cuts value"); + EXPECT_THROW_WITH_MESSAGE(testmax.fit(X, y), invalid_argument, "wrong proposed num_cuts value"); } TEST_F(TestFImdlp, SortIndices) { From 770502c8e57a3ea57a722091ce05e4eb08c444d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 14 Mar 2023 11:36:38 +0100 Subject: [PATCH 22/32] Update sample --- sample/sample.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/sample/sample.cpp b/sample/sample.cpp index 926d464..0a25f05 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -174,18 +174,10 @@ int main(int argc, char** argv) process_all_files(datasets, path, max_depth, min_length, max_cutpoints); else { process_file(path, file_name, datasets[file_name], max_depth, min_length, max_cutpoints); - cout << "File name: " << file_name << endl; - cout << "Max depth: " << max_depth << endl; - cout << "Min length: " << min_length << endl; - } - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); - samples_t X = { 4.7, 4.7, 4.7, 4.7, 4.8, 4.8, 4.8, 4.8, 4.9, 4.95, 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; - labels_t y = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; - test.fit(X, y); - vector computed = test.getCutPoints(); - cout << "Computed cut points: " << endl; - for (auto item : computed) { - cout << item << endl; + cout << "File name ....: " << file_name << endl; + cout << "Max depth ....: " << max_depth << endl; + cout << "Min length ...: " << min_length << endl; + cout << "Max cutpoints : " << max_cutpoints << endl; } return 0; } \ No newline at end of file From 1f4abade2c0b76759b1cec8f38e781fec4688349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Fri, 17 Mar 2023 00:14:28 +0100 Subject: [PATCH 23/32] Add launch.json for debugging sample in vscode --- sample/.vscode/launch.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 sample/.vscode/launch.json diff --git a/sample/.vscode/launch.json b/sample/.vscode/launch.json new file mode 100644 index 0000000..30e5379 --- /dev/null +++ b/sample/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch sample", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/build/sample", + "args": [ + "-f", + "glass" + ], + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/build/", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + } + ] +} \ No newline at end of file From f0845c5bd1dbfbd0af8729cdf743817b8fbdaf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 18 Mar 2023 18:40:10 +0100 Subject: [PATCH 24/32] Fix mistake in class type of ArffFiles Add some type casting to CPPFImdlp Add additional path to datasets in tests Fix some smells in sample Join CMakeLists --- .vscode/launch.json | 8 +++-- .vscode/tasks.json | 29 ---------------- CMakeLists.txt | 4 ++- CPPFImdlp.cpp | 11 +++--- sample/.vscode/launch.json | 19 +++-------- sample/CMakeLists.txt | 2 -- sample/sample.cpp | 70 ++++++++++++++++++++------------------ tests/ArffFiles.cpp | 22 ++++++------ tests/ArffFiles.h | 4 +-- tests/CMakeLists.txt | 7 ++-- tests/FImdlp_unittest.cpp | 22 +++++++++--- tests/datasets/glass.arff | 2 +- 12 files changed, 87 insertions(+), 113 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index c865479..d84105c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,12 +5,14 @@ "version": "0.2.0", "configurations": [ { - "name": "(lldb) Launch", - "type": "cppdbg", + "name": "lldb samplex", + "type": "lldb", "request": "launch", + "targetArchitecture": "arm64", "program": "${workspaceRoot}/sample/build/sample", "args": [ - "mfeat-factors" + "-f", + "glass" ], "stopAtEntry": false, "cwd": "${workspaceRoot}/sample/build/", diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 5318667..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "tasks": [ - { - "type": "cppbuild", - "label": "C/C++: clang++ build active file", - "command": "/usr/bin/clang++", - "args": [ - "-fcolor-diagnostics", - "-fansi-escape-codes", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}" - ], - "options": { - "cwd": "${fileDirname}" - }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "detail": "Task generated by Debugger." - } - ], - "version": "2.0.0" -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ff48211..a711c93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,5 +3,7 @@ project(mdlp) set(CMAKE_CXX_STANDARD 11) -add_library(mdlp CPPFImdlp.cpp Metrics.cpp) +add_library(mdlp CPPFImdlp.cpp Metrics.cpp sample/sample.cpp) +add_subdirectory(sample) +add_subdirectory(tests) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index c7b4a08..0f73173 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "CPPFImdlp.h" #include "Metrics.h" namespace mdlp { @@ -21,7 +20,7 @@ namespace mdlp { if (proposed_cuts == 0) { return numeric_limits::max(); } - if (proposed_cuts < 0 || proposed_cuts > X.size()) { + if (proposed_cuts < 0 || proposed_cuts > static_cast(X.size())) { throw invalid_argument("wrong proposed num_cuts value"); } if (proposed_cuts < 1) @@ -125,8 +124,8 @@ namespace mdlp { // Cutpoints are always on boundaries (definition 2) if (y[indices[idx]] == y[indices[idx - 1]]) continue; - entropy_left = precision_t(idx - start) / elements * metrics.entropy(start, idx); - entropy_right = precision_t(end - idx) / elements * metrics.entropy(idx, end); + entropy_left = precision_t(idx - start) / static_cast(elements) * metrics.entropy(start, idx); + entropy_right = precision_t(end - idx) / static_cast(elements) * metrics.entropy(idx, end); if (entropy_left + entropy_right < minEntropy) { minEntropy = entropy_left + entropy_right; candidate = idx; @@ -148,8 +147,8 @@ namespace mdlp { ent1 = metrics.entropy(start, cut); ent2 = metrics.entropy(cut, end); ig = metrics.informationGain(start, cut, end); - delta = log2(pow(3, precision_t(k)) - 2) - - (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2); + delta = static_cast(log2(pow(3, precision_t(k)) - 2) - + (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); precision_t term = 1 / N * (log2(N - 1) + delta); return ig > term; } diff --git a/sample/.vscode/launch.json b/sample/.vscode/launch.json index 30e5379..a573994 100644 --- a/sample/.vscode/launch.json +++ b/sample/.vscode/launch.json @@ -1,30 +1,21 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - "name": "Launch sample", + "name": "lldb puro", "type": "cppdbg", + // "targetArchitecture": "arm64", "request": "launch", "program": "${workspaceRoot}/build/sample", "args": [ "-f", - "glass" - ], - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } + "iris" ], "stopAtEntry": false, "cwd": "${workspaceRoot}/build/", "environment": [], "externalConsole": false, - "MIMode": "gdb", - } + "MIMode": "lldb" + }, ] } \ No newline at end of file diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 68ba4df..4f6fe11 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.20) -project(main) set(CMAKE_CXX_STANDARD 11) diff --git a/sample/sample.cpp b/sample/sample.cpp index 0a25f05..cd2bbde 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -14,39 +14,41 @@ using namespace mdlp; const string PATH = "../../tests/datasets/"; /* print a description of all supported options */ -void usage(const char* path) -{ +void usage(const char *path) { /* take only the last portion of the path */ - const char* basename = strrchr(path, '/'); + const char *basename = strrchr(path, '/'); basename = basename ? basename + 1 : path; cout << "usage: " << basename << "[OPTION]" << endl; cout << " -h, --help\t\t Print this help and exit." << endl; - cout << " -f, --file[=FILENAME]\t {all, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors, test}." << endl; + cout + << " -f, --file[=FILENAME]\t {all, glass, iris, kdd_JapaneseVowels, letter, liver-disorders, mfeat-factors, test}." + << endl; cout << " -p, --path[=FILENAME]\t folder where the arff dataset is located, default " << PATH << endl; cout << " -m, --max_depth=INT\t max_depth pased to discretizer. Default = MAX_INT" << endl; - cout << " -c, --max_cutpoints=FLOAT\t percentage of lines expressed in decimal or integer number or cut points. Default = 0 = any" << endl; + cout + << " -c, --max_cutpoints=FLOAT\t percentage of lines expressed in decimal or integer number or cut points. Default = 0 = any" + << endl; cout << " -n, --min_length=INT\t interval min_length pased to discretizer. Default = 3" << endl; } -tuple parse_arguments(int argc, char** argv) -{ +tuple parse_arguments(int argc, char **argv) { string file_name; string path = PATH; int max_depth = numeric_limits::max(); int min_length = 3; float max_cutpoints = 0; static struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "file", required_argument, 0, 'f' }, - { "path", required_argument, 0, 'p' }, - { "max_depth", required_argument, 0, 'm' }, - { "max_cutpoints", required_argument, 0, 'c' }, - { "min_length", required_argument, 0, 'n' }, - { 0, 0, 0, 0 } + {"help", no_argument, nullptr, 'h'}, + {"file", required_argument, nullptr, 'f'}, + {"path", required_argument, nullptr, 'p'}, + {"max_depth", required_argument, nullptr, 'm'}, + {"max_cutpoints", required_argument, nullptr, 'c'}, + {"min_length", required_argument, nullptr, 'n'}, + {nullptr, 0, nullptr, 0} }; - while (1) { - auto c = getopt_long(argc, argv, "hf:p:m:c:n:", long_options, 0); + while (true) { + auto c = getopt_long(argc, argv, "hf:p:m:c:n:", long_options, nullptr); if (c == -1) break; switch (c) { @@ -57,13 +59,13 @@ tuple parse_arguments(int argc, char** argv) file_name = optarg; break; case 'm': - max_depth = atoi(optarg); + max_depth = (int) strtol(optarg, nullptr, 10); break; case 'n': - min_length = atoi(optarg); + min_length = (int) strtol(optarg, nullptr, 10); break; case 'c': - max_cutpoints = atof(optarg); + max_cutpoints = strtof(optarg, nullptr); break; case 'p': path = optarg; @@ -84,8 +86,8 @@ tuple parse_arguments(int argc, char** argv) return make_tuple(file_name, path, max_depth, min_length, max_cutpoints); } -void process_file(string path, string file_name, bool class_last, int max_depth, int min_length, float max_cutpoints) -{ +void process_file(const string &path, const string &file_name, bool class_last, int max_depth, int min_length, + float max_cutpoints) { ArffFiles file; file.load(path + file_name + ".arff", class_last); @@ -93,16 +95,16 @@ void process_file(string path, string file_name, bool class_last, int max_depth, int items = file.getSize(); cout << "Number of lines: " << items << endl; cout << "Attributes: " << endl; - for (auto attribute : attributes) { + for (auto attribute: attributes) { cout << "Name: " << get<0>(attribute) << " Type: " << get<1>(attribute) << endl; } cout << "Class name: " << file.getClassName() << endl; cout << "Class type: " << file.getClassType() << endl; cout << "Data: " << endl; - vector& X = file.getX(); - labels_t& y = file.getY(); + vector &X = file.getX(); + labels_t &y = file.getY(); for (int i = 0; i < 5; i++) { - for (auto feature : X) { + for (auto feature: X) { cout << fixed << setprecision(1) << feature[i] << " "; } cout << y[i] << endl; @@ -115,7 +117,7 @@ void process_file(string path, string file_name, bool class_last, int max_depth, cout << "Min: " << *min_max.first << " Max: " << *min_max.second << endl; cout << "--------------------------" << setprecision(3) << endl; test.fit(X[i], y); - for (auto item : test.getCutPoints()) { + for (auto item: test.getCutPoints()) { cout << item << endl; } total += test.getCutPoints().size(); @@ -124,17 +126,18 @@ void process_file(string path, string file_name, bool class_last, int max_depth, cout << "Total feature states: " << total + attributes.size() << endl; } -void process_all_files(map datasets, string path, int max_depth, int min_length, float max_cutpoints) -{ - cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << endl << endl; +void process_all_files(const map &datasets, const string &path, int max_depth, int min_length, + float max_cutpoints) { + cout << "Results: " << "Max_depth: " << max_depth << " Min_length: " << min_length << " Max_cutpoints: " + << max_cutpoints << endl << endl; printf("%-20s %4s %4s\n", "Dataset", "Feat", "Cuts Time(ms)"); printf("==================== ==== ==== ========\n"); - for (auto dataset : datasets) { + for (const auto &dataset: datasets) { ArffFiles file; file.load(path + dataset.first + ".arff", dataset.second); auto attributes = file.getAttributes(); - vector& X = file.getX(); - labels_t& y = file.getY(); + vector &X = file.getX(); + labels_t &y = file.getY(); size_t timing = 0; int cut_points = 0; for (auto i = 0; i < attributes.size(); i++) { @@ -150,8 +153,7 @@ void process_all_files(map datasets, string path, int max_depth, i } -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { map datasets = { {"glass", true}, {"iris", true}, diff --git a/tests/ArffFiles.cpp b/tests/ArffFiles.cpp index 4fbca78..a7286eb 100644 --- a/tests/ArffFiles.cpp +++ b/tests/ArffFiles.cpp @@ -2,13 +2,10 @@ #include #include #include -#include using namespace std; -ArffFiles::ArffFiles() -{ -} +ArffFiles::ArffFiles() = default; vector ArffFiles::getLines() { return lines; @@ -37,19 +34,22 @@ vector& ArffFiles::getY() { return y; } -void ArffFiles::load(string fileName, bool classLast) +void ArffFiles::load(const string fileName, bool classLast) { ifstream file(fileName); if (file.is_open()) { - string line, keyword, attribute, type; + string line, keyword, attribute, type, type_w; while (getline(file, line)) { if (line.empty() || line[0] == '%' || line == "\r" || line == " ") { continue; } if (line.find("@attribute") != string::npos || line.find("@ATTRIBUTE") != string::npos) { stringstream ss(line); - ss >> keyword >> attribute >> type; - attributes.push_back({ attribute, type }); + ss >> keyword >> attribute; + type = ""; + while(ss >> type_w) + type += type_w + " "; + attributes.emplace_back(attribute, type ); continue; } if (line[0] == '@') { @@ -77,7 +77,7 @@ void ArffFiles::generateDataset(bool classLast) { X = vector>(attributes.size(), vector(lines.size())); vector yy = vector(lines.size(), ""); - int labelIndex = classLast ? attributes.size() : 0; + int labelIndex = classLast ? static_cast(attributes.size()) : 0; for (size_t i = 0; i < lines.size(); i++) { stringstream ss(lines[i]); string value; @@ -92,7 +92,7 @@ void ArffFiles::generateDataset(bool classLast) } y = factorize(yy); } -string ArffFiles::trim(const string& source) +string ArffFiles::trim(const string& source) { string s(source); s.erase(0, s.find_first_not_of(" \n\r\t")); @@ -105,7 +105,7 @@ vector ArffFiles::factorize(const vector& labels_t) yy.reserve(labels_t.size()); map labelMap; int i = 0; - for (string label : labels_t) { + for (const string &label : labels_t) { if (labelMap.find(label) == labelMap.end()) { labelMap[label] = i++; } diff --git a/tests/ArffFiles.h b/tests/ArffFiles.h index b56d28d..8590d04 100644 --- a/tests/ArffFiles.h +++ b/tests/ArffFiles.h @@ -18,10 +18,10 @@ public: unsigned long int getSize(); string getClassName(); string getClassType(); - string trim(const string&); + static string trim(const string&); vector>& getX(); vector& getY(); vector> getAttributes(); - vector factorize(const vector& labels_t); + static vector factorize(const vector& labels_t); }; #endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 58b1e86..67cc084 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,5 @@ -cmake_minimum_required(VERSION 3.14) -project(FImdlp) - # GoogleTest requires at least C++14 -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 11) include(FetchContent) include_directories(${GTEST_INCLUDE_DIRS}) @@ -18,7 +15,7 @@ FetchContent_MakeAvailable(googletest) enable_testing() add_executable(Metrics_unittest ../Metrics.cpp Metrics_unittest.cpp) -add_executable(FImdlp_unittest ../CPPFImdlp.cpp ../ArffFiles.cpp ../Metrics.cpp FImdlp_unittest.cpp) +add_executable(FImdlp_unittest ../CPPFImdlp.cpp ArffFiles.cpp ../Metrics.cpp FImdlp_unittest.cpp) target_link_libraries(Metrics_unittest GTest::gtest_main) target_link_libraries(FImdlp_unittest GTest::gtest_main) target_compile_options(Metrics_unittest PRIVATE --coverage) diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index ac805bd..56e6c1e 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -1,8 +1,9 @@ #include "gtest/gtest.h" #include "../Metrics.h" #include "../CPPFImdlp.h" -#include "ArffFiles.h" +#include #include +#include "ArffFiles.h" #define EXPECT_THROW_WITH_MESSAGE(stmt, etype, whatstring) EXPECT_THROW( \ try { \ stmt; \ @@ -17,11 +18,23 @@ namespace mdlp { public: precision_t precision = 0.000001; TestFImdlp(): CPPFImdlp() {} + string data_path; void SetUp() { X = { 4.7, 4.7, 4.7, 4.7, 4.8, 4.8, 4.8, 4.8, 4.9, 4.95, 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; y = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; fit(X, y); + data_path = set_data_path(); + } + string set_data_path() + { + string path = "../datasets/"; + ifstream file(path+"iris.arff"); + if (file.is_open()) { + file.close(); + return path; + } + return "../../tests/datasets/"; } void checkSortedVector() { @@ -37,6 +50,7 @@ namespace mdlp { { EXPECT_EQ(computed.size(), expected.size()); for (unsigned long i = 0; i < computed.size(); i++) { + cout << "(" << computed[i] << ", " << expected[i] << ") "; EXPECT_NEAR(computed[i], expected[i], precision); } } @@ -64,7 +78,7 @@ namespace mdlp { void test_dataset(CPPFImdlp& test, string filename, vector& expected, int depths[]) { ArffFiles file; - file.load("../datasets/" + filename + ".arff", true); + file.load(data_path + filename + ".arff", true); vector& X = file.getX(); labels_t& y = file.getY(); auto attributes = file.getAttributes(); @@ -73,10 +87,8 @@ namespace mdlp { EXPECT_EQ(test.get_depth(), depths[feature]); auto computed = test.getCutPoints(); cout << "Feature " << feature << ": "; - for (auto item : computed) - cout << item << " "; - cout << endl; checkCutPoints(computed, expected[feature]); + cout << endl; } } }; diff --git a/tests/datasets/glass.arff b/tests/datasets/glass.arff index abd9e3c..3bcb091 100755 --- a/tests/datasets/glass.arff +++ b/tests/datasets/glass.arff @@ -114,7 +114,7 @@ @attribute 'Ca' real @attribute 'Ba' real @attribute 'Fe' real -@attribute 'Type' { 'build wind float', 'build wind non-float', 'vehic wind float', 'vehic wind non-float', containers, tableware, headlamps} +@attribute 'Type' {'build wind float', 'build wind non-float', 'vehic wind float', 'vehic wind non-float', containers, tableware, headlamps} @data 1.51793,12.79,3.5,1.12,73.03,0.64,8.77,0,0,'build wind float' 1.51643,12.16,3.52,1.35,72.89,0.57,8.53,0,0,'vehic wind float' From cfade7a556b7a5c8b050737e7134c517d9ddca28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 19 Mar 2023 19:13:37 +0100 Subject: [PATCH 25/32] Remove unneeded loop in sortIndices Add some static casts --- CMakeLists.txt | 4 ++ CPPFImdlp.cpp | 74 ++++++++++++++------------ CPPFImdlp.h | 20 +++++-- Metrics.cpp | 46 +++++++++------- Metrics.h | 18 ++++--- sample/sample.cpp | 26 ++++----- tests/ArffFiles.cpp | 124 +++++++++++++++++++++++-------------------- tests/ArffFiles.h | 39 ++++++++++---- tests/CMakeLists.txt | 5 +- 9 files changed, 210 insertions(+), 146 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a711c93..cabfc07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.20) project(mdlp) +if (POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif () + set(CMAKE_CXX_STANDARD 11) add_library(mdlp CPPFImdlp.cpp Metrics.cpp sample/sample.cpp) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 0f73173..50cdc48 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -5,20 +5,22 @@ #include #include "CPPFImdlp.h" #include "Metrics.h" + namespace mdlp { - CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), - max_depth(max_depth_), proposed_cuts(proposed) - { + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed) : min_length(min_length_), + max_depth(max_depth_), + proposed_cuts(proposed) { } + CPPFImdlp::CPPFImdlp() = default; + CPPFImdlp::~CPPFImdlp() = default; - size_t CPPFImdlp::compute_max_num_cut_points() const - { + size_t CPPFImdlp::compute_max_num_cut_points() const { // Set the actual maximum number of cut points as a number or as a percentage of the number of samples if (proposed_cuts == 0) { - return numeric_limits::max(); + return numeric_limits::max(); } if (proposed_cuts < 0 || proposed_cuts > static_cast(X.size())) { throw invalid_argument("wrong proposed num_cuts value"); @@ -28,8 +30,7 @@ namespace mdlp { return static_cast(proposed_cuts); } - void CPPFImdlp::fit(samples_t& X_, labels_t& y_) - { + void CPPFImdlp::fit(samples_t &X_, labels_t &y_) { X = X_; y = y_; num_cut_points = compute_max_num_cut_points(); @@ -52,12 +53,15 @@ namespace mdlp { computeCutPoints(0, X.size(), 1); } - pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) - { - size_t n, m, idxPrev = cut - 1 >= start ? cut - 1 : cut; + pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) { + size_t n; + size_t m; + size_t idxPrev = cut - 1 >= start ? cut - 1 : cut; size_t idxNext = cut + 1 < end ? cut + 1 : cut; bool backWall; // true if duplicates reach begining of the interval - precision_t previous, actual, next; + precision_t previous; + precision_t actual; + precision_t next; previous = X[indices[idxPrev]]; actual = X[indices[cut]]; next = X[indices[idxNext]]; @@ -78,11 +82,10 @@ namespace mdlp { // Decide which values to use cut = cut + (backWall ? m + 1 : -n); actual = X[indices[cut]]; - return { (actual + previous) / 2, cut }; + return {(actual + previous) / 2, cut}; } - void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) - { + void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) { size_t cut; pair result; if (cutPoints.size() == num_cut_points) @@ -103,13 +106,15 @@ namespace mdlp { } } - size_t CPPFImdlp::getCandidate(size_t start, size_t end) - { + size_t CPPFImdlp::getCandidate(size_t start, size_t end) { /* Definition 1: A binary discretization for A is determined by selecting the cut point TA for which E(A, TA; S) is minimal amongst all the candidate cut points. */ - size_t candidate = numeric_limits::max(), elements = end - start; + size_t candidate = numeric_limits::max(); + size_t elements = end - start; bool sameValues = true; - precision_t entropy_left, entropy_right, minEntropy; + precision_t entropy_left; + precision_t entropy_right; + precision_t minEntropy; // Check if all the values of the variable in the interval are the same for (size_t idx = start + 1; idx < end; idx++) { if (X[indices[idx]] != X[indices[start]]) { @@ -134,11 +139,15 @@ namespace mdlp { return candidate; } - bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) - { - int k, k1, k2; - precision_t ig, delta; - precision_t ent, ent1, ent2; + bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) { + int k; + int k1; + int k2; + precision_t ig; + precision_t delta; + precision_t ent; + precision_t ent1; + precision_t ent2; auto N = precision_t(end - start); k = metrics.computeNumClasses(start, end); k1 = metrics.computeNumClasses(start, cut); @@ -148,33 +157,30 @@ namespace mdlp { ent2 = metrics.entropy(cut, end); ig = metrics.informationGain(start, cut, end); delta = static_cast(log2(pow(3, precision_t(k)) - 2) - - (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); + (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); precision_t term = 1 / N * (log2(N - 1) + delta); return ig > term; } // Argsort from https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes - indices_t CPPFImdlp::sortIndices(samples_t& X_, labels_t& y_) - { + indices_t CPPFImdlp::sortIndices(samples_t &X_, labels_t &y_) { indices_t idx(X_.size()); iota(idx.begin(), idx.end(), 0); - for (size_t i = 0; i < X_.size(); i++) - stable_sort(idx.begin(), idx.end(), [&X_, &y_](size_t i1, size_t i2) { + stable_sort(idx.begin(), idx.end(), [&X_, &y_](size_t i1, size_t i2) { if (X_[i1] == X_[i2]) return y_[i1] < y_[i2]; else return X_[i1] < X_[i2]; - }); + }); return idx; } - cutPoints_t CPPFImdlp::getCutPoints() - { + cutPoints_t CPPFImdlp::getCutPoints() { sort(cutPoints.begin(), cutPoints.end()); return cutPoints; } - int CPPFImdlp::get_depth() const - { + + int CPPFImdlp::get_depth() const { return depth; } } diff --git a/CPPFImdlp.h b/CPPFImdlp.h index 574d8cb..0c0311e 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -1,9 +1,11 @@ #ifndef CPPFIMDLP_H #define CPPFIMDLP_H + #include "typesFImdlp.h" #include "Metrics.h" #include #include + namespace mdlp { class CPPFImdlp { protected: @@ -18,20 +20,32 @@ namespace mdlp { cutPoints_t cutPoints; size_t num_cut_points = numeric_limits::max(); - static indices_t sortIndices(samples_t&, labels_t&); + static indices_t sortIndices(samples_t &, labels_t &); + void computeCutPoints(size_t, size_t, int); + bool mdlp(size_t, size_t, size_t); + size_t getCandidate(size_t, size_t); + size_t compute_max_num_cut_points() const; + pair valueCutPoint(size_t, size_t, size_t); + public: CPPFImdlp(); + CPPFImdlp(size_t, int, float); + ~CPPFImdlp(); - void fit(samples_t&, labels_t&); + + void fit(samples_t &, labels_t &); + cutPoints_t getCutPoints(); + int get_depth() const; - inline string version() { return "1.1.1"; }; + + inline string version() const { return "1.1.1"; }; }; } #endif diff --git a/Metrics.cpp b/Metrics.cpp index 766e508..c77da01 100644 --- a/Metrics.cpp +++ b/Metrics.cpp @@ -1,63 +1,71 @@ #include "Metrics.h" #include #include + using namespace std; namespace mdlp { - Metrics::Metrics(labels_t& y_, indices_t& indices_): y(y_), indices(indices_), numClasses(computeNumClasses(0, indices.size())), entropyCache(cacheEnt_t()), igCache(cacheIg_t()) - { + Metrics::Metrics(labels_t &y_, indices_t &indices_) : y(y_), indices(indices_), + numClasses(computeNumClasses(0, indices.size())) { } - int Metrics::computeNumClasses(size_t start, size_t end) - { + + int Metrics::computeNumClasses(size_t start, size_t end) { set nClasses; for (auto i = start; i < end; ++i) { nClasses.insert(y[indices[i]]); } - return nClasses.size(); + return static_cast(nClasses.size()); } - void Metrics::setData(labels_t& y_, indices_t& indices_) - { + + void Metrics::setData(const labels_t &y_, const indices_t &indices_) { indices = indices_; y = y_; numClasses = computeNumClasses(0, indices.size()); entropyCache.clear(); igCache.clear(); } - precision_t Metrics::entropy(size_t start, size_t end) - { - precision_t p, ventropy = 0; + + precision_t Metrics::entropy(size_t start, size_t end) { + precision_t p; + precision_t ventropy = 0; int nElements = 0; labels_t counts(numClasses + 1, 0); if (end - start < 2) return 0; - if (entropyCache.find({ start, end }) != entropyCache.end()) { + if (entropyCache.find({start, end}) != entropyCache.end()) { return entropyCache[{start, end}]; } for (auto i = &indices[start]; i != &indices[end]; ++i) { counts[y[*i]]++; nElements++; } - for (auto count : counts) { + for (auto count: counts) { if (count > 0) { - p = (precision_t)count / nElements; + p = static_cast(count) / static_cast(nElements); ventropy -= p * log2(p); } } entropyCache[{start, end}] = ventropy; return ventropy; } - precision_t Metrics::informationGain(size_t start, size_t cut, size_t end) - { + + precision_t Metrics::informationGain(size_t start, size_t cut, size_t end) { precision_t iGain; - precision_t entropyInterval, entropyLeft, entropyRight; - int nElementsLeft = cut - start, nElementsRight = end - cut; - int nElements = end - start; + precision_t entropyInterval; + precision_t entropyLeft; + precision_t entropyRight; + size_t nElementsLeft = cut - start; + size_t nElementsRight = end - cut; + size_t nElements = end - start; if (igCache.find(make_tuple(start, cut, end)) != igCache.end()) { return igCache[make_tuple(start, cut, end)]; } entropyInterval = entropy(start, end); entropyLeft = entropy(start, cut); entropyRight = entropy(cut, end); - iGain = entropyInterval - ((precision_t)nElementsLeft * entropyLeft + (precision_t)nElementsRight * entropyRight) / nElements; + iGain = entropyInterval - + (static_cast(nElementsLeft) * entropyLeft + + static_cast(nElementsRight) * entropyRight) / + static_cast(nElements); igCache[make_tuple(start, cut, end)] = iGain; return iGain; } diff --git a/Metrics.h b/Metrics.h index a371502..4046a6d 100644 --- a/Metrics.h +++ b/Metrics.h @@ -1,19 +1,25 @@ #ifndef CCMETRICS_H #define CCMETRICS_H + #include "typesFImdlp.h" + namespace mdlp { class Metrics { protected: - labels_t& y; - indices_t& indices; + labels_t &y; + indices_t &indices; int numClasses; - cacheEnt_t entropyCache; - cacheIg_t igCache; + cacheEnt_t entropyCache = cacheEnt_t(); + cacheIg_t igCache = cacheIg_t(); public: - Metrics(labels_t&, indices_t&); - void setData(labels_t&, indices_t&); + Metrics(labels_t &, indices_t &); + + void setData(const labels_t &, const indices_t &); + int computeNumClasses(size_t, size_t); + precision_t entropy(size_t, size_t); + precision_t informationGain(size_t, size_t, size_t); }; } diff --git a/sample/sample.cpp b/sample/sample.cpp index cd2bbde..49d5d91 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -38,17 +38,17 @@ tuple parse_arguments(int argc, char **argv) { int max_depth = numeric_limits::max(); int min_length = 3; float max_cutpoints = 0; - static struct option long_options[] = { + const option long_options[] = { {"help", no_argument, nullptr, 'h'}, {"file", required_argument, nullptr, 'f'}, {"path", required_argument, nullptr, 'p'}, {"max_depth", required_argument, nullptr, 'm'}, {"max_cutpoints", required_argument, nullptr, 'c'}, {"min_length", required_argument, nullptr, 'n'}, - {nullptr, 0, nullptr, 0} + {nullptr, no_argument, nullptr, 0} }; while (true) { - auto c = getopt_long(argc, argv, "hf:p:m:c:n:", long_options, nullptr); + const auto c = getopt_long(argc, argv, "hf:p:m:c:n:", long_options, nullptr); if (c == -1) break; switch (c) { @@ -56,16 +56,16 @@ tuple parse_arguments(int argc, char **argv) { usage(argv[0]); exit(0); case 'f': - file_name = optarg; + file_name = string(optarg); break; case 'm': - max_depth = (int) strtol(optarg, nullptr, 10); + max_depth = stoi(optarg); break; case 'n': - min_length = (int) strtol(optarg, nullptr, 10); + min_length = stoi(optarg); break; case 'c': - max_cutpoints = strtof(optarg, nullptr); + max_cutpoints = stof(optarg); break; case 'p': path = optarg; @@ -92,7 +92,7 @@ void process_file(const string &path, const string &file_name, bool class_last, file.load(path + file_name + ".arff", class_last); auto attributes = file.getAttributes(); - int items = file.getSize(); + auto items = file.getSize(); cout << "Number of lines: " << items << endl; cout << "Attributes: " << endl; for (auto attribute: attributes) { @@ -109,7 +109,7 @@ void process_file(const string &path, const string &file_name, bool class_last, } cout << y[i] << endl; } - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); + auto test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); auto total = 0; for (auto i = 0; i < attributes.size(); i++) { auto min_max = minmax_element(X[i].begin(), X[i].end()); @@ -141,7 +141,7 @@ void process_all_files(const map &datasets, const string &path, in size_t timing = 0; int cut_points = 0; for (auto i = 0; i < attributes.size(); i++) { - mdlp::CPPFImdlp test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); + auto test = mdlp::CPPFImdlp(min_length, max_depth, max_cutpoints); std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); test.fit(X[i], y); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); @@ -163,8 +163,10 @@ int main(int argc, char **argv) { {"mfeat-factors", true}, {"test", true} }; - string file_name, path; - int max_depth, min_length; + string file_name; + string path; + int max_depth; + int min_length; float max_cutpoints; tie(file_name, path, max_depth, min_length, max_cutpoints) = parse_arguments(argc, argv); if (datasets.find(file_name) == datasets.end() && file_name != "all") { diff --git a/tests/ArffFiles.cpp b/tests/ArffFiles.cpp index a7286eb..405a57e 100644 --- a/tests/ArffFiles.cpp +++ b/tests/ArffFiles.cpp @@ -6,82 +6,88 @@ using namespace std; ArffFiles::ArffFiles() = default; -vector ArffFiles::getLines() -{ + +vector ArffFiles::getLines() const { return lines; } -unsigned long int ArffFiles::getSize() -{ + +unsigned long int ArffFiles::getSize() const { return lines.size(); } -vector> ArffFiles::getAttributes() -{ + +vector> ArffFiles::getAttributes() const { return attributes; } -string ArffFiles::getClassName() -{ + +string ArffFiles::getClassName() const { return className; } -string ArffFiles::getClassType() -{ + +string ArffFiles::getClassType() const { return classType; } -vector>& ArffFiles::getX() -{ + +vector> &ArffFiles::getX() { return X; } -vector& ArffFiles::getY() -{ + +vector &ArffFiles::getY() { return y; } -void ArffFiles::load(const string fileName, bool classLast) -{ + +void ArffFiles::load(const string &fileName, bool classLast) { ifstream file(fileName); - if (file.is_open()) { - string line, keyword, attribute, type, type_w; - while (getline(file, line)) { - if (line.empty() || line[0] == '%' || line == "\r" || line == " ") { - continue; - } - if (line.find("@attribute") != string::npos || line.find("@ATTRIBUTE") != string::npos) { - stringstream ss(line); - ss >> keyword >> attribute; - type = ""; - while(ss >> type_w) - type += type_w + " "; - attributes.emplace_back(attribute, type ); - continue; - } - if (line[0] == '@') { - continue; - } - lines.push_back(line); - } - file.close(); - if (attributes.empty()) - throw invalid_argument("No attributes found"); - if (classLast) { - className = get<0>(attributes.back()); - classType = get<1>(attributes.back()); - attributes.pop_back(); - } else { - className = get<0>(attributes.front()); - classType = get<1>(attributes.front()); - attributes.erase(attributes.begin()); - } - generateDataset(classLast); - } else + if (!file.is_open()) { throw invalid_argument("Unable to open file"); + } + string line; + string keyword; + string attribute; + string type; + string type_w; + while (getline(file, line)) { + if (line.empty() || line[0] == '%' || line == "\r" || line == " ") { + continue; + } + if (line.find("@attribute") != string::npos || line.find("@ATTRIBUTE") != string::npos) { + stringstream ss(line); + ss >> keyword >> attribute; + type = ""; + while (ss >> type_w) + type += type_w + " "; + attributes.emplace_back(attribute, type); + continue; + } + if (line[0] == '@') { + continue; + } + lines.push_back(line); + } + file.close(); + if (attributes.empty()) + throw invalid_argument("No attributes found"); + if (classLast) { + className = get<0>(attributes.back()); + classType = get<1>(attributes.back()); + attributes.pop_back(); + } else { + className = get<0>(attributes.front()); + classType = get<1>(attributes.front()); + attributes.erase(attributes.begin()); + } + generateDataset(classLast); + } -void ArffFiles::generateDataset(bool classLast) -{ + +void ArffFiles::generateDataset(bool classLast) { X = vector>(attributes.size(), vector(lines.size())); - vector yy = vector(lines.size(), ""); + auto yy = vector(lines.size(), ""); int labelIndex = classLast ? static_cast(attributes.size()) : 0; for (size_t i = 0; i < lines.size(); i++) { stringstream ss(lines[i]); string value; - int pos = 0, xIndex = 0; + int pos = 0; + int xIndex = 0; while (getline(ss, value, ',')) { if (pos++ == labelIndex) { yy[i] = value; @@ -92,20 +98,20 @@ void ArffFiles::generateDataset(bool classLast) } y = factorize(yy); } -string ArffFiles::trim(const string& source) -{ + +string ArffFiles::trim(const string &source) { string s(source); s.erase(0, s.find_first_not_of(" \n\r\t")); s.erase(s.find_last_not_of(" \n\r\t") + 1); return s; } -vector ArffFiles::factorize(const vector& labels_t) -{ + +vector ArffFiles::factorize(const vector &labels_t) { vector yy; yy.reserve(labels_t.size()); map labelMap; int i = 0; - for (const string &label : labels_t) { + for (const string &label: labels_t) { if (labelMap.find(label) == labelMap.end()) { labelMap[label] = i++; } diff --git a/tests/ArffFiles.h b/tests/ArffFiles.h index 8590d04..38531af 100644 --- a/tests/ArffFiles.h +++ b/tests/ArffFiles.h @@ -1,27 +1,44 @@ #ifndef ARFFFILES_H #define ARFFFILES_H + #include #include + using namespace std; + class ArffFiles { private: vector lines; vector> attributes; - string className, classType; + string className; + string classType; vector> X; vector y; + void generateDataset(bool); + public: ArffFiles(); - void load(string, bool = true); - vector getLines(); - unsigned long int getSize(); - string getClassName(); - string getClassType(); - static string trim(const string&); - vector>& getX(); - vector& getY(); - vector> getAttributes(); - static vector factorize(const vector& labels_t); + + void load(const string &, bool = true); + + vector getLines() const; + + unsigned long int getSize() const; + + string getClassName() const; + + string getClassType() const; + + static string trim(const string &); + + vector> &getX(); + + vector &getY(); + + vector> getAttributes() const; + + static vector factorize(const vector &labels_t); }; + #endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 67cc084..d1c1e77 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,9 +4,10 @@ include(FetchContent) include_directories(${GTEST_INCLUDE_DIRS}) + FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) From 12222f790352189ef3a31908015ed47556e7ed28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Mon, 20 Mar 2023 20:24:32 +0100 Subject: [PATCH 26/32] Remove trailing space in attribute type of Arff --- tests/ArffFiles.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tests/ArffFiles.cpp b/tests/ArffFiles.cpp index 405a57e..b576699 100644 --- a/tests/ArffFiles.cpp +++ b/tests/ArffFiles.cpp @@ -7,35 +7,43 @@ using namespace std; ArffFiles::ArffFiles() = default; -vector ArffFiles::getLines() const { +vector ArffFiles::getLines() const +{ return lines; } -unsigned long int ArffFiles::getSize() const { +unsigned long int ArffFiles::getSize() const +{ return lines.size(); } -vector> ArffFiles::getAttributes() const { +vector> ArffFiles::getAttributes() const +{ return attributes; } -string ArffFiles::getClassName() const { +string ArffFiles::getClassName() const +{ return className; } -string ArffFiles::getClassType() const { +string ArffFiles::getClassType() const +{ return classType; } -vector> &ArffFiles::getX() { +vector>& ArffFiles::getX() +{ return X; } -vector &ArffFiles::getY() { +vector& ArffFiles::getY() +{ return y; } -void ArffFiles::load(const string &fileName, bool classLast) { +void ArffFiles::load(const string& fileName, bool classLast) +{ ifstream file(fileName); if (!file.is_open()) { throw invalid_argument("Unable to open file"); @@ -55,7 +63,7 @@ void ArffFiles::load(const string &fileName, bool classLast) { type = ""; while (ss >> type_w) type += type_w + " "; - attributes.emplace_back(attribute, type); + attributes.emplace_back(attribute, trim(type)); continue; } if (line[0] == '@') { @@ -79,7 +87,8 @@ void ArffFiles::load(const string &fileName, bool classLast) { } -void ArffFiles::generateDataset(bool classLast) { +void ArffFiles::generateDataset(bool classLast) +{ X = vector>(attributes.size(), vector(lines.size())); auto yy = vector(lines.size(), ""); int labelIndex = classLast ? static_cast(attributes.size()) : 0; @@ -99,19 +108,21 @@ void ArffFiles::generateDataset(bool classLast) { y = factorize(yy); } -string ArffFiles::trim(const string &source) { +string ArffFiles::trim(const string& source) +{ string s(source); s.erase(0, s.find_first_not_of(" \n\r\t")); s.erase(s.find_last_not_of(" \n\r\t") + 1); return s; } -vector ArffFiles::factorize(const vector &labels_t) { +vector ArffFiles::factorize(const vector& labels_t) +{ vector yy; yy.reserve(labels_t.size()); map labelMap; int i = 0; - for (const string &label: labels_t) { + for (const string& label : labels_t) { if (labelMap.find(label) == labelMap.end()) { labelMap[label] = i++; } From 27ea3bf33854428a3a94a32f624f8b7bc28be4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 21 Mar 2023 00:53:18 +0100 Subject: [PATCH 27/32] Refactor tests --- CPPFImdlp.h | 2 +- tests/FImdlp_unittest.cpp | 287 +++++++++++++++++++------------------ tests/Metrics_unittest.cpp | 23 +-- tests/test | 3 + 4 files changed, 160 insertions(+), 155 deletions(-) diff --git a/CPPFImdlp.h b/CPPFImdlp.h index 0c0311e..6a24ca3 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -45,7 +45,7 @@ namespace mdlp { int get_depth() const; - inline string version() const { return "1.1.1"; }; + static inline string version() { return "1.1.1"; }; }; } #endif diff --git a/tests/FImdlp_unittest.cpp b/tests/FImdlp_unittest.cpp index 56e6c1e..3b090bb 100644 --- a/tests/FImdlp_unittest.cpp +++ b/tests/FImdlp_unittest.cpp @@ -4,6 +4,7 @@ #include #include #include "ArffFiles.h" + #define EXPECT_THROW_WITH_MESSAGE(stmt, etype, whatstring) EXPECT_THROW( \ try { \ stmt; \ @@ -14,30 +15,33 @@ throw; \ , etype) namespace mdlp { - class TestFImdlp: public CPPFImdlp, public testing::Test { + class TestFImdlp : public CPPFImdlp, public testing::Test { public: - precision_t precision = 0.000001; - TestFImdlp(): CPPFImdlp() {} + precision_t precision = 0.000001f; + + TestFImdlp() : CPPFImdlp() {} + string data_path; - void SetUp() - { - X = { 4.7, 4.7, 4.7, 4.7, 4.8, 4.8, 4.8, 4.8, 4.9, 4.95, 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; - y = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + + void SetUp() override { + X = {4.7f, 4.7f, 4.7f, 4.7f, 4.8f, 4.8f, 4.8f, 4.8f, 4.9f, 4.95f, 5.7f, 5.3f, 5.2f, 5.1f, 5.0f, 5.6f, 5.1f, + 6.0f, 5.1f, 5.9f}; + y = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2}; fit(X, y); data_path = set_data_path(); } - string set_data_path() - { + + static string set_data_path() { string path = "../datasets/"; - ifstream file(path+"iris.arff"); + ifstream file(path + "iris.arff"); if (file.is_open()) { file.close(); return path; } return "../../tests/datasets/"; } - void checkSortedVector() - { + + void checkSortedVector() { indices_t testSortedIndices = sortIndices(X, y); precision_t prev = X[testSortedIndices[0]]; for (unsigned long i = 0; i < X.size(); ++i) { @@ -46,26 +50,18 @@ namespace mdlp { prev = X[testSortedIndices[i]]; } } - void checkCutPoints(cutPoints_t& computed, cutPoints_t& expected) - { + + void checkCutPoints(cutPoints_t &computed, cutPoints_t &expected) const { EXPECT_EQ(computed.size(), expected.size()); for (unsigned long i = 0; i < computed.size(); i++) { cout << "(" << computed[i] << ", " << expected[i] << ") "; EXPECT_NEAR(computed[i], expected[i], precision); } } - template - void checkVectors(std::vector const& expected, std::vector const& computed) - { - ASSERT_EQ(expected.size(), computed.size()); - for (auto i = 0; i < expected.size(); i++) { - EXPECT_NEAR(expected[i], computed[i], precision); - } - } - bool test_result(samples_t& X_, size_t cut, float midPoint, size_t limit, string title) - { + + bool test_result(const samples_t &X_, size_t cut, float midPoint, size_t limit, const string &title) { pair result; - labels_t y_ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + labels_t y_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; X = X_; y = y_; indices = sortIndices(X, y); @@ -75,12 +71,13 @@ namespace mdlp { EXPECT_EQ(result.second, limit); return true; } - void test_dataset(CPPFImdlp& test, string filename, vector& expected, int depths[]) - { + + void test_dataset(CPPFImdlp &test, const string &filename, vector &expected, + vector &depths) const { ArffFiles file; file.load(data_path + filename + ".arff", true); - vector& X = file.getX(); - labels_t& y = file.getY(); + vector &X = file.getX(); + labels_t &y = file.getY(); auto attributes = file.getAttributes(); for (auto feature = 0; feature < attributes.size(); feature++) { test.fit(X[feature], y); @@ -92,202 +89,206 @@ namespace mdlp { } } }; - TEST_F(TestFImdlp, FitErrorEmptyDataset) - { + + TEST_F(TestFImdlp, FitErrorEmptyDataset) { X = samples_t(); y = labels_t(); EXPECT_THROW_WITH_MESSAGE(fit(X, y), invalid_argument, "X and y must have at least one element"); } - TEST_F(TestFImdlp, FitErrorDifferentSize) - { - X = { 1, 2, 3 }; - y = { 1, 2 }; + + TEST_F(TestFImdlp, FitErrorDifferentSize) { + X = {1, 2, 3}; + y = {1, 2}; EXPECT_THROW_WITH_MESSAGE(fit(X, y), invalid_argument, "X and y must have the same size"); } - TEST_F(TestFImdlp, FitErrorMinLengtMaxDepth) - { + + TEST_F(TestFImdlp, FitErrorMinLengtMaxDepth) { auto testLength = CPPFImdlp(2, 10, 0); auto testDepth = CPPFImdlp(3, 0, 0); - X = { 1, 2, 3 }; - y = { 1, 2, 3 }; + X = {1, 2, 3}; + y = {1, 2, 3}; EXPECT_THROW_WITH_MESSAGE(testLength.fit(X, y), invalid_argument, "min_length must be greater than 2"); EXPECT_THROW_WITH_MESSAGE(testDepth.fit(X, y), invalid_argument, "max_depth must be greater than 0"); } - TEST_F(TestFImdlp, FitErrorMaxCutPoints) - { + + TEST_F(TestFImdlp, FitErrorMaxCutPoints) { auto testmin = CPPFImdlp(2, 10, -1); auto testmax = CPPFImdlp(3, 0, 200); - X = { 1, 2, 3 }; - y = { 1, 2, 3 }; + X = {1, 2, 3}; + y = {1, 2, 3}; EXPECT_THROW_WITH_MESSAGE(testmin.fit(X, y), invalid_argument, "wrong proposed num_cuts value"); EXPECT_THROW_WITH_MESSAGE(testmax.fit(X, y), invalid_argument, "wrong proposed num_cuts value"); } - TEST_F(TestFImdlp, SortIndices) - { - X = { 5.7, 5.3, 5.2, 5.1, 5.0, 5.6, 5.1, 6.0, 5.1, 5.9 }; - y = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; - indices = { 4, 3, 6, 8, 2, 1, 5, 0, 9, 7 }; + + TEST_F(TestFImdlp, SortIndices) { + X = {5.7f, 5.3f, 5.2f, 5.1f, 5.0f, 5.6f, 5.1f, 6.0f, 5.1f, 5.9f}; + y = {1, 1, 1, 1, 1, 2, 2, 2, 2, 2}; + indices = {4, 3, 6, 8, 2, 1, 5, 0, 9, 7}; checkSortedVector(); - X = { 5.77, 5.88, 5.99 }; - y = { 1, 2, 1 }; - indices = { 0, 1, 2 }; + X = {5.77f, 5.88f, 5.99f}; + y = {1, 2, 1}; + indices = {0, 1, 2}; checkSortedVector(); - X = { 5.33, 5.22, 5.11 }; - y = { 1, 2, 1 }; - indices = { 2, 1, 0 }; + X = {5.33f, 5.22f, 5.11f}; + y = {1, 2, 1}; + indices = {2, 1, 0}; checkSortedVector(); - X = { 5.33, 5.22, 5.33 }; - y = { 2, 2, 1 }; - indices = { 1, 2, 0 }; + X = {5.33f, 5.22f, 5.33f}; + y = {2, 2, 1}; + indices = {1, 2, 0}; } - TEST_F(TestFImdlp, TestShortDatasets) - { + + TEST_F(TestFImdlp, TestShortDatasets) { vector computed; - X = { 1 }; - y = { 1 }; + X = {1}; + y = {1}; fit(X, y); computed = getCutPoints(); EXPECT_EQ(computed.size(), 0); - X = { 1, 3 }; - y = { 1, 2 }; + X = {1, 3}; + y = {1, 2}; fit(X, y); computed = getCutPoints(); EXPECT_EQ(computed.size(), 0); - X = { 2, 4 }; - y = { 1, 2 }; + X = {2, 4}; + y = {1, 2}; fit(X, y); computed = getCutPoints(); EXPECT_EQ(computed.size(), 0); - X = { 1, 2, 3 }; - y = { 1, 2, 2 }; + X = {1, 2, 3}; + y = {1, 2, 2}; fit(X, y); computed = getCutPoints(); EXPECT_EQ(computed.size(), 1); EXPECT_NEAR(computed[0], 1.5, precision); } - TEST_F(TestFImdlp, TestArtificialDataset) - { + + TEST_F(TestFImdlp, TestArtificialDataset) { fit(X, y); - cutPoints_t expected = { 5.05 }; + cutPoints_t expected = {5.05f}; vector computed = getCutPoints(); - int expectedSize = expected.size(); EXPECT_EQ(computed.size(), expected.size()); for (unsigned long i = 0; i < computed.size(); i++) { EXPECT_NEAR(computed[i], expected[i], precision); } } - TEST_F(TestFImdlp, TestIris) - { + + TEST_F(TestFImdlp, TestIris) { vector expected = { - { 5.45, 5.75 }, - { 2.75, 2.85, 2.95, 3.05, 3.35 }, - { 2.45, 4.75, 5.05 }, - { 0.8, 1.75 } + {5.45f, 5.75f}, + {2.75f, 2.85f, 2.95f, 3.05f, 3.35f}, + {2.45f, 4.75f, 5.05f}, + {0.8f, 1.75f} }; - int depths[] = { 3, 5, 5, 5 }; + vector depths = {3, 5, 4, 3}; auto test = CPPFImdlp(); - //test_dataset(test, "iris.arff", expected, depths); + test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, ComputeCutPointsGCase) - { + + TEST_F(TestFImdlp, ComputeCutPointsGCase) { cutPoints_t expected; - expected = { 1.5 }; - samples_t X_ = { 0, 1, 2, 2, 2 }; - labels_t y_ = { 1, 1, 1, 2, 2 }; + expected = {1.5}; + samples_t X_ = {0, 1, 2, 2, 2}; + labels_t y_ = {1, 1, 1, 2, 2}; fit(X_, y_); auto computed = getCutPoints(); checkCutPoints(computed, expected); } - TEST_F(TestFImdlp, ValueCutPoint) - { + + TEST_F(TestFImdlp, ValueCutPoint) { // Case titles as stated in the doc - samples_t X1a{ 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0 }; - test_result(X1a, 6, 7.3 / 2, 6, "1a"); - samples_t X2a = { 3.1, 3.2, 3.3, 3.4, 3.7, 3.7, 3.7, 3.8, 3.9, 4.0 }; - test_result(X2a, 6, 7.1 / 2, 4, "2a"); - samples_t X2b = { 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.8, 3.9, 4.0 }; - test_result(X2b, 6, 7.5 / 2, 7, "2b"); - samples_t X3a = { 3.1, 3.2, 3.3, 3.4, 3.7, 3.7, 3.7, 3.8, 3.9, 4.0 }; - test_result(X3a, 4, 7.1 / 2, 4, "3a"); - samples_t X3b = { 3.1, 3.2, 3.3, 3.4, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7 }; - test_result(X3b, 4, 7.1 / 2, 4, "3b"); - samples_t X4a = { 3.1, 3.2, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.9, 4.0 }; - test_result(X4a, 4, 6.9 / 2, 2, "4a"); - samples_t X4b = { 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.8, 3.9, 4.0 }; - test_result(X4b, 4, 7.5 / 2, 7, "4b"); - samples_t X4c = { 3.1, 3.2, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7, 3.7 }; - test_result(X4c, 4, 6.9 / 2, 2, "4c"); + samples_t X1a{3.1f, 3.2f, 3.3f, 3.4f, 3.5f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f}; + test_result(X1a, 6, 7.3f / 2, 6, "1a"); + samples_t X2a = {3.1f, 3.2f, 3.3f, 3.4f, 3.7f, 3.7f, 3.7f, 3.8f, 3.9f, 4.0f}; + test_result(X2a, 6, 7.1f / 2, 4, "2a"); + samples_t X2b = {3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.8f, 3.9f, 4.0f}; + test_result(X2b, 6, 7.5f / 2, 7, "2b"); + samples_t X3a = {3.f, 3.2f, 3.3f, 3.4f, 3.7f, 3.7f, 3.7f, 3.8f, 3.9f, 4.0f}; + test_result(X3a, 4, 7.1f / 2, 4, "3a"); + samples_t X3b = {3.1f, 3.2f, 3.3f, 3.4f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f}; + test_result(X3b, 4, 7.1f / 2, 4, "3b"); + samples_t X4a = {3.1f, 3.2f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.9f, 4.0f}; + test_result(X4a, 4, 6.9f / 2, 2, "4a"); + samples_t X4b = {3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.8f, 3.9f, 4.0f}; + test_result(X4b, 4, 7.5f / 2, 7, "4b"); + samples_t X4c = {3.1f, 3.2f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f, 3.7f}; + test_result(X4c, 4, 6.9f / 2, 2, "4c"); } - TEST_F(TestFImdlp, MaxDepth) - { + + TEST_F(TestFImdlp, MaxDepth) { // Set max_depth to 1 auto test = CPPFImdlp(3, 1, 0); vector expected = { - { 5.45 }, - { 3.35 }, - { 2.45 }, - {0.8 } + {5.45f}, + {3.35f}, + {2.45f}, + {0.8f} }; - int depths[] = { 1, 1, 1, 1 }; + vector depths = {1, 1, 1, 1}; test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, MinLength) - { + + TEST_F(TestFImdlp, MinLength) { auto test = CPPFImdlp(75, 100, 0); // Set min_length to 75 vector expected = { - { 5.45, 5.75 }, - { 2.85, 3.35 }, - { 2.45, 4.75 }, - { 0.8, 1.75 } + {5.45f, 5.75f}, + {2.85f, 3.35f}, + {2.45f, 4.75f}, + {0.8f, 1.75f} }; - int depths[] = { 3, 2, 2, 2 }; + vector depths = {3, 2, 2, 2}; test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, MinLengthMaxDepth) - { + + TEST_F(TestFImdlp, MinLengthMaxDepth) { // Set min_length to 75 auto test = CPPFImdlp(75, 2, 0); vector expected = { - { 5.45, 5.75 }, - { 2.85, 3.35 }, - { 2.45, 4.75 }, - { 0.8, 1.75 } + {5.45f, 5.75f}, + {2.85f, 3.35f}, + {2.45f, 4.75f}, + {0.8f, 1.75f} }; - int depths[] = { 2, 2, 2, 2 }; + vector depths = {2, 2, 2, 2}; test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, MaxCutPointsInteger) - { + + TEST_F(TestFImdlp, MaxCutPointsInteger) { // Set min_length to 75 auto test = CPPFImdlp(75, 2, 1); vector expected = { - { 5.45 }, - { 3.35 }, - { 2.45 }, - { 0.8} + {5.45f}, + {3.35f}, + {2.45f}, + {0.8f} }; - int depths[] = { 1, 1, 1, 1 }; + vector depths = {1, 1, 1, 1}; test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, MaxCutPointsFloat) - { + + TEST_F(TestFImdlp, MaxCutPointsFloat) { // Set min_length to 75 - auto test = CPPFImdlp(75, 2, 0.2); + auto test = CPPFImdlp(75, 2, 0.2f); vector expected = { - { 5.45, 5.75 }, - { 2.85, 3.35 }, - { 2.45, 4.75 }, - { 0.8, 1.75 } + {5.45f, 5.75f}, + {2.85f, 3.35f}, + {2.45f, 4.75f}, + {0.8f, 1.75f} }; - int depths[] = { 2, 2, 2, 2 }; + vector depths = {2, 2, 2, 2}; test_dataset(test, "iris", expected, depths); } - TEST_F(TestFImdlp, ProposedCuts) - { - vector> proposed_list = { { 0.1, 2}, { 0.5, 10}, {0.07, 1}, {1, 1}, {2, 2} }; - size_t expected, computed; - for (auto proposed_item : proposed_list) { + + TEST_F(TestFImdlp, ProposedCuts) { + vector> proposed_list = {{0.1f, 2}, + {0.5f, 10}, + {0.07f, 1}, + {1.0f, 1}, + {2.0f, 2}}; + size_t expected; + size_t computed; + for (auto proposed_item: proposed_list) { tie(proposed_cuts, expected) = proposed_item; computed = compute_max_num_cut_points(); ASSERT_EQ(expected, computed); diff --git a/tests/Metrics_unittest.cpp b/tests/Metrics_unittest.cpp index e7d6f8b..5cd73d6 100644 --- a/tests/Metrics_unittest.cpp +++ b/tests/Metrics_unittest.cpp @@ -5,19 +5,18 @@ namespace mdlp { class TestMetrics: public Metrics, public testing::Test { public: - labels_t y; - samples_t X; - indices_t indices; - precision_t precision = 0.000001; + labels_t y_ = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + indices_t indices_ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + precision_t precision = 0.000001f; - TestMetrics(): Metrics(y, indices) {} - void SetUp() + TestMetrics(): Metrics(y_, indices_) {}; + + void SetUp() override { - y = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; - indices = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - setData(y, indices); + setData(y_, indices_); } }; + TEST_F(TestMetrics, NumClasses) { y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; @@ -25,20 +24,22 @@ namespace mdlp { EXPECT_EQ(2, computeNumClasses(0, 10)); EXPECT_EQ(2, computeNumClasses(8, 10)); } + TEST_F(TestMetrics, Entropy) { EXPECT_EQ(1, entropy(0, 10)); EXPECT_EQ(0, entropy(0, 5)); y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; setData(y, indices); - ASSERT_NEAR(0.468996, entropy(0, 10), precision); + ASSERT_NEAR(0.468996f, entropy(0, 10), precision); } + TEST_F(TestMetrics, InformationGain) { ASSERT_NEAR(1, informationGain(0, 5, 10), precision); ASSERT_NEAR(1, informationGain(0, 5, 10), precision); // For cache y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; setData(y, indices); - ASSERT_NEAR(0.108032, informationGain(0, 5, 10), precision); + ASSERT_NEAR(0.108032f, informationGain(0, 5, 10), precision); } } diff --git a/tests/test b/tests/test index 4180150..33fde47 100755 --- a/tests/test +++ b/tests/test @@ -1,6 +1,9 @@ if [ -d build ] ; then rm -fr build fi +if [ -d gcovr-report ] ; then + rm -fr gcovr-report +fi cmake -S . -B build -Wno-dev cmake --build build cd build From 77135739cf72cfc02603332c681baae4dcea28f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Tue, 21 Mar 2023 09:55:40 +0100 Subject: [PATCH 28/32] Reformat some test files --- tests/ArffFiles.cpp | 35 ++++++++++++----------------------- tests/Metrics_unittest.cpp | 27 +++++++++++---------------- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/tests/ArffFiles.cpp b/tests/ArffFiles.cpp index b576699..0d88818 100644 --- a/tests/ArffFiles.cpp +++ b/tests/ArffFiles.cpp @@ -7,43 +7,35 @@ using namespace std; ArffFiles::ArffFiles() = default; -vector ArffFiles::getLines() const -{ +vector ArffFiles::getLines() const { return lines; } -unsigned long int ArffFiles::getSize() const -{ +unsigned long int ArffFiles::getSize() const { return lines.size(); } -vector> ArffFiles::getAttributes() const -{ +vector> ArffFiles::getAttributes() const { return attributes; } -string ArffFiles::getClassName() const -{ +string ArffFiles::getClassName() const { return className; } -string ArffFiles::getClassType() const -{ +string ArffFiles::getClassType() const { return classType; } -vector>& ArffFiles::getX() -{ +vector> &ArffFiles::getX() { return X; } -vector& ArffFiles::getY() -{ +vector &ArffFiles::getY() { return y; } -void ArffFiles::load(const string& fileName, bool classLast) -{ +void ArffFiles::load(const string &fileName, bool classLast) { ifstream file(fileName); if (!file.is_open()) { throw invalid_argument("Unable to open file"); @@ -87,8 +79,7 @@ void ArffFiles::load(const string& fileName, bool classLast) } -void ArffFiles::generateDataset(bool classLast) -{ +void ArffFiles::generateDataset(bool classLast) { X = vector>(attributes.size(), vector(lines.size())); auto yy = vector(lines.size(), ""); int labelIndex = classLast ? static_cast(attributes.size()) : 0; @@ -108,21 +99,19 @@ void ArffFiles::generateDataset(bool classLast) y = factorize(yy); } -string ArffFiles::trim(const string& source) -{ +string ArffFiles::trim(const string &source) { string s(source); s.erase(0, s.find_first_not_of(" \n\r\t")); s.erase(s.find_last_not_of(" \n\r\t") + 1); return s; } -vector ArffFiles::factorize(const vector& labels_t) -{ +vector ArffFiles::factorize(const vector &labels_t) { vector yy; yy.reserve(labels_t.size()); map labelMap; int i = 0; - for (const string& label : labels_t) { + for (const string &label: labels_t) { if (labelMap.find(label) == labelMap.end()) { labelMap[label] = i++; } diff --git a/tests/Metrics_unittest.cpp b/tests/Metrics_unittest.cpp index 5cd73d6..e059fac 100644 --- a/tests/Metrics_unittest.cpp +++ b/tests/Metrics_unittest.cpp @@ -1,44 +1,39 @@ #include "gtest/gtest.h" #include "../Metrics.h" - namespace mdlp { - class TestMetrics: public Metrics, public testing::Test { + class TestMetrics : public Metrics, public testing::Test { public: - labels_t y_ = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; - indices_t indices_ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + labels_t y_ = {1, 1, 1, 1, 1, 2, 2, 2, 2, 2}; + indices_t indices_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; precision_t precision = 0.000001f; - TestMetrics(): Metrics(y_, indices_) {}; + TestMetrics() : Metrics(y_, indices_) {}; - void SetUp() override - { + void SetUp() override { setData(y_, indices_); } }; - TEST_F(TestMetrics, NumClasses) - { - y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; + TEST_F(TestMetrics, NumClasses) { + y = {1, 1, 1, 1, 1, 1, 1, 1, 2, 1}; EXPECT_EQ(1, computeNumClasses(4, 8)); EXPECT_EQ(2, computeNumClasses(0, 10)); EXPECT_EQ(2, computeNumClasses(8, 10)); } - TEST_F(TestMetrics, Entropy) - { + TEST_F(TestMetrics, Entropy) { EXPECT_EQ(1, entropy(0, 10)); EXPECT_EQ(0, entropy(0, 5)); - y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; + y = {1, 1, 1, 1, 1, 1, 1, 1, 2, 1}; setData(y, indices); ASSERT_NEAR(0.468996f, entropy(0, 10), precision); } - TEST_F(TestMetrics, InformationGain) - { + TEST_F(TestMetrics, InformationGain) { ASSERT_NEAR(1, informationGain(0, 5, 10), precision); ASSERT_NEAR(1, informationGain(0, 5, 10), precision); // For cache - y = { 1, 1, 1, 1, 1, 1, 1, 1, 2, 1 }; + y = {1, 1, 1, 1, 1, 1, 1, 1, 2, 1}; setData(y, indices); ASSERT_NEAR(0.108032f, informationGain(0, 5, 10), precision); } From 42e83b3d26b9a237b09d718a01abefc30812c9b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Wed, 22 Mar 2023 18:17:11 +0100 Subject: [PATCH 29/32] move limits include to CPPFImdlp header --- CPPFImdlp.cpp | 41 +++++++++++++++++++++++++---------------- CPPFImdlp.h | 5 +++-- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 50cdc48..48d0301 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -2,22 +2,23 @@ #include #include #include -#include #include "CPPFImdlp.h" #include "Metrics.h" namespace mdlp { - CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed) : min_length(min_length_), - max_depth(max_depth_), - proposed_cuts(proposed) { + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), + max_depth(max_depth_), + proposed_cuts(proposed) + { } CPPFImdlp::CPPFImdlp() = default; CPPFImdlp::~CPPFImdlp() = default; - size_t CPPFImdlp::compute_max_num_cut_points() const { + size_t CPPFImdlp::compute_max_num_cut_points() const + { // Set the actual maximum number of cut points as a number or as a percentage of the number of samples if (proposed_cuts == 0) { return numeric_limits::max(); @@ -30,7 +31,8 @@ namespace mdlp { return static_cast(proposed_cuts); } - void CPPFImdlp::fit(samples_t &X_, labels_t &y_) { + void CPPFImdlp::fit(samples_t& X_, labels_t& y_) + { X = X_; y = y_; num_cut_points = compute_max_num_cut_points(); @@ -53,7 +55,8 @@ namespace mdlp { computeCutPoints(0, X.size(), 1); } - pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) { + pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) + { size_t n; size_t m; size_t idxPrev = cut - 1 >= start ? cut - 1 : cut; @@ -82,10 +85,11 @@ namespace mdlp { // Decide which values to use cut = cut + (backWall ? m + 1 : -n); actual = X[indices[cut]]; - return {(actual + previous) / 2, cut}; + return { (actual + previous) / 2, cut }; } - void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) { + void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) + { size_t cut; pair result; if (cutPoints.size() == num_cut_points) @@ -106,7 +110,8 @@ namespace mdlp { } } - size_t CPPFImdlp::getCandidate(size_t start, size_t end) { + size_t CPPFImdlp::getCandidate(size_t start, size_t end) + { /* Definition 1: A binary discretization for A is determined by selecting the cut point TA for which E(A, TA; S) is minimal amongst all the candidate cut points. */ size_t candidate = numeric_limits::max(); @@ -139,7 +144,8 @@ namespace mdlp { return candidate; } - bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) { + bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) + { int k; int k1; int k2; @@ -157,13 +163,14 @@ namespace mdlp { ent2 = metrics.entropy(cut, end); ig = metrics.informationGain(start, cut, end); delta = static_cast(log2(pow(3, precision_t(k)) - 2) - - (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); + (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); precision_t term = 1 / N * (log2(N - 1) + delta); return ig > term; } // Argsort from https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes - indices_t CPPFImdlp::sortIndices(samples_t &X_, labels_t &y_) { + indices_t CPPFImdlp::sortIndices(samples_t& X_, labels_t& y_) + { indices_t idx(X_.size()); iota(idx.begin(), idx.end(), 0); stable_sort(idx.begin(), idx.end(), [&X_, &y_](size_t i1, size_t i2) { @@ -171,16 +178,18 @@ namespace mdlp { return y_[i1] < y_[i2]; else return X_[i1] < X_[i2]; - }); + }); return idx; } - cutPoints_t CPPFImdlp::getCutPoints() { + cutPoints_t CPPFImdlp::getCutPoints() + { sort(cutPoints.begin(), cutPoints.end()); return cutPoints; } - int CPPFImdlp::get_depth() const { + int CPPFImdlp::get_depth() const + { return depth; } } diff --git a/CPPFImdlp.h b/CPPFImdlp.h index 6a24ca3..c205719 100644 --- a/CPPFImdlp.h +++ b/CPPFImdlp.h @@ -3,6 +3,7 @@ #include "typesFImdlp.h" #include "Metrics.h" +#include #include #include @@ -20,7 +21,7 @@ namespace mdlp { cutPoints_t cutPoints; size_t num_cut_points = numeric_limits::max(); - static indices_t sortIndices(samples_t &, labels_t &); + static indices_t sortIndices(samples_t&, labels_t&); void computeCutPoints(size_t, size_t, int); @@ -39,7 +40,7 @@ namespace mdlp { ~CPPFImdlp(); - void fit(samples_t &, labels_t &); + void fit(samples_t&, labels_t&); cutPoints_t getCutPoints(); From da41a9317d44534c76f16c480e9ae1a3cb32a2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 1 Apr 2023 17:53:00 +0200 Subject: [PATCH 30/32] Refactor github build action --- CPPFImdlp.cpp | 42 ++++++++++++++++-------------------------- tests/CMakeLists.txt | 1 - 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/CPPFImdlp.cpp b/CPPFImdlp.cpp index 48d0301..5778219 100644 --- a/CPPFImdlp.cpp +++ b/CPPFImdlp.cpp @@ -7,18 +7,16 @@ namespace mdlp { - CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed): min_length(min_length_), - max_depth(max_depth_), - proposed_cuts(proposed) - { + CPPFImdlp::CPPFImdlp(size_t min_length_, int max_depth_, float proposed) : min_length(min_length_), + max_depth(max_depth_), + proposed_cuts(proposed) { } CPPFImdlp::CPPFImdlp() = default; CPPFImdlp::~CPPFImdlp() = default; - size_t CPPFImdlp::compute_max_num_cut_points() const - { + size_t CPPFImdlp::compute_max_num_cut_points() const { // Set the actual maximum number of cut points as a number or as a percentage of the number of samples if (proposed_cuts == 0) { return numeric_limits::max(); @@ -31,8 +29,7 @@ namespace mdlp { return static_cast(proposed_cuts); } - void CPPFImdlp::fit(samples_t& X_, labels_t& y_) - { + void CPPFImdlp::fit(samples_t &X_, labels_t &y_) { X = X_; y = y_; num_cut_points = compute_max_num_cut_points(); @@ -55,13 +52,12 @@ namespace mdlp { computeCutPoints(0, X.size(), 1); } - pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) - { + pair CPPFImdlp::valueCutPoint(size_t start, size_t cut, size_t end) { size_t n; size_t m; size_t idxPrev = cut - 1 >= start ? cut - 1 : cut; size_t idxNext = cut + 1 < end ? cut + 1 : cut; - bool backWall; // true if duplicates reach begining of the interval + bool backWall; // true if duplicates reach beginning of the interval precision_t previous; precision_t actual; precision_t next; @@ -85,11 +81,10 @@ namespace mdlp { // Decide which values to use cut = cut + (backWall ? m + 1 : -n); actual = X[indices[cut]]; - return { (actual + previous) / 2, cut }; + return {(actual + previous) / 2, cut}; } - void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) - { + void CPPFImdlp::computeCutPoints(size_t start, size_t end, int depth_) { size_t cut; pair result; if (cutPoints.size() == num_cut_points) @@ -110,8 +105,7 @@ namespace mdlp { } } - size_t CPPFImdlp::getCandidate(size_t start, size_t end) - { + size_t CPPFImdlp::getCandidate(size_t start, size_t end) { /* Definition 1: A binary discretization for A is determined by selecting the cut point TA for which E(A, TA; S) is minimal amongst all the candidate cut points. */ size_t candidate = numeric_limits::max(); @@ -144,8 +138,7 @@ namespace mdlp { return candidate; } - bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) - { + bool CPPFImdlp::mdlp(size_t start, size_t cut, size_t end) { int k; int k1; int k2; @@ -163,14 +156,13 @@ namespace mdlp { ent2 = metrics.entropy(cut, end); ig = metrics.informationGain(start, cut, end); delta = static_cast(log2(pow(3, precision_t(k)) - 2) - - (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); + (precision_t(k) * ent - precision_t(k1) * ent1 - precision_t(k2) * ent2)); precision_t term = 1 / N * (log2(N - 1) + delta); return ig > term; } // Argsort from https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes - indices_t CPPFImdlp::sortIndices(samples_t& X_, labels_t& y_) - { + indices_t CPPFImdlp::sortIndices(samples_t &X_, labels_t &y_) { indices_t idx(X_.size()); iota(idx.begin(), idx.end(), 0); stable_sort(idx.begin(), idx.end(), [&X_, &y_](size_t i1, size_t i2) { @@ -178,18 +170,16 @@ namespace mdlp { return y_[i1] < y_[i2]; else return X_[i1] < X_[i2]; - }); + }); return idx; } - cutPoints_t CPPFImdlp::getCutPoints() - { + cutPoints_t CPPFImdlp::getCutPoints() { sort(cutPoints.begin(), cutPoints.end()); return cutPoints; } - int CPPFImdlp::get_depth() const - { + int CPPFImdlp::get_depth() const { return depth; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d1c1e77..4eb31f5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,3 @@ -# GoogleTest requires at least C++14 set(CMAKE_CXX_STANDARD 11) include(FetchContent) From 0ead15be7c7085051549ce727669757457d7f902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 1 Apr 2023 17:53:37 +0200 Subject: [PATCH 31/32] Refactor github build action --- .github/workflows/build.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index deea63f..85f34f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: - main - "*" pull_request: - types: [opened, synchronize, reopened] + types: [ opened, synchronize, reopened ] jobs: build: name: Build @@ -24,17 +24,13 @@ jobs: sudo apt-get -y install gcovr - name: Tests & build-wrapper run: | - mkdir build - cmake -S . -B build -Wno-dev - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release - cd tests - mkdir build cmake -S . -B build -Wno-dev build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release cd build - ctest -C Release --output-on-failure - cd ../.. - gcovr -e "test/*" --txt --sonarqube=coverage.xml + make + ctest -C Release --output-on-failure --test-dir tests + cd .. + gcovr -f CPPFImdlp.cpp -f Metrics.cpp --merge-mode-functions=separate --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c662a96da84833933b2c45a8cd4cd6057b74fb23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sat, 1 Apr 2023 17:59:46 +0200 Subject: [PATCH 32/32] Refactor github build action --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85f34f6..02ac0a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,8 @@ jobs: make ctest -C Release --output-on-failure --test-dir tests cd .. - gcovr -f CPPFImdlp.cpp -f Metrics.cpp --merge-mode-functions=separate --txt --sonarqube=coverage.xml + # gcovr -f CPPFImdlp.cpp -f Metrics.cpp --merge-mode-functions=separate --txt --sonarqube=coverage.xml + gcovr -f CPPFImdlp.cpp -f Metrics.cpp --txt --sonarqube=coverage.xml - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}