# LCOV - BayesNet Coverage Report - bayesnet/classifiers/Classifier.cc

LCOV - code coverage report

Current view: | top level - bayesnet/classifiers - Classifier.cc (source / functions) |  |  | Coverage | Total | Hit
Test: | BayesNet Coverage Report |  | Lines: | 100.0 % | 126 | 126
Test Date: | 2024-05-06 17:54:04 |  | Functions: | 100.0 % | 24 | 24
Legend: | Lines: hit not hit |

                Line data    Source code

           1              : // ***************************************************************
           2              : // SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
           3              : // SPDX-FileType: SOURCE
           4              : // SPDX-License-Identifier: MIT
           5              : // ***************************************************************
           6              :
           7              : #include <sstream>
           8              : #include "bayesnet/utils/bayesnetUtils.h"
           9              : #include "Classifier.h"
          10              :
          11              : namespace bayesnet {
          12         2240 :     Classifier::Classifier(Network model) : model(model), m(0), n(0), metrics(Metrics()), fitted(false) {}
          13              :     const std::string CLASSIFIER_NOT_FITTED = "Classifier has not been fitted";
          14         1760 :     Classifier& Classifier::build(const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
          15              :     {
          16         1760 :         this->features = features;
          17         1760 :         this->className = className;
          18         1760 :         this->states = states;
          19         1760 :         m = dataset.size(1);
          20         1760 :         n = features.size();
          21         1760 :         checkFitParameters();
          22         1728 :         auto n_classes = states.at(className).size();
          23         1728 :         metrics = Metrics(dataset, features, className, n_classes);
          24         1728 :         model.initialize();
          25         1728 :         buildModel(weights);
          26         1728 :         trainModel(weights);
          27         1712 :         fitted = true;
          28         1712 :         return *this;
          29              :     }
          30          340 :     void Classifier::buildDataset(torch::Tensor& ytmp)
          31              :     {
          32              :         try {
          33          340 :             auto yresized = torch::transpose(ytmp.view({ ytmp.size(0), 1 }), 0, 1);
          34         1052 :             dataset = torch::cat({ dataset, yresized }, 0);
          35          340 :         }
          36           16 :         catch (const std::exception& e) {
          37           16 :             std::stringstream oss;
          38           16 :             oss << "* Error in X and y dimensions *\n";
          39           16 :             oss << "X dimensions: " << dataset.sizes() << "\n";
          40           16 :             oss << "y dimensions: " << ytmp.sizes();
          41           16 :             throw std::runtime_error(oss.str());
          42           32 :         }
          43          680 :     }
          44         1576 :     void Classifier::trainModel(const torch::Tensor& weights)
          45              :     {
          46         1576 :         model.fit(dataset, weights, features, className, states);
          47         1576 :     }
          48              :     // X is nxm where n is the number of features and m the number of samples
          49          128 :     Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
          50              :     {
          51          128 :         dataset = X;
          52          128 :         buildDataset(y);
          53          120 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
          54          208 :         return build(features, className, states, weights);
          55          120 :     }
          56              :     // X is nxm where n is the number of features and m the number of samples
          57          136 :     Classifier& Classifier::fit(std::vector<std::vector<int>>& X, std::vector<int>& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
          58              :     {
          59          136 :         dataset = torch::zeros({ static_cast<int>(X.size()), static_cast<int>(X[0].size()) }, torch::kInt32);
          60          976 :         for (int i = 0; i < X.size(); ++i) {
          61         3360 :             dataset.index_put_({ i, "..." }, torch::tensor(X[i], torch::kInt32));
          62              :         }
          63          136 :         auto ytmp = torch::tensor(y, torch::kInt32);
          64          136 :         buildDataset(ytmp);
          65          128 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
          66          240 :         return build(features, className, states, weights);
          67          992 :     }
          68          852 :     Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
          69              :     {
          70          852 :         this->dataset = dataset;
          71          852 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
          72         1704 :         return build(features, className, states, weights);
          73          852 :     }
          74          660 :     Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
          75              :     {
          76          660 :         this->dataset = dataset;
          77          660 :         return build(features, className, states, weights);
          78              :     }
          79         1760 :     void Classifier::checkFitParameters()
          80              :     {
          81         1760 :         if (torch::is_floating_point(dataset)) {
          82            8 :             throw std::invalid_argument("dataset (X, y) must be of type Integer");
          83              :         }
          84         1752 :         if (dataset.size(0) - 1 != features.size()) {
          85            8 :             throw std::invalid_argument("Classifier: X " + std::to_string(dataset.size(0) - 1) + " and features " + std::to_string(features.size()) + " must have the same number of features");
          86              :         }
          87         1744 :         if (states.find(className) == states.end()) {
          88            8 :             throw std::invalid_argument("class name not found in states");
          89              :         }
          90        32996 :         for (auto feature : features) {
          91        31268 :             if (states.find(feature) == states.end()) {
          92            8 :                 throw std::invalid_argument("feature [" + feature + "] not found in states");
          93              :             }
          94        31268 :         }
          95         1728 :     }
          96         1844 :     torch::Tensor Classifier::predict(torch::Tensor& X)
          97              :     {
          98         1844 :         if (!fitted) {
          99           16 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
         100              :         }
         101         1828 :         return model.predict(X);
         102              :     }
         103           16 :     std::vector<int> Classifier::predict(std::vector<std::vector<int>>& X)
         104              :     {
         105           16 :         if (!fitted) {
         106            8 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
         107              :         }
         108            8 :         auto m_ = X[0].size();
         109            8 :         auto n_ = X.size();
         110            8 :         std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
         111           40 :         for (auto i = 0; i < n_; i++) {
         112           64 :             Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
         113              :         }
         114            8 :         auto yp = model.predict(Xd);
         115           16 :         return yp;
         116            8 :     }
         117         1484 :     torch::Tensor Classifier::predict_proba(torch::Tensor& X)
         118              :     {
         119         1484 :         if (!fitted) {
         120            8 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
         121              :         }
         122         1476 :         return model.predict_proba(X);
         123              :     }
         124          548 :     std::vector<std::vector<double>> Classifier::predict_proba(std::vector<std::vector<int>>& X)
         125              :     {
         126          548 :         if (!fitted) {
         127            8 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
         128              :         }
         129          540 :         auto m_ = X[0].size();
         130          540 :         auto n_ = X.size();
         131          540 :         std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
         132              :         // Convert to nxm vector
         133         5040 :         for (auto i = 0; i < n_; i++) {
         134         9000 :             Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
         135              :         }
         136          540 :         auto yp = model.predict_proba(Xd);
         137         1080 :         return yp;
         138          540 :     }
         139          112 :     float Classifier::score(torch::Tensor& X, torch::Tensor& y)
         140              :     {
         141          112 :         torch::Tensor y_pred = predict(X);
         142          208 :         return (y_pred == y).sum().item<float>() / y.size(0);
         143          104 :     }
         144           16 :     float Classifier::score(std::vector<std::vector<int>>& X, std::vector<int>& y)
         145              :     {
         146           16 :         if (!fitted) {
         147            8 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
         148              :         }
         149            8 :         return model.score(X, y);
         150              :     }
         151           24 :     std::vector<std::string> Classifier::show() const
         152              :     {
         153           24 :         return model.show();
         154              :     }
         155         1576 :     void Classifier::addNodes()
         156              :     {
         157              :         // Add all nodes to the network
         158        30872 :         for (const auto& feature : features) {
         159        29296 :             model.addNode(feature);
         160              :         }
         161         1576 :         model.addNode(className);
         162         1576 :     }
         163          332 :     int Classifier::getNumberOfNodes() const
         164              :     {
         165              :         // Features does not include class
         166          332 :         return fitted ? model.getFeatures().size() : 0;
         167              :     }
         168          332 :     int Classifier::getNumberOfEdges() const
         169              :     {
         170          332 :         return fitted ? model.getNumEdges() : 0;
         171              :     }
         172           24 :     int Classifier::getNumberOfStates() const
         173              :     {
         174           24 :         return fitted ? model.getStates() : 0;
         175              :     }
         176          348 :     int Classifier::getClassNumStates() const
         177              :     {
         178          348 :         return fitted ? model.getClassNumStates() : 0;
         179              :     }
         180            4 :     std::vector<std::string> Classifier::topological_order()
         181              :     {
         182            4 :         return model.topological_sort();
         183              :     }
         184            4 :     std::string Classifier::dump_cpt() const
         185              :     {
         186            4 :         return model.dump_cpt();
         187              :     }
         188           92 :     void Classifier::setHyperparameters(const nlohmann::json& hyperparameters)
         189              :     {
         190           92 :         if (!hyperparameters.empty()) {
         191            8 :             throw std::invalid_argument("Invalid hyperparameters" + hyperparameters.dump());
         192              :         }
         193           84 :     }
         194              : }

Generated by: LCOV version 2.0-1