diff --git a/MLPP/LinAlg/LinAlg.cpp b/MLPP/LinAlg/LinAlg.cpp index 58fcedc..fe98305 100644 --- a/MLPP/LinAlg/LinAlg.cpp +++ b/MLPP/LinAlg/LinAlg.cpp @@ -704,6 +704,34 @@ namespace MLPP{ return true; } + bool LinAlg::negativeDefiniteChecker(std::vector> A){ + auto [eigenvectors, eigenvals] = eig(A); + std::vector eigenvals_vec; + for(int i = 0; i < eigenvals.size(); i++){ + eigenvals_vec.push_back(eigenvals[i][i]); + } + for(int i = 0; i < eigenvals_vec.size(); i++){ + if(eigenvals_vec[i] >= 0){ // Simply check to ensure all eigenvalues are negative. + return false; + } + } + return true; + } + + bool LinAlg::zeroEigenvalue(std::vector> A){ + auto [eigenvectors, eigenvals] = eig(A); + std::vector eigenvals_vec; + for(int i = 0; i < eigenvals.size(); i++){ + eigenvals_vec.push_back(eigenvals[i][i]); + } + for(int i = 0; i < eigenvals_vec.size(); i++){ + if(eigenvals_vec[i] == 0){ + return true; + } + } + return false; + } + void LinAlg::printMatrix(std::vector> A){ for(int i = 0; i < A.size(); i++){ for(int j = 0; j < A[i].size(); j++){ diff --git a/MLPP/LinAlg/LinAlg.hpp b/MLPP/LinAlg/LinAlg.hpp index b032d42..657c465 100644 --- a/MLPP/LinAlg/LinAlg.hpp +++ b/MLPP/LinAlg/LinAlg.hpp @@ -105,6 +105,10 @@ namespace MLPP{ std::vector solve(std::vector> A, std::vector b); bool positiveDefiniteChecker(std::vector> A); + + bool negativeDefiniteChecker(std::vector> A); + + bool zeroEigenvalue(std::vector> A); void printMatrix(std::vector> A); diff --git a/MLPP/NumericalAnalysis/NumericalAnalysis.cpp b/MLPP/NumericalAnalysis/NumericalAnalysis.cpp index a131714..60f99e7 100644 --- a/MLPP/NumericalAnalysis/NumericalAnalysis.cpp +++ b/MLPP/NumericalAnalysis/NumericalAnalysis.cpp @@ -260,4 +260,43 @@ namespace MLPP{ } return laplacian; } + + std::string NumericalAnalysis::secondPartialDerivativeTest(double(*function)(std::vector), std::vector x){ + LinAlg alg; + std::vector> hessianMatrix = hessian(function, x); + /* + The reason we do this is because the 2nd partial derivative test is less conclusive for functions of variables greater than + 2, and the calculations specific to the bivariate case are less computationally intensive. + */ + if(x.size() == 2){ + double det = alg.det(hessianMatrix, hessianMatrix.size()); + double secondDerivative = numDiff_2(function, x, 0, 0); + if(secondDerivative > 0 && det > 0){ + return "min"; + } + else if(secondDerivative < 0 && det > 0){ + return "max"; + } + else if(det < 0){ + return "saddle"; + } + else{ + return "test was inconclusive"; + } + } + else { + if(alg.positiveDefiniteChecker(hessianMatrix)){ + return "min"; + } + else if(alg.negativeDefiniteChecker(hessianMatrix)){ + return "max"; + } + else if(!alg.zeroEigenvalue(hessianMatrix)){ + return "saddle"; + } + else{ + return "test was inconclusive"; + } + } + } } \ No newline at end of file diff --git a/MLPP/NumericalAnalysis/NumericalAnalysis.hpp b/MLPP/NumericalAnalysis/NumericalAnalysis.hpp index ba5f815..d455947 100644 --- a/MLPP/NumericalAnalysis/NumericalAnalysis.hpp +++ b/MLPP/NumericalAnalysis/NumericalAnalysis.hpp @@ -48,6 +48,8 @@ namespace MLPP{ double laplacian(double(*function)(std::vector), std::vector x); // laplacian + std::string secondPartialDerivativeTest(double(*function)(std::vector), std::vector x); + }; }