diff --git a/src/mlpack/methods/lars/lars.cpp b/src/mlpack/methods/lars/lars.cpp index 848f9ebb67..df0ce98f45 100644 --- a/src/mlpack/methods/lars/lars.cpp +++ b/src/mlpack/methods/lars/lars.cpp @@ -68,6 +68,120 @@ LARS::LARS(const arma::mat& data, Train(data, responses, transposeData); } +// Copy Constructor. +LARS::LARS(const LARS& other) : + matGramInternal(other.matGramInternal), + matGram(&matGramInternal), + matUtriCholFactor(other.matUtriCholFactor), + useCholesky(other.useCholesky), + lasso(other.lasso), + lambda1(other.lambda1), + lambda2(other.lambda2), + elasticNet(other.elasticNet), + tolerance(other.tolerance), + betaPath(other.betaPath), + lambdaPath(other.lambdaPath), + activeSet(other.activeSet), + isActive(other.isActive), + ignoreSet(other.ignoreSet), + isIgnored(other.isIgnored) +{ + // Nothing to do here. +} + +// Move constructor. +LARS::LARS(LARS&& other) : + matGramInternal(std::move(other.matGramInternal)), + matGram(other.matGram), + matUtriCholFactor(std::move(other.matUtriCholFactor)), + useCholesky(other.useCholesky), + lasso(other.lasso), + lambda1(other.lambda1), + lambda2(other.lambda2), + elasticNet(other.elasticNet), + tolerance(other.tolerance), + betaPath(std::move(other.betaPath)), + lambdaPath(std::move(other.lambdaPath)), + activeSet(std::move(other.activeSet)), + isActive(std::move(other.isActive)), + ignoreSet(std::move(other.ignoreSet)), + isIgnored(std::move(other.isIgnored)) +{ + // Clean the other object to prevent to objects pointing + // at the memory location. + if (other.matGram) + delete other.matGram; + + other.matGram = new arma::mat(other.matGramInternal); + other.lambda1 = 0.0; + other.lambda2 = 0.0; +} + +// Copy operator. +LARS& LARS::operator=(const LARS& other) +{ + if (&other == this) + return *this; + + // Clean the memory first. + if (matGram) + delete matGram; + + matGramInternal = other.matGramInternal; + matGram = &matGramInternal; + matUtriCholFactor = other.matUtriCholFactor; + useCholesky = other.useCholesky; + lasso = other.lasso; + lambda1 = other.lambda1; + lambda2 = other.lambda2; + elasticNet = other.elasticNet; + tolerance = other.tolerance; + betaPath = other.betaPath; + lambdaPath = other.lambdaPath; + activeSet = other.activeSet; + isActive = other.isActive; + ignoreSet = other.ignoreSet; + isIgnored = other.isIgnored; + return *this; +} + +// Move Operator. +LARS& LARS::operator=(LARS&& other) +{ + if (&other == this) + return *this; + + // Clean the memory first. + if (matGram) + delete matGram; + + matGramInternal = std::move(other.matGramInternal); + matGram = other.matGram; + matUtriCholFactor = std::move(other.matUtriCholFactor); + useCholesky = other.useCholesky; + lasso = other.lasso; + lambda1 = other.lambda1; + lambda2 = other.lambda2; + elasticNet = other.elasticNet; + tolerance = other.tolerance; + betaPath = std::move(other.betaPath); + lambdaPath = std::move(other.lambdaPath); + activeSet = std::move(other.activeSet); + isActive = std::move(other.isActive); + ignoreSet = std::move(other.ignoreSet); + isIgnored = std::move(other.isIgnored); + + // Clean the other object to prevent to objects pointing + // at the memory location. + if (other.matGram) + delete other.matGram; + + other.matGram = new arma::mat(other.matGramInternal); + other.lambda1 = 0.0; + other.lambda2 = 0.0; + return *this; +} + double LARS::Train(const arma::mat& matX, const arma::rowvec& y, arma::vec& beta, diff --git a/src/mlpack/methods/lars/lars.hpp b/src/mlpack/methods/lars/lars.hpp index 9d7aa24ada..da3de1fdfd 100644 --- a/src/mlpack/methods/lars/lars.hpp +++ b/src/mlpack/methods/lars/lars.hpp @@ -170,6 +170,34 @@ class LARS const double lambda2 = 0.0, const double tolerance = 1e-16); + /** + * Construct the LARS object by copying the given LARS object. + * + * @param other LARS object to copy. + */ + LARS(const LARS& other); + + /** + * Construct the LARS object by taking ownership of the given LARS object. + * + * @param other LARS object to take ownership of. + */ + LARS(LARS&& other); + + /** + * Copy the given LARS object. + * + * @param other LARS object to copy. + */ + LARS &operator=(const LARS& other); + + /** + * Take ownership of the given LARS object. + * + * @param other LARS object to take ownership of. + */ + LARS &operator=(LARS&& other); + /** * Run LARS. The input matrix (like all mlpack matrices) should be * column-major -- each column is an observation and each row is a dimension. diff --git a/src/mlpack/tests/lars_test.cpp b/src/mlpack/tests/lars_test.cpp index 6b7edc267e..f2eb89b230 100644 --- a/src/mlpack/tests/lars_test.cpp +++ b/src/mlpack/tests/lars_test.cpp @@ -421,4 +421,36 @@ BOOST_AUTO_TEST_CASE(LARSTestComputeError) BOOST_REQUIRE_EQUAL(cost == train1, true); } +BOOST_AUTO_TEST_CASE(LARSCopyConstructorTest) +{ + arma::mat features, Y; + arma::rowvec targets; + data::Load("lars_dependent_x.csv", features); + data::Load("lars_dependent_y.csv", Y); + targets = Y.row(0); + mlpack::regression::LARS* glm1 = new mlpack::regression::LARS(false, .1, .1); + arma::rowvec predictions, predictionsFromCopiedModel; + std::vector models; + glm1->Train(features, targets); + glm1->Predict(features, predictions); + models.emplace_back(*glm1); + delete glm1; // Free LARS internal memory. + models[0].Predict(features, predictionsFromCopiedModel); + CheckMatrices(predictions, predictionsFromCopiedModel); + // Check if we can train the model again. + BOOST_REQUIRE_NO_THROW(models[0].Train(features, targets)); + + // Check for object. + mlpack::regression::LARS glm2(false, 0.1, 0.1); + models.emplace_back(glm2); + BOOST_REQUIRE_NO_THROW(glm2.Train(features, targets)); + BOOST_REQUIRE_NO_THROW(models[1].Train(features, targets)); + + // Check assignment operator. + mlpack::regression::LARS glm3 = glm2; + models[1].Predict(features, predictions); + glm3.Predict(features, predictionsFromCopiedModel); + CheckMatrices(predictions, predictionsFromCopiedModel); +} + BOOST_AUTO_TEST_SUITE_END();