diff --git a/src/mlpack/methods/det/dtree.hpp b/src/mlpack/methods/det/dtree.hpp index d910af7167e..3ddba58987d 100644 --- a/src/mlpack/methods/det/dtree.hpp +++ b/src/mlpack/methods/det/dtree.hpp @@ -58,6 +58,26 @@ class DTree */ DTree(); + /** + * Create a copy constructor. + */ + DTree(const DTree& obj); + + /** + * Create a copy assignment operator. + */ + DTree& operator=(const DTree& obj); + + /** + * Create a move constructor. + */ + DTree(DTree&& obj); + + /** + * Create a move assignment operator. + */ + DTree& operator=(DTree&& obj); + /** * Create a density estimation tree with the given bounds and the given number * of total points. Children will not be created. diff --git a/src/mlpack/methods/det/dtree_impl.hpp b/src/mlpack/methods/det/dtree_impl.hpp index fae801d623d..86ea4bddce5 100644 --- a/src/mlpack/methods/det/dtree_impl.hpp +++ b/src/mlpack/methods/det/dtree_impl.hpp @@ -165,9 +165,139 @@ DTree::DTree() : right(NULL) { /* Nothing to do. */ } +template +DTree::DTree(const DTree& obj) : + start(obj.start), + end(obj.end), + maxVals(obj.maxVals), + minVals(obj.minVals), + splitDim(obj.splitDim), + splitValue(obj.splitValue), + logNegError(obj.logNegError), + subtreeLeavesLogNegError(obj.subtreeLeavesLogNegError), + subtreeLeaves(obj.subtreeLeaves), + root(obj.root), + ratio(obj.ratio), + logVolume(obj.logVolume), + bucketTag(obj.bucketTag), + alphaUpper(obj.alphaUpper), + left((obj.left == NULL) ? NULL : new DTree(*obj.left)), + right((obj.right == NULL) ? NULL : new DTree(*obj.right)) +{ /* Noting to do. */ } -// Root node initializers +template +DTree& DTree::operator=(const DTree& obj) +{ + //Copying the values from obj + start = obj.start; + end = obj.end; + maxVals = obj.maxVals; + minVals = obj.minVals; + splitDim = obj.splitDim; + splitValue = obj.splitValue; + logNegError = obj.logNegError; + subtreeLeavesLogNegError = obj.subtreeLeavesLogNegError; + subtreeLeaves = obj.subtreeLeaves; + root = obj.root; + ratio = obj.ratio; + logVolume = obj.logVolume; + bucketTag = obj.bucketTag; + alphaUpper = obj.alphaUpper; + + //Free the space allocated + delete left; + delete right; + + //Copying the children + left = ((obj.left == NULL) ? NULL : new DTree(*obj.left)); + left = ((obj.right == NULL) ? NULL : new DTree(*obj.right)); + + return *this; +} + +template +DTree::DTree(DTree&& obj): + start(obj.start), + end(obj.end), + maxVals(std::move(obj.maxVals)), + minVals(std::move(obj.minVals)), + splitDim(obj.splitDim), + splitValue(std::move(obj.splitValue)), + logNegError(obj.logNegError), + subtreeLeavesLogNegError(obj.subtreeLeavesLogNegError), + subtreeLeaves(obj.subtreeLeaves), + root(obj.root), + ratio(obj.ratio), + logVolume(obj.logVolume), + bucketTag(std::move(obj.bucketTag)), + alphaUpper(obj.alphaUpper), + left(obj.left), + right(obj.right) +{ + //Set obj to default values + obj.start = 0; + obj.end = 0; + obj.splitDim = size_t(-1); + obj.splitValue = std::numeric_limits::max(); + obj.logNegError = -DBL_MAX; + obj.subtreeLeavesLogNegError = -DBL_MAX; + obj.subtreeLeaves = 0; + obj.root = true; + obj.ratio = 1.0; + obj.logVolume = -DBL_MAX; + obj.bucketTag = -1; + obj.alphaUpper = 0.0; + obj.left = NULL; + obj.right = NULL; +} + +template +DTree& DTree::operator=(DTree&& obj) +{ + //Moving the values from object + start = obj.start; + end = obj.end; + splitDim = obj.splitDim; + logNegError = obj.logNegError; + subtreeLeavesLogNegError = obj.subtreeLeavesLogNegError; + subtreeLeaves = obj.subtreeLeaves; + root = obj.root; + ratio = obj.ratio; + logVolume = obj.logVolume; + alphaUpper = obj.alphaUpper; + maxVals = std::move(obj.maxVals); + minVals = std::move(obj.minVals); + splitValue = std::move(obj.splitValue); + bucketTag = std::move(obj.bucketTag); + + //Free the space allocated + delete left; + delete right; + + //Moving children + left = obj.left; + right = obj.right; + + //Set obj to default values + obj.start = 0; + obj.end = 0; + obj.splitDim = size_t(-1); + obj.splitValue = std::numeric_limits::max(); + obj.logNegError = -DBL_MAX; + obj.subtreeLeavesLogNegError = -DBL_MAX; + obj.subtreeLeaves = 0; + obj.root = true; + obj.ratio = 1.0; + obj.logVolume = -DBL_MAX; + obj.bucketTag = -1; + obj.alphaUpper = 0.0; + obj.left = NULL; + obj.right = NULL; +} + + +// Root node initializers template DTree::DTree(const StatType& maxVals, const StatType& minVals, @@ -263,8 +393,8 @@ DTree::DTree(const StatType& maxVals, template DTree::~DTree() { - delete left; - delete right; + delete left; + delete right; } // This function computes the log-l2-negative-error of a given node from the diff --git a/src/mlpack/tests/det_test.cpp b/src/mlpack/tests/det_test.cpp index 5bba5aa4751..c4d92836599 100644 --- a/src/mlpack/tests/det_test.cpp +++ b/src/mlpack/tests/det_test.cpp @@ -49,12 +49,12 @@ BOOST_AUTO_TEST_CASE(TestGetMaxMinVals) DTree tree(testData); - BOOST_REQUIRE_EQUAL(tree.maxVals[0], 7); - BOOST_REQUIRE_EQUAL(tree.minVals[0], 3); - BOOST_REQUIRE_EQUAL(tree.maxVals[1], 7); - BOOST_REQUIRE_EQUAL(tree.minVals[1], 0); - BOOST_REQUIRE_EQUAL(tree.maxVals[2], 8); - BOOST_REQUIRE_EQUAL(tree.minVals[2], 1); + BOOST_REQUIRE_EQUAL(tree.MaxVals()[0], 7); + BOOST_REQUIRE_EQUAL(tree.MinVals()[0], 3); + BOOST_REQUIRE_EQUAL(tree.MaxVals()[1], 7); + BOOST_REQUIRE_EQUAL(tree.MinVals()[1], 0); + BOOST_REQUIRE_EQUAL(tree.MaxVals()[2], 8); + BOOST_REQUIRE_EQUAL(tree.MinVals()[2], 1); } BOOST_AUTO_TEST_CASE(TestComputeNodeError) @@ -494,4 +494,275 @@ BOOST_AUTO_TEST_CASE(TestPrintLeafMembership) } */ +// Test the copy constructor and the copy operator. +BOOST_AUTO_TEST_CASE(CopyConstructorAndOperatorTest) +{ + arma::mat testData(3, 5); + + testData << 4 << 5 << 7 << 3 << 5 << arma::endr + << 5 << 0 << 1 << 7 << 1 << arma::endr + << 5 << 6 << 7 << 1 << 8 << arma::endr; + + //Constructing another DTree for testing the children + arma::Col oTest(5); + oTest << 0 << 1 << 2 << 3 << 4; + + DTree *testDTree = new DTree(testData); + testDTree->Grow(testData, oTest, false, 2, 1); + + DTree testDTree2(*testDTree); + DTree testDTree3 = *testDTree; + + double maxVals0 = testDTree->MaxVals()[0]; + double maxVals1 = testDTree->MaxVals()[1]; + double maxVals2 = testDTree->MaxVals()[2]; + double minVals0 = testDTree->MinVals()[0]; + double minVals1 = testDTree->MinVals()[1]; + double minVals2 = testDTree->MinVals()[2]; + + double maxValsL0 = testDTree->Left()->MaxVals()[0]; + double maxValsL1 = testDTree->Left()->MaxVals()[1]; + double maxValsL2 = testDTree->Left()->MaxVals()[2]; + double minValsL0 = testDTree->Left()->MinVals()[0]; + double minValsL1 = testDTree->Left()->MinVals()[1]; + double minValsL2 = testDTree->Left()->MinVals()[2]; + + double maxValsR0 = testDTree->Right()->MaxVals()[0]; + double maxValsR1 = testDTree->Right()->MaxVals()[1]; + double maxValsR2 = testDTree->Right()->MaxVals()[2]; + double minValsR0 = testDTree->Right()->MinVals()[0]; + double minValsR1 = testDTree->Right()->MinVals()[1]; + double minValsR2 = testDTree->Right()->MinVals()[2]; + + //Delete the original tree + delete testDTree; + + //Test the data of copied tree (using copy constructor) + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[0], maxVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[0], minVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[1], maxVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[1], minVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[2], maxVals2); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[2], minVals2); + + //Test the data of copied tree (using copy operator) + BOOST_REQUIRE_EQUAL(testDTree3.MaxVals()[0], maxVals0); + BOOST_REQUIRE_EQUAL(testDTree3.MinVals()[0], minVals0); + BOOST_REQUIRE_EQUAL(testDTree3.MaxVals()[1], maxVals1); + BOOST_REQUIRE_EQUAL(testDTree3.MinVals()[1], minVals1); + BOOST_REQUIRE_EQUAL(testDTree3.MaxVals()[2], maxVals2); + BOOST_REQUIRE_EQUAL(testDTree3.MinVals()[2], minVals2); + + // Test the structure of the tree copied using copy constructor. + BOOST_REQUIRE(testDTree2.Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Right() == NULL); + + // Test the structure of the tree copied using copy operator. + BOOST_REQUIRE(testDTree3.Left()->Left() == NULL); + BOOST_REQUIRE(testDTree3.Left()->Right() == NULL); + BOOST_REQUIRE(testDTree3.Right()->Left()->Left() == NULL); + BOOST_REQUIRE(testDTree3.Right()->Left()->Right() == NULL); + BOOST_REQUIRE(testDTree3.Right()->Right()->Left() == NULL); + BOOST_REQUIRE(testDTree3.Right()->Right()->Right() == NULL); + + //Test data of tree copied using copy constructor. + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[0], maxValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[1], maxValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[2], maxValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[0], minValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[1], minValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[2], minValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[0], maxValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[1], maxValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[2], maxValsR2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[0], minValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[1], minValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[2], minValsR2); + BOOST_REQUIRE(testDTree2.SplitDim() == 2); + BOOST_REQUIRE_CLOSE(testDTree2.SplitValue(), 5.5, 1e-5); + BOOST_REQUIRE(testDTree2.Right()->SplitDim() == 1); + BOOST_REQUIRE_CLOSE(testDTree2.Right()->SplitValue(), 0.5, 1e-5); + + //Test data of tree copied using copy operator. + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MaxVals()[0], maxValsL0); + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MaxVals()[1], maxValsL1); + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MaxVals()[2], maxValsL2); + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MinVals()[0], minValsL0); + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MinVals()[1], minValsL1); + BOOST_REQUIRE_EQUAL(testDTree3.Left()->MinVals()[2], minValsL2); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MaxVals()[0], maxValsR0); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MaxVals()[1], maxValsR1); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MaxVals()[2], maxValsR2); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MinVals()[0], minValsR0); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MinVals()[1], minValsR1); + BOOST_REQUIRE_EQUAL(testDTree3.Right()->MinVals()[2], minValsR2); + BOOST_REQUIRE(testDTree3.SplitDim() == 2); + BOOST_REQUIRE_CLOSE(testDTree3.SplitValue(), 5.5, 1e-5); + BOOST_REQUIRE(testDTree3.Right()->SplitDim() == 1); + BOOST_REQUIRE_CLOSE(testDTree3.Right()->SplitValue(), 0.5, 1e-5); + +} + +// Test the move constructor. +BOOST_AUTO_TEST_CASE(MoveConstructorTest) +{ + arma::mat testData(3, 5); + + testData << 4 << 5 << 7 << 3 << 5 << arma::endr + << 5 << 0 << 1 << 7 << 1 << arma::endr + << 5 << 6 << 7 << 1 << 8 << arma::endr; + + //Constructing another DTree for testing the children + arma::Col oTest(5); + oTest << 0 << 1 << 2 << 3 << 4; + + DTree *testDTree = new DTree(testData); + testDTree->Grow(testData, oTest, false, 2, 1); + + double maxVals0 = testDTree->MaxVals()[0]; + double maxVals1 = testDTree->MaxVals()[1]; + double maxVals2 = testDTree->MaxVals()[2]; + double minVals0 = testDTree->MinVals()[0]; + double minVals1 = testDTree->MinVals()[1]; + double minVals2 = testDTree->MinVals()[2]; + + double maxValsL0 = testDTree->Left()->MaxVals()[0]; + double maxValsL1 = testDTree->Left()->MaxVals()[1]; + double maxValsL2 = testDTree->Left()->MaxVals()[2]; + double minValsL0 = testDTree->Left()->MinVals()[0]; + double minValsL1 = testDTree->Left()->MinVals()[1]; + double minValsL2 = testDTree->Left()->MinVals()[2]; + + double maxValsR0 = testDTree->Right()->MaxVals()[0]; + double maxValsR1 = testDTree->Right()->MaxVals()[1]; + double maxValsR2 = testDTree->Right()->MaxVals()[2]; + double minValsR0 = testDTree->Right()->MinVals()[0]; + double minValsR1 = testDTree->Right()->MinVals()[1]; + double minValsR2 = testDTree->Right()->MinVals()[2]; + + //Construct new tree using move constructor. + DTree testDTree2(std::move(*testDTree)); + + //Delete the original tree + delete testDTree; + + //Test the data of copied tree (using copy constructor) + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[0], maxVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[0], minVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[1], maxVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[1], minVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[2], maxVals2); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[2], minVals2); + + // Test the structure of the tree copied using copy constructor. + BOOST_REQUIRE(testDTree2.Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Right() == NULL); + + //Test data of tree copied using copy constructor. + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[0], maxValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[1], maxValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[2], maxValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[0], minValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[1], minValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[2], minValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[0], maxValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[1], maxValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[2], maxValsR2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[0], minValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[1], minValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[2], minValsR2); + BOOST_REQUIRE(testDTree2.SplitDim() == 2); + BOOST_REQUIRE_CLOSE(testDTree2.SplitValue(), 5.5, 1e-5); + BOOST_REQUIRE(testDTree2.Right()->SplitDim() == 1); + BOOST_REQUIRE_CLOSE(testDTree2.Right()->SplitValue(), 0.5, 1e-5); + +} + +// Test the move operator. +BOOST_AUTO_TEST_CASE(MoveOperatorTest) +{ + arma::mat testData(3, 5); + + testData << 4 << 5 << 7 << 3 << 5 << arma::endr + << 5 << 0 << 1 << 7 << 1 << arma::endr + << 5 << 6 << 7 << 1 << 8 << arma::endr; + + //Constructing another DTree for testing the children + arma::Col oTest(5); + oTest << 0 << 1 << 2 << 3 << 4; + + DTree *testDTree = new DTree(testData); + testDTree->Grow(testData, oTest, false, 2, 1); + + double maxVals0 = testDTree->MaxVals()[0]; + double maxVals1 = testDTree->MaxVals()[1]; + double maxVals2 = testDTree->MaxVals()[2]; + double minVals0 = testDTree->MinVals()[0]; + double minVals1 = testDTree->MinVals()[1]; + double minVals2 = testDTree->MinVals()[2]; + + double maxValsL0 = testDTree->Left()->MaxVals()[0]; + double maxValsL1 = testDTree->Left()->MaxVals()[1]; + double maxValsL2 = testDTree->Left()->MaxVals()[2]; + double minValsL0 = testDTree->Left()->MinVals()[0]; + double minValsL1 = testDTree->Left()->MinVals()[1]; + double minValsL2 = testDTree->Left()->MinVals()[2]; + + double maxValsR0 = testDTree->Right()->MaxVals()[0]; + double maxValsR1 = testDTree->Right()->MaxVals()[1]; + double maxValsR2 = testDTree->Right()->MaxVals()[2]; + double minValsR0 = testDTree->Right()->MinVals()[0]; + double minValsR1 = testDTree->Right()->MinVals()[1]; + double minValsR2 = testDTree->Right()->MinVals()[2]; + + //Construct new tree using move constructor. + DTree testDTree2 = std::move(*testDTree); + + //Delete the original tree + delete testDTree; + + //Test the data of copied tree (using copy constructor) + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[0], maxVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[0], minVals0); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[1], maxVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[1], minVals1); + BOOST_REQUIRE_EQUAL(testDTree2.MaxVals()[2], maxVals2); + BOOST_REQUIRE_EQUAL(testDTree2.MinVals()[2], minVals2); + + // Test the structure of the tree copied using copy constructor. + BOOST_REQUIRE(testDTree2.Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Left()->Right() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Left() == NULL); + BOOST_REQUIRE(testDTree2.Right()->Right()->Right() == NULL); + + //Test data of tree copied using copy constructor. + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[0], maxValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[1], maxValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MaxVals()[2], maxValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[0], minValsL0); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[1], minValsL1); + BOOST_REQUIRE_EQUAL(testDTree2.Left()->MinVals()[2], minValsL2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[0], maxValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[1], maxValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MaxVals()[2], maxValsR2); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[0], minValsR0); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[1], minValsR1); + BOOST_REQUIRE_EQUAL(testDTree2.Right()->MinVals()[2], minValsR2); + BOOST_REQUIRE(testDTree2.SplitDim() == 2); + BOOST_REQUIRE_CLOSE(testDTree2.SplitValue(), 5.5, 1e-5); + BOOST_REQUIRE(testDTree2.Right()->SplitDim() == 1); + BOOST_REQUIRE_CLOSE(testDTree2.Right()->SplitValue(), 0.5, 1e-5); + +} + BOOST_AUTO_TEST_SUITE_END();