diff --git a/.clang-format b/.clang-format index dc69733..813766e 100644 --- a/.clang-format +++ b/.clang-format @@ -1,83 +1,280 @@ -# Google C/C++ Code Style settings -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# Author: Kehan Xue, kehan.xue (at) gmail.com - -Language: Cpp -BasedOnStyle: Google +--- +Language: Cpp +# BasedOnStyle: Google AccessModifierOffset: -1 AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: None -AlignOperands: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Empty +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding -AllowShortLambdasOnASingleLine: Inline -AllowShortLoopsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability BinPackArguments: true -BreakBeforeBraces: Custom +BinPackParameters: true +BitFieldColonSpacing: Both BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterStruct: false + AfterCaseLabel: false + AfterClass: false AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterUnion: false + AfterEnum: false AfterExternBlock: false - BeforeCatch: false - BeforeElse: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false BeforeLambdaBody: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon -ColumnLimit: 80 +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true -DerivePointerAlignment: false # Make sure the * or & align on the left +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true -IncludeBlocks: Preserve +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true IndentPPDirectives: None -IndentWidth: 2 -KeepEmptyLinesAtTheStartOfBlocks: true +IndentRequiresClause: true +IndentWidth: 8 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left -ReflowComments: false -# SeparateDefinitionBlocks: Always # Only support since clang-format 14 +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false -SpaceInEmptyParentheses: false +SpaceInEmptyBlock: false SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false SpacesInSquareBrackets: false -Standard: c++11 -TabWidth: 4 -UseTab: Never \ No newline at end of file +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/examples/main.cpp b/examples/main.cpp index 19b1560..aa82240 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,54 +1,47 @@ #include + #include "mlcpppy/classifiers/neighbors/kdtree.h" -int main(int argc, char const *argv[]) -{ - int x, y, k; - if (argc < 4) { - std::cerr << "Uso: " << argv[0] << " \n"; - x = 7; - y = 4; - k = 2; - // return 1; - } else { - x = std::atoi(argv[1]); - y = std::atoi(argv[2]); - k = std::atoi(argv[3]); - } - - std::vector> points = { - {1, 2}, - {2, 1}, - {3, 2}, - {7, 4}, - {5, 9}, - {6, 1}, - {0, 3}, - {4, 7}, - {8, 2}, - {3, 5} - }; - - auto tree = new KDTree(); - tree->BuildTree(points); - tree->PrintInorder(); - - std::vector> points_knn = tree->KNearestNeighbor({x, y}, k); - - for (size_t i = 0; i < points_knn.size(); ++i) { - std::cout << "Point " << i << ": "; - for (size_t j = 0; j < points_knn[i].size(); ++j) { - std::cout << points_knn[i][j] << " "; +int main(int argc, char const *argv[]) { + double x, y; + int k; + if (argc < 4) { + std::cerr << "Uso: " << argv[0] << " \n"; + x = 7; + y = 4; + k = 2; + // return 1; + } else { + x = std::atoi(argv[1]); + y = std::atoi(argv[2]); + k = std::atoi(argv[3]); } - std::cout << std::endl; - } - std::cout << std::endl; - std::cout << std::endl; - std::cout << std::endl; + std::vector> points = { + {1.0, 2.0}, {2.0, 1.0}, {3.0, 2.0}, {7.0, 4.0}, {5.0, 9.0}, + {6.0, 1.0}, {0.0, 3.0}, {4.0, 7.0}, {8.0, 2.0}, {3.0, 5.0}}; + + auto tree = new KDTree(); + tree->BuildTree(points); + tree->PrintInorder(); + + std::vector> points_knn = + tree->KNearestNeighbor(Point({x, y}), k); + + for (size_t i = 0; i < points_knn.size(); ++i) { + std::cout << "Point " << i << ": "; + for (size_t j = 0; j < points_knn[i].size(); ++j) { + std::cout << points_knn[i].data().at(j) << " "; + } + std::cout << std::endl; + } + + std::cout << std::endl; + std::cout << std::endl; + std::cout << std::endl; - tree->Insert({x+1, y-1}); - tree->PrintInorder(); + tree->Insert({x + 1, y - 1}); + tree->PrintInorder(); - return 0; + return 0; } diff --git a/include/mlcpppy/classifiers/classifier.h b/include/mlcpppy/classifiers/classifier.h index d91dd5b..4333b97 100644 --- a/include/mlcpppy/classifiers/classifier.h +++ b/include/mlcpppy/classifiers/classifier.h @@ -17,9 +17,9 @@ #ifndef CLASSIFIER_H #define CLASSIFIER_H class Classifier { - public: - virtual void train() = 0; - virtual int predict() = 0; - virtual ~Classifier() {} + public: + virtual void train() = 0; + virtual int predict() = 0; + virtual ~Classifier() {} }; #endif // CLASSIFIER_H \ No newline at end of file diff --git a/include/mlcpppy/classifiers/knn.h b/include/mlcpppy/classifiers/knn.h index bea4ada..f5610e2 100644 --- a/include/mlcpppy/classifiers/knn.h +++ b/include/mlcpppy/classifiers/knn.h @@ -20,9 +20,9 @@ #include "classifier.h" class KNN : Classifier { - private: - public: - KNN(/* args */); - ~KNN(); + private: + public: + KNN(/* args */); + ~KNN(); }; #endif // KNN_H \ No newline at end of file diff --git a/include/mlcpppy/classifiers/neighbors/kdtree.h b/include/mlcpppy/classifiers/neighbors/kdtree.h index da34d32..b36405b 100644 --- a/include/mlcpppy/classifiers/neighbors/kdtree.h +++ b/include/mlcpppy/classifiers/neighbors/kdtree.h @@ -19,30 +19,278 @@ #include #include +#include #include "nearest_neighbor.h" -class KDTree : public NearestNeighbor { - private: - class Node; - Node* root_; - int K_; - std::priority_queue> bests_; - - Node* Build(std::vector> points, int depth); - Node* NearestNeighbor(Node* root, std::vector& target, int depth); - void KNearestNeighbor(Node* root, std::vector& target, int depth); - - Node* Closest(Node* n0, Node* n1, std::vector& target); - double DistSquared(const std::vector& p0, const std::vector& p1); - static void Inorder(Node* root); - - public: - KDTree(); - void Insert(std::vector point) override; - void BuildTree(std::vector> points) override; - std::vector> KNearestNeighbor(std::vector target_points, int k) override; - void PrintInorder(); - - ~KDTree() override; +template +class KDTree : public NearestNeighbor { +private: + class Node { + public: + Node* left_; + Node* right_; + Point point_; + int depth_; + + explicit Node() {} + explicit Node(Point point, Node* left, Node* right) : point_(point), left_(left), right_(right) {} + explicit Node(Point point, Node* left, Node* right, int depth) : point_(point), left_(left), right_(right), depth_(depth) {} + explicit Node(Point point) : point_(point) { + this->left_ = nullptr; + this->right_ = nullptr; + } + explicit Node(Point point, int depth) : point_(point), depth_(depth) { + this->left_ = nullptr; + this->right_ = nullptr; + } + + ~Node() { + delete left_; + delete right_; + } + }; + + Node* root_; + int K_; + std::priority_queue> bests_; + + Node* Build(std::vector> points, int depth) { + if (points.empty()) { + return nullptr; + } + int k = points.at(0).data().size(); + int axis = depth % k; + + std::sort(points.begin(), points.end(), [axis](const Point& a, const Point& b) { + return a.data()[axis] < b.data()[axis]; + }); + + int median = points.size() / 2; + std::vector> points_left(points.begin(), points.begin() + median); + std::vector> points_right(points.begin() + median + 1, points.end()); + + + return new Node( + points.at(median), + Build(points_left, depth + 1), + Build(points_right, depth + 1), + depth + ); + } + + + Node* NearestNeighbor(Node* root, Point& target, int depth) { + if (root == nullptr) return nullptr; + + Node* next_branch; + Node* other_branch; + + int axis = depth % root->point_.size(); + + if (target.data().at(axis) < root->point_.data().at(axis)) + { + next_branch = root->left_; + other_branch = root->right_; + } else { + next_branch = root->right_; + other_branch = root->left_; + } + + Node* temp = NearestNeighbor(next_branch, target, depth + 1); + Node* best = Closest(temp, root, target); + double radius_squared = DistSquared(target, best->point_); + + double dist = target.data().at(axis) - root->point_.data().at(axis); + + if (radius_squared >= dist * dist) + { + temp = NearestNeighbor(other_branch, target, depth + 1); + best = Closest(temp, best, target); + } + + return best; + + } + + void KNearestNeighbor(Node* root, Point& target, int depth) { + if (root == nullptr) return; + + Node* next_branch; + Node* other_branch; + + int axis = depth % root->point_.size(); + + if (target.data().at(axis) < root->point_.data().at(axis)) + { + next_branch = root->left_; + other_branch = root->right_; + } else { + next_branch = root->right_; + other_branch = root->left_; + } + + KNearestNeighbor(next_branch, target, depth + 1); + double dist = DistSquared(target, root->point_); + + if (this->bests_.size() < this->K_) + { + this->bests_.push({dist, root}); + } + else if (dist < this->bests_.top().first) + { + bests_.pop(); + this->bests_.push({dist, root}); + } + + double diff = target.data().at(axis) - root->point_.data().at(axis); + + if (this->bests_.size() < this->K_ || diff * diff < this->bests_.top().first) { + KNearestNeighbor(other_branch, target, depth + 1); + } + } + + + Node* Closest(Node* n0, Node* n1, Point& target) { + if (n0 == nullptr) return n1; + if (n1 == nullptr) return n0; + + long d1 = DistSquared(n0->point_, target); + long d2 = DistSquared(n1->point_, target); + + if (d1 < d2) + return n0; + else + return n1; + } + + double DistSquared(const Point& p0, const Point& p1) { + long total = 0; + size_t numDims = p0.size(); + + for (size_t i = 0; i < numDims; ++i) { + int diff = std::abs(p0.data()[i] - p1.data()[i]); + total += static_cast(diff) * diff; // mais eficiente que pow para int + } + + return total; + } + + static void Inorder(Node* root) { + if (!root) return; + + Inorder(root->left_); + + if (root->left_) { + std::cout << " \""; + for (size_t i = 0; i < root->point_.size(); i++) + std::cout << root->point_.data()[i] << (i + 1 == root->point_.size() ? "" : ","); + std::cout << "\" -> \""; + for (size_t i = 0; i < root->left_->point_.size(); i++) + std::cout << root->left_->point_.data()[i] << (i + 1 == root->left_->point_.size() ? "" : ","); + std::cout << "\" [label=\"esq\"];\n"; + } + + if (root->right_) { + std::cout << " \""; + for (size_t i = 0; i < root->point_.size(); i++) + std::cout << root->point_.data()[i] << (i + 1 == root->point_.size() ? "" : ","); + std::cout << "\" -> \""; + for (size_t i = 0; i < root->right_->point_.size(); i++) + std::cout << root->right_->point_.data()[i] << (i + 1 == root->right_->point_.size() ? "" : ","); + std::cout << "\" [label=\"dir\"];\n"; + } + + Inorder(root->right_); + } + +public: + KDTree() : root_(nullptr) {} + void Insert(Point point) override { + Node* p = this->root_; + Node* prev = nullptr; + + int depth = 0; + int n_dims = point.size(); + + while (p != nullptr) + { + prev = p; + if (point.data().at(depth) < p->point_.data().at(depth)) + p = p->left_; + else + p= p->right_; + depth = (depth + 1) % n_dims; + } + + if (this->root_ == nullptr) + this->root_ = new Node(point); + else if((point.data().at((depth - 1) % n_dims)) < (prev->point_.data().at((depth - 1) % n_dims))) + prev->left_ = new Node(point, depth); + else + prev->right_ = new Node(point, depth); + } + + void BuildTree(std::vector> points) override { + if (points.empty()) + { + return; + } + + int initial_size = points.at(0).size(); + + for (auto &point : points) + { + if (point.data().empty()) + { + return; + } + + if (point.size() != initial_size) + { + return; + } + + + } + + this->root_ = Build(points, 0); + } + + + std::vector> KNearestNeighbor(Point target_points, int k) override { + if (k) + this->K_ = k; + + if (k == 1){ + Node* result = NearestNeighbor(this->root_, target_points, 0); + std::vector> points; + if (result) + { + points.push_back(result->point_); + } + return points; + } + + KNearestNeighbor(this->root_, target_points, 0); + std::vector> tmp; + while (!this->bests_.empty()) + { + tmp.push_back(this->bests_.top().second->point_); + this->bests_.pop(); + } + + return tmp; + } + + void PrintInorder() { + // TODO: A ideia é isso gerar um arquivo para o graphiz gerar o grafo + std::cout << "digraph G {\n"; + Inorder(this->root_); + std::cout << "}\n"; + } + + ~KDTree() override { + delete root_; + } }; #endif // KDTREE_H \ No newline at end of file diff --git a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h index 1a310d5..dbb445e 100644 --- a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h +++ b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h @@ -17,14 +17,15 @@ #ifndef NEAREST_NEIGHBOR_H #define NEAREST_NEIGHBOR_H #include +#include "point.h" +template class NearestNeighbor { public: virtual ~NearestNeighbor() = default; - virtual std::vector> KNearestNeighbor(std::vector, - int) = 0; - virtual void Insert(std::vector) = 0; - virtual void BuildTree(std::vector>) = 0; - virtual void Delete(std::vector){}; + virtual std::vector> KNearestNeighbor(Point, int) = 0; + virtual void Insert(Point) = 0; + virtual void BuildTree(std::vector>) = 0; + virtual void Delete(Point){}; }; #endif // NEAREST_NEIGHBOR_H \ No newline at end of file diff --git a/include/mlcpppy/classifiers/neighbors/point.h b/include/mlcpppy/classifiers/neighbors/point.h new file mode 100644 index 0000000..b012019 --- /dev/null +++ b/include/mlcpppy/classifiers/neighbors/point.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Pedro Bianchini de Quadros + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef POINT_H +#define POINT_H + +#include +#include +#include +#include + +template ::value>::type> +class Point { + private: + std::array data_; + + public: + explicit Point() : data_{} {} + explicit Point(const std::array &data) : data_(data) {} + inline Point(std::initializer_list list) { + if (list.size() != N) { + throw std::invalid_argument( + "Initializer list size must match Point dimension"); + } + std::copy(list.begin(), list.end(), data_.begin()); + } + const std::array &data() const { return data_; } + const size_t size() const { + return data_.size(); + } +}; + +#endif // POINT_H \ No newline at end of file diff --git a/include/mlcpppy/export.h b/include/mlcpppy/export.h index 0255c74..ca4df2d 100644 --- a/include/mlcpppy/export.h +++ b/include/mlcpppy/export.h @@ -1,11 +1,11 @@ #pragma once #ifdef _WIN32 - #ifdef MLCPPPY_EXPORTS - #define MLCPPPY_API __declspec(dllexport) - #else - #define MLCPPPY_API __declspec(dllimport) - #endif +#ifdef MLCPPPY_EXPORTS +#define MLCPPPY_API __declspec(dllexport) #else - #define MLCPPPY_API +#define MLCPPPY_API __declspec(dllimport) +#endif +#else +#define MLCPPPY_API #endif diff --git a/src/classifiers/neighbors/kdtree.cpp b/src/classifiers/neighbors/kdtree.cpp deleted file mode 100644 index e1fdba2..0000000 --- a/src/classifiers/neighbors/kdtree.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2025 Pedro Bianchini de Quadros - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mlcpppy/classifiers/neighbors/kdtree.h" -#include -#include - -class KDTree::Node { -public: - Node* left_; - Node* right_; - std::vector point_; - int depth_; - - explicit Node() {} - explicit Node(std::vector point, Node* left, Node* right) : point_(point), left_(left), right_(right) {} - explicit Node(std::vector point, Node* left, Node* right, int depth) : point_(point), left_(left), right_(right), depth_(depth) {} - explicit Node(std::vector point) : point_(point) { - this->left_ = nullptr; - this->right_ = nullptr; - } - explicit Node(std::vector point, int depth) : point_(point), depth_(depth) { - this->left_ = nullptr; - this->right_ = nullptr; - } - - ~Node() { - delete left_; - delete right_; - } -}; - -KDTree::Node* KDTree::Build(std::vector> points, int depth) { - - if (points.empty()) { - return nullptr; - } - int k = points.at(0).size(); - int axis = depth % k; - - std::sort(points.begin(), points.end(), [axis](const std::vector& a, const std::vector& b) { - return a[axis] < b[axis]; - }); - - int median = points.size() / 2; - std::vector> points_left(points.begin(), points.begin() + median); - std::vector> points_right(points.begin() + median + 1, points.end()); - - - return new Node( - points.at(median), - Build(points_left, depth + 1), - Build(points_right, depth + 1), - depth - ); -} - -KDTree::KDTree() : root_(nullptr) {} - -void KDTree::Insert(std::vector point) { - Node* p = this->root_; - Node* prev = nullptr; - - int depth = 0; - int n_dims = point.size(); - - while (p != nullptr) - { - prev = p; - if (point.at(depth) < p->point_.at(depth)) - p = p->left_; - else - p= p->right_; - depth = (depth + 1) % n_dims; - } - - if (this->root_ == nullptr) - this->root_ = new Node(point); - else if((point.at((depth - 1) % n_dims)) < (prev->point_.at((depth - 1) % n_dims))) - prev->left_ = new Node(point, depth); - else - prev->right_ = new Node(point, depth); -} - -void KDTree::BuildTree(std::vector> points) { - if (points.empty()) - { - return; - } - - int initial_size = points.at(0).size(); - - for (auto &point : points) - { - if (point.empty()) - { - return; - } - - if (point.size() != initial_size) - { - return; - } - - - } - - this->root_ = Build(points, 0); -} - -void KDTree::KNearestNeighbor(Node* root, std::vector& target, int depth) { - if (root == nullptr) return; - - Node* next_branch; - Node* other_branch; - - int axis = depth % root->point_.size(); - - if (target.at(axis) < root->point_.at(axis)) - { - next_branch = root->left_; - other_branch = root->right_; - } else { - next_branch = root->right_; - other_branch = root->left_; - } - - KNearestNeighbor(next_branch, target, depth + 1); - double dist = DistSquared(target, root->point_); - - if (this->bests_.size() < this->K_) - { - this->bests_.push({dist, root}); - } - else if (dist < this->bests_.top().first) - { - bests_.pop(); - this->bests_.push({dist, root}); - } - - double diff = target.at(axis) - root->point_.at(axis); - - if (this->bests_.size() < this->K_ || diff * diff < this->bests_.top().first) { - KNearestNeighbor(other_branch, target, depth + 1); - } -} - -KDTree::Node* KDTree::NearestNeighbor(KDTree::Node* root, std::vector& target, int depth) { - if (root == nullptr) return nullptr; - - Node* next_branch; - Node* other_branch; - - int axis = depth % root->point_.size(); - - if (target.at(axis) < root->point_.at(axis)) - { - next_branch = root->left_; - other_branch = root->right_; - } else { - next_branch = root->right_; - other_branch = root->left_; - } - - Node* temp = NearestNeighbor(next_branch, target, depth + 1); - Node* best = Closest(temp, root, target); - double radius_squared = DistSquared(target, best->point_); - - double dist = target.at(axis) - root->point_.at(axis); - - if (radius_squared >= dist * dist) - { - temp = NearestNeighbor(other_branch, target, depth + 1); - best = Closest(temp, best, target); - } - - return best; - -} - -KDTree::Node* KDTree::Closest(KDTree::Node* n0, KDTree::Node* n1, std::vector& target) { - if (n0 == nullptr) return n1; - - if (n1 == nullptr) return n0; - - long d1 = DistSquared(n0->point_, target); - long d2 = DistSquared(n1->point_, target); - - if (d1 < d2) - return n0; - else - return n1; -} - -double KDTree::DistSquared(const std::vector& p0, const std::vector& p1) { - long total = 0; - size_t numDims = p0.size(); - - for (size_t i = 0; i < numDims; ++i) { - int diff = std::abs(p0[i] - p1[i]); - total += static_cast(diff) * diff; // mais eficiente que pow para int - } - - return total; -} - - -std::vector> KDTree::KNearestNeighbor(std::vector target_points, int k) { - - if (k) - this->K_ = k; - - if (k == 1){ - Node* result = NearestNeighbor(this->root_, target_points, 0); - std::vector> points; - if (result) - { - points.push_back(result->point_); - } - return points; - } - - KNearestNeighbor(this->root_, target_points, 0); - std::vector> tmp; - while (!this->bests_.empty()) - { - tmp.push_back(this->bests_.top().second->point_); - this->bests_.pop(); - } - - return tmp; -} - -void KDTree::Inorder(Node* root) { - if (!root) return; - - Inorder(root->left_); - - if (root->left_) { - std::cout << " \""; - for (size_t i = 0; i < root->point_.size(); i++) - std::cout << root->point_[i] << (i + 1 == root->point_.size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->left_->point_.size(); i++) - std::cout << root->left_->point_[i] << (i + 1 == root->left_->point_.size() ? "" : ","); - std::cout << "\" [label=\"esq\"];\n"; - } - - if (root->right_) { - std::cout << " \""; - for (size_t i = 0; i < root->point_.size(); i++) - std::cout << root->point_[i] << (i + 1 == root->point_.size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->right_->point_.size(); i++) - std::cout << root->right_->point_[i] << (i + 1 == root->right_->point_.size() ? "" : ","); - std::cout << "\" [label=\"dir\"];\n"; - } - - Inorder(root->right_); -} - -void KDTree::PrintInorder() { - // TODO: A ideia é isso gerar um arquivo para o graphiz gerar o grafo - std::cout << "digraph G {\n"; - Inorder(this->root_); - std::cout << "}\n"; -} - -KDTree::~KDTree() { delete root_; }