Skip to content

Commit 7f80c76

Browse files
committed
Raise an error when the OLS fails in adaptive lasso
1 parent 4eecc73 commit 7f80c76

File tree

1 file changed

+122
-3
lines changed

1 file changed

+122
-3
lines changed

alm/optimize.cpp

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -592,9 +592,48 @@ auto Optimize::run_manual_cv(const std::string &job_prefix, const int maxorder,
592592
A_merged << A, A_validation;
593593
b_merged << b, b_validation;
594594

595-
Eigen::VectorXd x_ols = A_merged.colPivHouseholderQr().solve(b_merged);
595+
// Use QR decomposition with pivoting to check if system is well-determined
596+
Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr_solver(A_merged);
597+
598+
// Check if the system is underdetermined or rank-deficient
599+
const auto rank = qr_solver.rank();
600+
601+
if (rank < N_new) {
602+
std::string error_msg =
603+
"Adaptive lasso failed: The least squares problem is rank-deficient.\n"
604+
" Matrix rank = " + std::to_string(rank) +
605+
", Number of parameters = " + std::to_string(N_new) + "\n"
606+
" This typically occurs when there are too few training data\n"
607+
" or when some parameters cannot be uniquely determined.\n"
608+
" Please try one of the following:\n"
609+
" - Increase the amount of training data (DFSET)\n"
610+
" - Use LMODEL = 1 (least-squares) or 2 (elastic-net) instead\n"
611+
" - Reduce the interaction cutoff distance\n";
612+
ALM_NS::exit("optimize_main", error_msg.c_str());
613+
}
614+
615+
Eigen::VectorXd x_ols = qr_solver.solve(b_merged);
596616
Eigen::VectorXd weight_adalasso = x_ols.cwiseAbs();
597617

618+
// Check if any weights are too small (could cause numerical issues)
619+
const double min_weight_threshold = 1.0e-10;
620+
int n_zero_weights = 0;
621+
for (int i = 0; i < weight_adalasso.size(); ++i) {
622+
if (weight_adalasso[i] < min_weight_threshold) {
623+
n_zero_weights++;
624+
}
625+
}
626+
627+
if (n_zero_weights > 0) {
628+
if (verbosity > 0) {
629+
std::cout << " WARNING: Adaptive lasso detected " << n_zero_weights
630+
<< " near-zero weights (< " << min_weight_threshold << ").\n";
631+
std::cout << " This may indicate an underdetermined or ill-conditioned problem.\n";
632+
std::cout << " The optimization will continue but results may be unreliable.\n";
633+
std::cout << " Consider using LMODEL = 2 (elastic-net) instead.\n\n";
634+
}
635+
}
636+
598637
A = A * weight_adalasso.asDiagonal();
599638
A_validation = A_validation * weight_adalasso.asDiagonal();
600639
}
@@ -716,8 +755,44 @@ auto Optimize::run_auto_cv(const std::string &job_prefix, const int maxorder, co
716755
matrix_train->amat_dense.size() / N_new,
717756
N_new);
718757
Eigen::VectorXd b_full = Eigen::Map<Eigen::VectorXd>(matrix_train->bvec.data(), matrix_train->bvec.size());
719-
Eigen::VectorXd x_ols = A_full.colPivHouseholderQr().solve(b_full);
758+
759+
// Use QR decomposition with pivoting to check if system is well-determined
760+
Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr_solver(A_full);
761+
762+
// Check if the system is underdetermined or rank-deficient
763+
const auto rank = qr_solver.rank();
764+
765+
if (rank < N_new) {
766+
std::string error_msg =
767+
"Adaptive lasso failed in CV: The least squares problem is rank-deficient.\n"
768+
" Matrix rank = " + std::to_string(rank) +
769+
", Number of parameters = " + std::to_string(N_new) + "\n"
770+
" This typically occurs when there are too few training data\n"
771+
" or when some parameters cannot be uniquely determined.\n"
772+
" Please try one of the following:\n"
773+
" - Increase the amount of training data (DFSET)\n"
774+
" - Use LMODEL = 1 (least-squares) or 2 (elastic-net) instead\n"
775+
" - Reduce the interaction cutoff distance\n";
776+
ALM_NS::exit("optimize_main", error_msg.c_str());
777+
}
778+
779+
Eigen::VectorXd x_ols = qr_solver.solve(b_full);
720780
weight_adalasso = x_ols.cwiseAbs();
781+
782+
// Check if any weights are too small (could cause numerical issues)
783+
const double min_weight_threshold = 1.0e-10;
784+
int n_zero_weights = 0;
785+
for (int i = 0; i < weight_adalasso.size(); ++i) {
786+
if (weight_adalasso[i] < min_weight_threshold) {
787+
n_zero_weights++;
788+
}
789+
}
790+
791+
if (n_zero_weights > 0 && verbosity > 0) {
792+
std::cout << " WARNING: Adaptive lasso detected " << n_zero_weights
793+
<< " near-zero weights (< " << min_weight_threshold << ").\n";
794+
std::cout << " This may indicate an underdetermined or ill-conditioned problem.\n\n";
795+
}
721796
}
722797

723798
if (optcontrol.l1_alpha_max <= 0) {
@@ -1273,8 +1348,52 @@ auto Optimize::optimize_with_given_l1alpha(const int maxorder, const size_t M, c
12731348
b = Eigen::Map<Eigen::VectorXd>(matrix_train->bvec.data(), M);
12741349

12751350
if (optcontrol.linear_model == 3) {
1276-
Eigen::VectorXd x_ols = A.colPivHouseholderQr().solve(b);
1351+
// Use QR decomposition with pivoting to check if system is well-determined
1352+
Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr_solver(A);
1353+
1354+
// Check if the system is underdetermined or rank-deficient
1355+
const auto rank = qr_solver.rank();
1356+
1357+
if (rank < N_new) {
1358+
std::string error_msg =
1359+
"Adaptive lasso failed: The least squares problem is rank-deficient.\n"
1360+
" Matrix rank = " + std::to_string(rank) +
1361+
", Number of parameters = " + std::to_string(N_new) + "\n"
1362+
" Number of data points = " + std::to_string(M) + "\n"
1363+
" This typically occurs when there are too few training data\n"
1364+
" or when some parameters cannot be uniquely determined.\n"
1365+
" Please try one of the following:\n"
1366+
" - Increase the amount of training data (DFSET)\n"
1367+
" - Use LMODEL = 1 (least-squares) or 2 (elastic-net) instead\n"
1368+
" - Reduce the interaction cutoff distance\n";
1369+
ALM_NS::exit("optimize_elasticnet", error_msg.c_str());
1370+
}
1371+
1372+
Eigen::VectorXd x_ols = qr_solver.solve(b);
12771373
weight_adalasso = x_ols.cwiseAbs();
1374+
1375+
// Check if any weights are too small (could cause numerical issues)
1376+
const double min_weight_threshold = 1.0e-10;
1377+
int n_zero_weights = 0;
1378+
double max_weight = weight_adalasso.maxCoeff();
1379+
1380+
for (int i = 0; i < weight_adalasso.size(); ++i) {
1381+
if (weight_adalasso[i] < min_weight_threshold) {
1382+
n_zero_weights++;
1383+
}
1384+
}
1385+
1386+
if (n_zero_weights > 0) {
1387+
if (verbosity > 0) {
1388+
std::cout << " WARNING: Adaptive lasso detected " << n_zero_weights
1389+
<< " near-zero weights (< " << min_weight_threshold << ").\n";
1390+
std::cout << " Maximum weight = " << max_weight << "\n";
1391+
std::cout << " This may indicate an underdetermined or ill-conditioned problem.\n";
1392+
std::cout << " The optimization will continue but results may be unreliable.\n";
1393+
std::cout << " Consider using LMODEL = 2 (elastic-net) instead.\n\n";
1394+
}
1395+
}
1396+
12781397
A = A * weight_adalasso.asDiagonal();
12791398
}
12801399

0 commit comments

Comments
 (0)