From 92d4806af1e6fc2cca3d41c345d2d79c2db84a06 Mon Sep 17 00:00:00 2001 From: Pedro Bianchini de Quadros Date: Tue, 9 Sep 2025 14:29:29 -0300 Subject: [PATCH 1/3] err: impl error fixing in home --- examples/main.cpp | 27 +++--- .../mlcpppy/classifiers/neighbors/kdtree.h | 21 +++-- .../classifiers/neighbors/nearest_neighbor.h | 13 +-- include/mlcpppy/classifiers/neighbors/point.h | 44 ++++++++++ src/classifiers/neighbors/kdtree.cpp | 84 +++++++++---------- 5 files changed, 120 insertions(+), 69 deletions(-) create mode 100644 include/mlcpppy/classifiers/neighbors/point.h diff --git a/examples/main.cpp b/examples/main.cpp index 19b1560..a1e5a8a 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -3,7 +3,8 @@ int main(int argc, char const *argv[]) { - int x, y, k; + double x, y; + int k; if (argc < 4) { std::cerr << "Uso: " << argv[0] << " \n"; x = 7; @@ -16,24 +17,24 @@ int main(int argc, char const *argv[]) 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} + 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({x, y}, k); + std::vector> points_knn = tree->KNearestNeighbor(Point({x, y}), k); for (size_t i = 0; i < points_knn.size(); ++i) { std::cout << "Point " << i << ": "; diff --git a/include/mlcpppy/classifiers/neighbors/kdtree.h b/include/mlcpppy/classifiers/neighbors/kdtree.h index da34d32..a9bb2d5 100644 --- a/include/mlcpppy/classifiers/neighbors/kdtree.h +++ b/include/mlcpppy/classifiers/neighbors/kdtree.h @@ -19,28 +19,31 @@ #include #include + #include "nearest_neighbor.h" -class KDTree : public NearestNeighbor { +template +class KDTree : public NearestNeighbor { private: + using PointType = typename NearestNeighbor::PointType; 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* Build(std::vector points, int depth); + Node* NearestNeighbor(Node* root, const PointType& target, int depth); + void KNearestNeighbor(Node* root, const PointType& target, int depth); - Node* Closest(Node* n0, Node* n1, std::vector& target); - double DistSquared(const std::vector& p0, const std::vector& p1); + Node* Closest(Node* n0, Node* n1, const PointType& target); + double DistSquared(const PointType& p0, const PointType& 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 Insert(const PointType& point) override; + void BuildTree(const std::vector& points) override; + std::vector KNearestNeighbor(const PointType& target_points, int k) override; void PrintInorder(); ~KDTree() override; diff --git a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h index 1a310d5..c19ee9b 100644 --- a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h +++ b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h @@ -18,13 +18,16 @@ #define NEAREST_NEIGHBOR_H #include +#include "point.h" + +template class NearestNeighbor { public: + using PointType = Point; 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(const PointType&, int) = 0; + virtual void Insert(const PointType&) = 0; + virtual void BuildTree(const std::vector&) = 0; + virtual void Delete(const PointType&){}; }; #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..76b9db9 --- /dev/null +++ b/include/mlcpppy/classifiers/neighbors/point.h @@ -0,0 +1,44 @@ +/* + * 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_; } +}; + + + + +#endif \ No newline at end of file diff --git a/src/classifiers/neighbors/kdtree.cpp b/src/classifiers/neighbors/kdtree.cpp index e1fdba2..39f8771 100644 --- a/src/classifiers/neighbors/kdtree.cpp +++ b/src/classifiers/neighbors/kdtree.cpp @@ -23,17 +23,17 @@ class KDTree::Node { public: Node* left_; Node* right_; - std::vector point_; + PointType 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) { + explicit Node(PointType point, Node* left, Node* right) : point_(point), left_(left), right_(right) {} + explicit Node(PointType point, Node* left, Node* right, int depth) : point_(point), left_(left), right_(right), depth_(depth) {} + explicit Node(PointType point) : point_(point) { this->left_ = nullptr; this->right_ = nullptr; } - explicit Node(std::vector point, int depth) : point_(point), depth_(depth) { + explicit Node(PointType point, int depth) : point_(point), depth_(depth) { this->left_ = nullptr; this->right_ = nullptr; } @@ -44,21 +44,21 @@ class KDTree::Node { } }; -KDTree::Node* KDTree::Build(std::vector> points, int depth) { +KDTree::Node* KDTree::Build(std::vector points, int depth) { if (points.empty()) { return nullptr; } - int k = points.at(0).size(); + int k = points.at(0).data().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]; + std::sort(points.begin(), points.end(), [axis](const PointType& a, const PointType& 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()); + std::vector points_left(points.begin(), points.begin() + median); + std::vector points_right(points.begin() + median + 1, points.end()); return new Node( @@ -71,17 +71,17 @@ KDTree::Node* KDTree::Build(std::vector> points, int depth) { KDTree::KDTree() : root_(nullptr) {} -void KDTree::Insert(std::vector point) { +void KDTree::Insert(const PointType& point) { Node* p = this->root_; Node* prev = nullptr; int depth = 0; - int n_dims = point.size(); + int n_dims = point.data().size(); while (p != nullptr) { prev = p; - if (point.at(depth) < p->point_.at(depth)) + if (point.data().at(depth) < p->point_.data().at(depth)) p = p->left_; else p= p->right_; @@ -90,28 +90,28 @@ void KDTree::Insert(std::vector point) { if (this->root_ == nullptr) this->root_ = new Node(point); - else if((point.at((depth - 1) % n_dims)) < (prev->point_.at((depth - 1) % n_dims))) + 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 KDTree::BuildTree(std::vector> points) { +void KDTree::BuildTree(const std::vector& points) { if (points.empty()) { return; } - int initial_size = points.at(0).size(); + int initial_size = points.at(0).data().size(); for (auto &point : points) { - if (point.empty()) + if (point.data().empty()) { return; } - if (point.size() != initial_size) + if (point.data().size() != initial_size) { return; } @@ -122,15 +122,15 @@ void KDTree::BuildTree(std::vector> points) { this->root_ = Build(points, 0); } -void KDTree::KNearestNeighbor(Node* root, std::vector& target, int depth) { +void KDTree::KNearestNeighbor(Node* root, const PointType& target, int depth) { if (root == nullptr) return; Node* next_branch; Node* other_branch; - int axis = depth % root->point_.size(); + int axis = depth % root->point_.data().size(); - if (target.at(axis) < root->point_.at(axis)) + if (target.data().at(axis) < root->point_.data().at(axis)) { next_branch = root->left_; other_branch = root->right_; @@ -152,22 +152,22 @@ void KDTree::KNearestNeighbor(Node* root, std::vector& target, int depth) { this->bests_.push({dist, root}); } - double diff = target.at(axis) - root->point_.at(axis); + 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); } } -KDTree::Node* KDTree::NearestNeighbor(KDTree::Node* root, std::vector& target, int depth) { +KDTree::Node* KDTree::NearestNeighbor(KDTree::Node* root, const PointType& target, int depth) { if (root == nullptr) return nullptr; Node* next_branch; Node* other_branch; - int axis = depth % root->point_.size(); + int axis = depth % root->point_.data().size(); - if (target.at(axis) < root->point_.at(axis)) + if (target.data().at(axis) < root->point_.data().at(axis)) { next_branch = root->left_; other_branch = root->right_; @@ -180,7 +180,7 @@ KDTree::Node* KDTree::NearestNeighbor(KDTree::Node* root, std::vector& targ Node* best = Closest(temp, root, target); double radius_squared = DistSquared(target, best->point_); - double dist = target.at(axis) - root->point_.at(axis); + double dist = target.data().at(axis) - root->point_.data().at(axis); if (radius_squared >= dist * dist) { @@ -192,7 +192,7 @@ KDTree::Node* KDTree::NearestNeighbor(KDTree::Node* root, std::vector& targ } -KDTree::Node* KDTree::Closest(KDTree::Node* n0, KDTree::Node* n1, std::vector& target) { +KDTree::Node* KDTree::Closest(KDTree::Node* n0, KDTree::Node* n1, const PointType& target) { if (n0 == nullptr) return n1; if (n1 == nullptr) return n0; @@ -206,12 +206,12 @@ KDTree::Node* KDTree::Closest(KDTree::Node* n0, KDTree::Node* n1, std::vector& p0, const std::vector& p1) { +double KDTree::DistSquared(const PointType& p0, const PointType& p1) { long total = 0; - size_t numDims = p0.size(); + size_t numDims = p0.data().size(); for (size_t i = 0; i < numDims; ++i) { - int diff = std::abs(p0[i] - p1[i]); + int diff = std::abs(p0.data()[i] - p1.data()[i]); total += static_cast(diff) * diff; // mais eficiente que pow para int } @@ -219,14 +219,14 @@ double KDTree::DistSquared(const std::vector& p0, const std::vector& p } -std::vector> KDTree::KNearestNeighbor(std::vector target_points, int k) { +std::vector KDTree::KNearestNeighbor(const PointType& target_points, int k) { if (k) this->K_ = k; if (k == 1){ Node* result = NearestNeighbor(this->root_, target_points, 0); - std::vector> points; + std::vector points; if (result) { points.push_back(result->point_); @@ -235,7 +235,7 @@ std::vector> KDTree::KNearestNeighbor(std::vector target_p } KNearestNeighbor(this->root_, target_points, 0); - std::vector> tmp; + std::vector tmp; while (!this->bests_.empty()) { tmp.push_back(this->bests_.top().second->point_); @@ -252,21 +252,21 @@ void KDTree::Inorder(Node* root) { 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() ? "" : ","); + for (size_t i = 0; i < root->point_.data().size(); i++) + std::cout << root->point_.data()[i] << (i + 1 == root->point_.data().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() ? "" : ","); + for (size_t i = 0; i < root->left_->point_.data().size(); i++) + std::cout << root->left_->point_.data()[i] << (i + 1 == root->left_->point_.data().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() ? "" : ","); + for (size_t i = 0; i < root->point_.data().size(); i++) + std::cout << root->point_.data()[i] << (i + 1 == root->point_.data().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() ? "" : ","); + for (size_t i = 0; i < root->right_->point_.data().size(); i++) + std::cout << root->right_->point_.data()[i] << (i + 1 == root->right_->point_.data().size() ? "" : ","); std::cout << "\" [label=\"dir\"];\n"; } From 1284d63ac72ade048ac61bbcdb3bb9180bc16380 Mon Sep 17 00:00:00 2001 From: Pedro Bianchini de Quadros Date: Tue, 9 Sep 2025 14:48:41 -0300 Subject: [PATCH 2/3] chore: update clang-format --- .clang-format | 285 ++++++++++-- examples/main.cpp | 86 ++-- include/mlcpppy/classifiers/classifier.h | 8 +- include/mlcpppy/classifiers/knn.h | 8 +- .../mlcpppy/classifiers/neighbors/kdtree.h | 41 +- .../classifiers/neighbors/nearest_neighbor.h | 15 +- include/mlcpppy/classifiers/neighbors/point.h | 35 +- include/mlcpppy/export.h | 12 +- src/classifiers/neighbors/kdtree.cpp | 415 +++++++++--------- 9 files changed, 548 insertions(+), 357 deletions(-) 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 a1e5a8a..48cd35c 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,55 +1,47 @@ #include + #include "mlcpppy/classifiers/neighbors/kdtree.h" -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::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][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][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 a9bb2d5..d2e8155 100644 --- a/include/mlcpppy/classifiers/neighbors/kdtree.h +++ b/include/mlcpppy/classifiers/neighbors/kdtree.h @@ -17,35 +17,36 @@ #ifndef KDTREE_H #define KDTREE_H -#include #include +#include #include "nearest_neighbor.h" template class KDTree : public NearestNeighbor { - private: - using PointType = typename NearestNeighbor::PointType; - class Node; - Node* root_; - int K_; - std::priority_queue> bests_; + private: + using PointType = typename NearestNeighbor::PointType; + class Node; + Node *root_; + int K_; + std::priority_queue> bests_; - Node* Build(std::vector points, int depth); - Node* NearestNeighbor(Node* root, const PointType& target, int depth); - void KNearestNeighbor(Node* root, const PointType& target, int depth); + Node *Build(std::vector points, int depth); + Node *NearestNeighbor(Node *root, const PointType &target, int depth); + void KNearestNeighbor(Node *root, const PointType &target, int depth); - Node* Closest(Node* n0, Node* n1, const PointType& target); - double DistSquared(const PointType& p0, const PointType& p1); - static void Inorder(Node* root); + Node *Closest(Node *n0, Node *n1, const PointType &target); + double DistSquared(const PointType &p0, const PointType &p1); + static void Inorder(Node *root); - public: - KDTree(); - void Insert(const PointType& point) override; - void BuildTree(const std::vector& points) override; - std::vector KNearestNeighbor(const PointType& target_points, int k) override; - void PrintInorder(); + public: + KDTree(); + void Insert(const PointType &point) override; + void BuildTree(const std::vector &points) override; + std::vector KNearestNeighbor(const PointType &target_points, + int k) override; + void PrintInorder(); - ~KDTree() override; + ~KDTree() override; }; #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 c19ee9b..c00f716 100644 --- a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h +++ b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h @@ -22,12 +22,13 @@ template class NearestNeighbor { - public: - using PointType = Point; - virtual ~NearestNeighbor() = default; - virtual std::vector KNearestNeighbor(const PointType&, int) = 0; - virtual void Insert(const PointType&) = 0; - virtual void BuildTree(const std::vector&) = 0; - virtual void Delete(const PointType&){}; + public: + using PointType = Point; + virtual ~NearestNeighbor() = default; + virtual std::vector KNearestNeighbor(const PointType &, + int) = 0; + virtual void Insert(const PointType &) = 0; + virtual void BuildTree(const std::vector &) = 0; + virtual void Delete(const PointType &) {}; }; #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 index 76b9db9..8435afc 100644 --- a/include/mlcpppy/classifiers/neighbors/point.h +++ b/include/mlcpppy/classifiers/neighbors/point.h @@ -17,28 +17,29 @@ #ifndef POINT_H #define POINT_H -#include +#include #include #include -#include +#include -template ::value>::type> +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"); + 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()); } - std::copy(list.begin(), list.end(), data_.begin()); - } - const std::array& data() const { return data_; } + const std::array &data() const { return data_; } }; - - - #endif \ 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 index 39f8771..44f5d4f 100644 --- a/src/classifiers/neighbors/kdtree.cpp +++ b/src/classifiers/neighbors/kdtree.cpp @@ -16,268 +16,267 @@ */ #include "mlcpppy/classifiers/neighbors/kdtree.h" + #include #include class KDTree::Node { -public: - Node* left_; - Node* right_; - PointType point_; - int depth_; - - explicit Node() {} - explicit Node(PointType point, Node* left, Node* right) : point_(point), left_(left), right_(right) {} - explicit Node(PointType point, Node* left, Node* right, int depth) : point_(point), left_(left), right_(right), depth_(depth) {} - explicit Node(PointType point) : point_(point) { - this->left_ = nullptr; - this->right_ = nullptr; - } - explicit Node(PointType point, int depth) : point_(point), depth_(depth) { - this->left_ = nullptr; - this->right_ = nullptr; - } - - ~Node() { - delete left_; - delete right_; - } + public: + Node *left_; + Node *right_; + PointType point_; + int depth_; + + explicit Node() {} + explicit Node(PointType point, Node *left, Node *right) + : point_(point), left_(left), right_(right) {} + explicit Node(PointType point, Node *left, Node *right, int depth) + : point_(point), left_(left), right_(right), depth_(depth) {} + explicit Node(PointType point) : point_(point) { + this->left_ = nullptr; + this->right_ = nullptr; + } + explicit Node(PointType 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) { +KDTree::Node *KDTree::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 PointType &a, const PointType &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); +} - if (points.empty()) { - return nullptr; - } - int k = points.at(0).data().size(); - int axis = depth % k; +KDTree::KDTree() : root_(nullptr) {} - std::sort(points.begin(), points.end(), [axis](const PointType& a, const PointType& b) { - return a.data()[axis] < b.data()[axis]; - }); +void KDTree::Insert(const PointType &point) { + Node *p = this->root_; + Node *prev = nullptr; - int median = points.size() / 2; - std::vector points_left(points.begin(), points.begin() + median); - std::vector points_right(points.begin() + median + 1, points.end()); + int depth = 0; + int n_dims = point.data().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; + } - return new Node( - points.at(median), - Build(points_left, depth + 1), - Build(points_right, depth + 1), - depth - ); + 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); } -KDTree::KDTree() : root_(nullptr) {} +void KDTree::BuildTree(const std::vector &points) { + if (points.empty()) { + return; + } -void KDTree::Insert(const PointType& point) { - Node* p = this->root_; - Node* prev = nullptr; + int initial_size = points.at(0).data().size(); - int depth = 0; - int n_dims = point.data().size(); + for (auto &point : points) { + if (point.data().empty()) { + return; + } - 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); + if (point.data().size() != initial_size) { + return; + } + } + + this->root_ = Build(points, 0); } -void KDTree::BuildTree(const std::vector& points) { - if (points.empty()) - { - return; - } +void KDTree::KNearestNeighbor(Node *root, const PointType &target, int depth) { + if (root == nullptr) return; - int initial_size = points.at(0).data().size(); + Node *next_branch; + Node *other_branch; - for (auto &point : points) - { - if (point.data().empty()) - { - return; - } + int axis = depth % root->point_.data().size(); - if (point.data().size() != initial_size) - { - return; + 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}); + } - this->root_ = Build(points, 0); -} + double diff = target.data().at(axis) - root->point_.data().at(axis); -void KDTree::KNearestNeighbor(Node* root, const PointType& target, int depth) { - if (root == nullptr) return; - - Node* next_branch; - Node* other_branch; - - int axis = depth % root->point_.data().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); - } + 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, const PointType& target, int depth) { - if (root == nullptr) return nullptr; +KDTree::Node *KDTree::NearestNeighbor(KDTree::Node *root, + const PointType &target, int depth) { + if (root == nullptr) return nullptr; - Node* next_branch; - Node* other_branch; + Node *next_branch; + Node *other_branch; - int axis = depth % root->point_.data().size(); + int axis = depth % root->point_.data().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_); + 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_; + } - double dist = target.data().at(axis) - root->point_.data().at(axis); + Node *temp = NearestNeighbor(next_branch, target, depth + 1); + Node *best = Closest(temp, root, target); + double radius_squared = DistSquared(target, best->point_); - if (radius_squared >= dist * dist) - { - temp = NearestNeighbor(other_branch, target, depth + 1); - best = Closest(temp, best, target); - } + double dist = target.data().at(axis) - root->point_.data().at(axis); - return best; + 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, const PointType& target) { - if (n0 == nullptr) return n1; +KDTree::Node *KDTree::Closest(KDTree::Node *n0, KDTree::Node *n1, + const PointType &target) { + if (n0 == nullptr) return n1; - if (n1 == nullptr) return n0; + if (n1 == nullptr) return n0; - long d1 = DistSquared(n0->point_, target); - long d2 = DistSquared(n1->point_, target); + long d1 = DistSquared(n0->point_, target); + long d2 = DistSquared(n1->point_, target); - if (d1 < d2) - return n0; - else - return n1; + if (d1 < d2) + return n0; + else + return n1; } -double KDTree::DistSquared(const PointType& p0, const PointType& p1) { - long total = 0; - size_t numDims = p0.data().size(); +double KDTree::DistSquared(const PointType &p0, const PointType &p1) { + long total = 0; + size_t numDims = p0.data().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 - } + 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; + return total; } +std::vector KDTree::KNearestNeighbor(const PointType &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; + } -std::vector KDTree::KNearestNeighbor(const PointType& target_points, int k) { + KNearestNeighbor(this->root_, target_points, 0); + std::vector tmp; + while (!this->bests_.empty()) { + tmp.push_back(this->bests_.top().second->point_); + this->bests_.pop(); + } - if (k) - this->K_ = k; + return tmp; +} - if (k == 1){ - Node* result = NearestNeighbor(this->root_, target_points, 0); - std::vector points; - if (result) - { - points.push_back(result->point_); +void KDTree::Inorder(Node *root) { + if (!root) return; + + Inorder(root->left_); + + if (root->left_) { + std::cout << " \""; + for (size_t i = 0; i < root->point_.data().size(); i++) + std::cout + << root->point_.data()[i] + << (i + 1 == root->point_.data().size() ? "" : ","); + std::cout << "\" -> \""; + for (size_t i = 0; i < root->left_->point_.data().size(); i++) + std::cout << root->left_->point_.data()[i] + << (i + 1 == root->left_->point_.data().size() + ? "" + : ","); + std::cout << "\" [label=\"esq\"];\n"; + } + + if (root->right_) { + std::cout << " \""; + for (size_t i = 0; i < root->point_.data().size(); i++) + std::cout + << root->point_.data()[i] + << (i + 1 == root->point_.data().size() ? "" : ","); + std::cout << "\" -> \""; + for (size_t i = 0; i < root->right_->point_.data().size(); i++) + std::cout + << root->right_->point_.data()[i] + << (i + 1 == root->right_->point_.data().size() + ? "" + : ","); + std::cout << "\" [label=\"dir\"];\n"; } - 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_.data().size(); i++) - std::cout << root->point_.data()[i] << (i + 1 == root->point_.data().size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->left_->point_.data().size(); i++) - std::cout << root->left_->point_.data()[i] << (i + 1 == root->left_->point_.data().size() ? "" : ","); - std::cout << "\" [label=\"esq\"];\n"; - } - - if (root->right_) { - std::cout << " \""; - for (size_t i = 0; i < root->point_.data().size(); i++) - std::cout << root->point_.data()[i] << (i + 1 == root->point_.data().size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->right_->point_.data().size(); i++) - std::cout << root->right_->point_.data()[i] << (i + 1 == root->right_->point_.data().size() ? "" : ","); - std::cout << "\" [label=\"dir\"];\n"; - } - - Inorder(root->right_); + 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"; + // 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_; } From 5c414a4b28891d13d664aa2d68d27303eabbd880 Mon Sep 17 00:00:00 2001 From: Pedro Bianchini de Quadros Date: Wed, 10 Sep 2025 09:47:09 -0300 Subject: [PATCH 3/3] feat(NearestNeighbor): new class point --- examples/main.cpp | 6 +- .../mlcpppy/classifiers/neighbors/kdtree.h | 298 ++++++++++++++++-- .../classifiers/neighbors/nearest_neighbor.h | 17 +- include/mlcpppy/classifiers/neighbors/point.h | 7 +- src/classifiers/neighbors/kdtree.cpp | 282 ----------------- 5 files changed, 286 insertions(+), 324 deletions(-) delete mode 100644 src/classifiers/neighbors/kdtree.cpp diff --git a/examples/main.cpp b/examples/main.cpp index 48cd35c..aa82240 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -21,17 +21,17 @@ int main(int argc, char const *argv[]) { {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(); + auto tree = new KDTree(); tree->BuildTree(points); tree->PrintInorder(); - std::vector> points_knn = + 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][j] << " "; + std::cout << points_knn[i].data().at(j) << " "; } std::cout << std::endl; } diff --git a/include/mlcpppy/classifiers/neighbors/kdtree.h b/include/mlcpppy/classifiers/neighbors/kdtree.h index d2e8155..b36405b 100644 --- a/include/mlcpppy/classifiers/neighbors/kdtree.h +++ b/include/mlcpppy/classifiers/neighbors/kdtree.h @@ -17,36 +17,280 @@ #ifndef KDTREE_H #define KDTREE_H -#include #include - +#include +#include #include "nearest_neighbor.h" -template +template class KDTree : public NearestNeighbor { - private: - using PointType = typename NearestNeighbor::PointType; - class Node; - Node *root_; - int K_; - std::priority_queue> bests_; - - Node *Build(std::vector points, int depth); - Node *NearestNeighbor(Node *root, const PointType &target, int depth); - void KNearestNeighbor(Node *root, const PointType &target, int depth); - - Node *Closest(Node *n0, Node *n1, const PointType &target); - double DistSquared(const PointType &p0, const PointType &p1); - static void Inorder(Node *root); - - public: - KDTree(); - void Insert(const PointType &point) override; - void BuildTree(const std::vector &points) override; - std::vector KNearestNeighbor(const PointType &target_points, - int k) override; - void PrintInorder(); - - ~KDTree() override; +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 c00f716..dbb445e 100644 --- a/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h +++ b/include/mlcpppy/classifiers/neighbors/nearest_neighbor.h @@ -17,18 +17,15 @@ #ifndef NEAREST_NEIGHBOR_H #define NEAREST_NEIGHBOR_H #include - #include "point.h" -template +template class NearestNeighbor { - public: - using PointType = Point; - virtual ~NearestNeighbor() = default; - virtual std::vector KNearestNeighbor(const PointType &, - int) = 0; - virtual void Insert(const PointType &) = 0; - virtual void BuildTree(const std::vector &) = 0; - virtual void Delete(const PointType &) {}; + public: + virtual ~NearestNeighbor() = default; + 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 index 8435afc..b012019 100644 --- a/include/mlcpppy/classifiers/neighbors/point.h +++ b/include/mlcpppy/classifiers/neighbors/point.h @@ -22,7 +22,7 @@ #include #include -template ::value>::type> class Point { @@ -40,6 +40,9 @@ class Point { std::copy(list.begin(), list.end(), data_.begin()); } const std::array &data() const { return data_; } + const size_t size() const { + return data_.size(); + } }; -#endif \ No newline at end of file +#endif // POINT_H \ No newline at end of file diff --git a/src/classifiers/neighbors/kdtree.cpp b/src/classifiers/neighbors/kdtree.cpp deleted file mode 100644 index 44f5d4f..0000000 --- a/src/classifiers/neighbors/kdtree.cpp +++ /dev/null @@ -1,282 +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_; - PointType point_; - int depth_; - - explicit Node() {} - explicit Node(PointType point, Node *left, Node *right) - : point_(point), left_(left), right_(right) {} - explicit Node(PointType point, Node *left, Node *right, int depth) - : point_(point), left_(left), right_(right), depth_(depth) {} - explicit Node(PointType point) : point_(point) { - this->left_ = nullptr; - this->right_ = nullptr; - } - explicit Node(PointType 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).data().size(); - int axis = depth % k; - - std::sort(points.begin(), points.end(), - [axis](const PointType &a, const PointType &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); -} - -KDTree::KDTree() : root_(nullptr) {} - -void KDTree::Insert(const PointType &point) { - Node *p = this->root_; - Node *prev = nullptr; - - int depth = 0; - int n_dims = point.data().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 KDTree::BuildTree(const std::vector &points) { - if (points.empty()) { - return; - } - - int initial_size = points.at(0).data().size(); - - for (auto &point : points) { - if (point.data().empty()) { - return; - } - - if (point.data().size() != initial_size) { - return; - } - } - - this->root_ = Build(points, 0); -} - -void KDTree::KNearestNeighbor(Node *root, const PointType &target, int depth) { - if (root == nullptr) return; - - Node *next_branch; - Node *other_branch; - - int axis = depth % root->point_.data().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); - } -} - -KDTree::Node *KDTree::NearestNeighbor(KDTree::Node *root, - const PointType &target, int depth) { - if (root == nullptr) return nullptr; - - Node *next_branch; - Node *other_branch; - - int axis = depth % root->point_.data().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; -} - -KDTree::Node *KDTree::Closest(KDTree::Node *n0, KDTree::Node *n1, - const PointType &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 PointType &p0, const PointType &p1) { - long total = 0; - size_t numDims = p0.data().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; -} - -std::vector KDTree::KNearestNeighbor(const PointType &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_.data().size(); i++) - std::cout - << root->point_.data()[i] - << (i + 1 == root->point_.data().size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->left_->point_.data().size(); i++) - std::cout << root->left_->point_.data()[i] - << (i + 1 == root->left_->point_.data().size() - ? "" - : ","); - std::cout << "\" [label=\"esq\"];\n"; - } - - if (root->right_) { - std::cout << " \""; - for (size_t i = 0; i < root->point_.data().size(); i++) - std::cout - << root->point_.data()[i] - << (i + 1 == root->point_.data().size() ? "" : ","); - std::cout << "\" -> \""; - for (size_t i = 0; i < root->right_->point_.data().size(); i++) - std::cout - << root->right_->point_.data()[i] - << (i + 1 == root->right_->point_.data().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_; }