Add bounded coupled damping and prediction-based attempt selection
This commit is contained in:
@@ -26,6 +26,7 @@ import mean_field;
|
||||
#include "plateau_nonlinear_step_diagnostics.hpp"
|
||||
#include "plateau_coupled_trial_diagnostics.hpp"
|
||||
#include "plateau_coupled_trial_physical.hpp"
|
||||
#include "plateau_damped_coupled_diagnostics.hpp"
|
||||
#include "plateau_gravity_application_diagnostics.hpp"
|
||||
#include "plateau_positivity_strategy_diagnostics.hpp"
|
||||
#include "plateau_polynomial_certificate.hpp"
|
||||
@@ -40,6 +41,10 @@ struct Options {
|
||||
bool strategyProbe = false, positivityProbe = false, positivityCertificate = false;
|
||||
bool preconditionerProbe = false, nonlinearStepProbe = false;
|
||||
bool coupledTrialProbe = false, coupledSeedControl = false;
|
||||
bool dampedCoupledProbe = false;
|
||||
std::string coupledSearch = "near";
|
||||
int coupledBetaSteps = 4;
|
||||
double coupledBetaReduction = .5, coupledExcessRatio = 1.;
|
||||
double coupledTolerance = .03, coupledMaximumRatio = 1.;
|
||||
int coupledIterations = 160, coupledRestart = 160, coupledAttempts = 1;
|
||||
std::string preconditionerVariant = "independent";
|
||||
@@ -108,6 +113,16 @@ Options Parse(int argc, char **argv) {
|
||||
o.preconditionerProbe = true;
|
||||
else if (a == "--nonlinear-step-probe")
|
||||
o.nonlinearStepProbe = true;
|
||||
else if (a == "--damped-coupled-probe")
|
||||
o.dampedCoupledProbe = true;
|
||||
else if (a == "--coupled-search")
|
||||
o.coupledSearch = v();
|
||||
else if (a == "--coupled-beta-steps")
|
||||
o.coupledBetaSteps = std::stoi(v());
|
||||
else if (a == "--coupled-beta-reduction")
|
||||
o.coupledBetaReduction = std::stod(v());
|
||||
else if (a == "--coupled-excess-ratio")
|
||||
o.coupledExcessRatio = std::stod(v());
|
||||
else if (a == "--coupled-trial-probe")
|
||||
o.coupledTrialProbe = true;
|
||||
else if (a == "--coupled-seed-control")
|
||||
@@ -158,17 +173,17 @@ Options Parse(int argc, char **argv) {
|
||||
throw std::invalid_argument("Invalid options");
|
||||
if (o.probeRestart != 40 && o.probeRestart != 160)
|
||||
throw std::invalid_argument("--probe-restart must be 40 or 160");
|
||||
if ((o.pressureProbe || o.shapeCurvature || o.surfaceTrace || o.strategyProbe || o.positivityProbe || o.coupledTrialProbe) && (o.probeState.empty() || o.probeDirection.empty()))
|
||||
if ((o.pressureProbe || o.shapeCurvature || o.surfaceTrace || o.strategyProbe || o.positivityProbe || o.coupledTrialProbe || o.dampedCoupledProbe) && (o.probeState.empty() || o.probeDirection.empty()))
|
||||
throw std::invalid_argument("Focused probes require --probe-state and --probe-direction");
|
||||
if (o.positivityCertificate && o.probeState.empty())
|
||||
throw std::invalid_argument("--positivity-certificate requires --probe-state");
|
||||
if (o.positivityCertificate && (o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
|
||||
o.strategyProbe || o.positivityProbe || o.preconditionerProbe || o.nonlinearStepProbe || o.coupledTrialProbe || !o.probeDirection.empty() || !o.replayCandidate.empty()))
|
||||
o.strategyProbe || o.positivityProbe || o.preconditionerProbe || o.nonlinearStepProbe || o.coupledTrialProbe || o.dampedCoupledProbe || !o.probeDirection.empty() || !o.replayCandidate.empty()))
|
||||
throw std::invalid_argument("--positivity-certificate is a certificate-only mode; omit other probe options");
|
||||
if (o.preconditionerProbe) {
|
||||
experiment::plateau_diagnostics::ParsePreconditionerBenchmarkVariant(o.preconditionerVariant);
|
||||
if (o.maxNewton != 0 || o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
|
||||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || o.nonlinearStepProbe || o.coupledTrialProbe || !o.probeDirection.empty() ||
|
||||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || o.nonlinearStepProbe || o.coupledTrialProbe || o.dampedCoupledProbe || !o.probeDirection.empty() ||
|
||||
!o.replayCandidate.empty() || !o.captureIterations.empty() || o.strategy != "baseline")
|
||||
throw std::invalid_argument("--preconditioner-probe requires --max-newton 0 and no nonlinear/probe strategies");
|
||||
if (!std::isfinite(o.linearTolerance) || o.linearTolerance <= 0.0)
|
||||
@@ -176,22 +191,25 @@ Options Parse(int argc, char **argv) {
|
||||
}
|
||||
if (o.nonlinearStepProbe) {
|
||||
if (o.maxNewton != 0 || o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
|
||||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || o.preconditionerProbe || o.coupledTrialProbe ||
|
||||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || o.preconditionerProbe || o.coupledTrialProbe || o.dampedCoupledProbe ||
|
||||
!o.probeDirection.empty() || !o.replayCandidate.empty() || !o.captureIterations.empty() ||
|
||||
o.strategy != "baseline")
|
||||
throw std::invalid_argument("--nonlinear-step-probe requires --max-newton 0 and no other probes or strategy; it compares raw and curvature paths itself");
|
||||
if (!std::isfinite(o.linearTolerance) || o.linearTolerance <= 0.0)
|
||||
throw std::invalid_argument("A nonlinear-step probe requires a finite positive --linear-tol");
|
||||
}
|
||||
if (o.coupledTrialProbe) {
|
||||
if (o.coupledTrialProbe || o.dampedCoupledProbe) {
|
||||
if (o.maxNewton != 0 || o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace ||
|
||||
o.strategyProbe || o.positivityProbe || o.positivityCertificate || o.preconditionerProbe ||
|
||||
o.nonlinearStepProbe || !o.replayCandidate.empty() || !o.captureIterations.empty() ||
|
||||
o.coupledAttempts != 1 || (o.strategy != "coupled" && o.strategy != "curvature-coupled"))
|
||||
o.nonlinearStepProbe || (o.coupledTrialProbe && o.dampedCoupledProbe) || !o.replayCandidate.empty() || !o.captureIterations.empty() ||
|
||||
o.coupledAttempts != 1 || (o.strategy != "coupled" && o.strategy != "curvature-coupled" && o.strategy != "backtracked-coupled" && o.strategy != "curvature-backtracked-coupled"))
|
||||
throw std::invalid_argument("--coupled-trial-probe requires --max-newton 0 and strategy coupled or curvature-coupled, without other probes");
|
||||
if (o.coupledTrialProbe && o.strategy != "coupled" && o.strategy != "curvature-coupled")
|
||||
throw std::invalid_argument("The independent coupled probe requires its original full-step strategy");
|
||||
if (!std::isfinite(o.linearTolerance) || o.linearTolerance <= 0.)
|
||||
throw std::invalid_argument("A coupled trial probe requires a finite positive outer --linear-tol");
|
||||
} else if (o.coupledSeedControl) {
|
||||
}
|
||||
if (o.coupledSeedControl && !o.coupledTrialProbe) {
|
||||
throw std::invalid_argument("--coupled-seed-control requires --coupled-trial-probe");
|
||||
}
|
||||
if (!o.preconditionerProbe) {
|
||||
@@ -200,12 +218,12 @@ Options Parse(int argc, char **argv) {
|
||||
throw std::invalid_argument("Newton preconditioner variants are independent, gravity-schur, stellar-ldu, or stellar-ldu-gravity-schur");
|
||||
if (o.preconditionerVariant != "independent") {
|
||||
if (o.probes || o.pressureProbe || o.shapeCurvature || o.surfaceTrace || o.strategyProbe ||
|
||||
o.positivityProbe || o.positivityCertificate || (!o.probeState.empty() && !o.nonlinearStepProbe && !o.coupledTrialProbe) ||
|
||||
(!o.probeDirection.empty() && !o.coupledTrialProbe) ||
|
||||
o.positivityProbe || o.positivityCertificate || (!o.probeState.empty() && !o.nonlinearStepProbe && !o.coupledTrialProbe && !o.dampedCoupledProbe) ||
|
||||
(!o.probeDirection.empty() && !o.coupledTrialProbe && !o.dampedCoupledProbe) ||
|
||||
!o.replayCandidate.empty())
|
||||
throw std::invalid_argument("A custom Newton preconditioner cannot be combined with frozen or certificate probes");
|
||||
if (o.strategy != "baseline" && o.strategy != "positivity" && o.strategy != "curvature" &&
|
||||
o.strategy != "positivity-curvature" && o.strategy != "coupled" && o.strategy != "curvature-coupled")
|
||||
o.strategy != "positivity-curvature" && o.strategy != "coupled" && o.strategy != "curvature-coupled" && o.strategy != "backtracked-coupled" && o.strategy != "curvature-backtracked-coupled")
|
||||
throw std::invalid_argument("Unsupported custom Newton preconditioner strategy");
|
||||
if (!std::isfinite(o.linearTolerance) || o.linearTolerance <= 0.0 || o.schurCorrectionSweeps < 0 ||
|
||||
!std::isfinite(o.schurRelaxation) || o.schurRelaxation <= 0.0)
|
||||
@@ -216,13 +234,19 @@ Options Parse(int argc, char **argv) {
|
||||
throw std::invalid_argument("A supplied direction or candidate requires --probe-state");
|
||||
if (o.strategy != "baseline" && o.strategy != "positivity" && o.strategy != "curvature" &&
|
||||
o.strategy != "reduced" && o.strategy != "positivity-curvature" && o.strategy != "positivity-reduced" &&
|
||||
o.strategy != "coupled" && o.strategy != "curvature-coupled")
|
||||
o.strategy != "coupled" && o.strategy != "curvature-coupled" && o.strategy != "backtracked-coupled" && o.strategy != "curvature-backtracked-coupled")
|
||||
throw std::invalid_argument("Unknown numerical strategy");
|
||||
if (!std::isfinite(o.gravityTolerance) || o.gravityTolerance <= 0.0 || o.gravityIterations < 1 || o.gravityRestart < 1)
|
||||
throw std::invalid_argument("Invalid gravity solve controls");
|
||||
if (!std::isfinite(o.coupledTolerance) || o.coupledTolerance <= 0. || o.coupledIterations < 1 ||
|
||||
o.coupledRestart < 1 || !std::isfinite(o.coupledMaximumRatio) || o.coupledMaximumRatio <= 0. || o.coupledAttempts < 1)
|
||||
throw std::invalid_argument("Invalid coupled correction controls");
|
||||
if (o.coupledSearch != "none" && o.coupledSearch != "full" && o.coupledSearch != "damped" && o.coupledSearch != "near")
|
||||
throw std::invalid_argument("Unknown coupled search mode");
|
||||
if (!o.dampedCoupledProbe && o.coupledSearch != "near")
|
||||
throw std::invalid_argument("--coupled-search selects controls only in --damped-coupled-probe");
|
||||
solver::BacktrackedCoupledStep{.maximumSteps=o.coupledBetaSteps, .reduction=o.coupledBetaReduction}.Validate();
|
||||
solver::NearAcceptanceCoupledAttempt{.maximumExcessRatio=o.coupledExcessRatio}.Validate();
|
||||
if (std::filesystem::exists(o.output))
|
||||
throw std::runtime_error("Output directory already exists");
|
||||
std::filesystem::create_directories(o.output);
|
||||
@@ -488,7 +512,7 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
<< "\nM=1\nR_reference=1\nxi1=" << scales.xi << "\nmu1=" << scales.mu << "\ncentral_phase=" << !o.noCentral
|
||||
<< "\nrestart=" << o.restart << "\nprobe_restart=" << o.probeRestart
|
||||
<< "\nfrozen_probe_backend=" << (o.preconditionerProbe ? "direct_mfem_fgmres_preconditioner_benchmark" :
|
||||
o.coupledTrialProbe ? "supplied_outer_direction_verified_with_full_J_and_auxiliary_fgmres" :
|
||||
(o.coupledTrialProbe || o.dampedCoupledProbe) ? "supplied_outer_direction_verified_with_full_J_and_auxiliary_fgmres" :
|
||||
o.nonlinearStepProbe ? "prepared_fgmres_cold_with_independent_true_gate" :
|
||||
o.positivityCertificate ? "polynomial_certificate_no_linear_solve" :
|
||||
o.probeDirection.empty() ? "direct_mfem_fgmres_cold" : "supplied_direction_no_linear_solve")
|
||||
@@ -498,6 +522,11 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
<< "\ngravity_iterations=" << o.gravityIterations << "\ngravity_restart=" << o.gravityRestart
|
||||
<< "\npreconditioner_probe=" << o.preconditionerProbe
|
||||
<< "\nnonlinear_step_probe=" << o.nonlinearStepProbe
|
||||
<< "\ndamped_coupled_probe=" << o.dampedCoupledProbe
|
||||
<< "\ncoupled_search=" << o.coupledSearch
|
||||
<< "\ncoupled_beta_steps=" << o.coupledBetaSteps
|
||||
<< "\ncoupled_beta_reduction=" << o.coupledBetaReduction
|
||||
<< "\ncoupled_excess_ratio=" << o.coupledExcessRatio
|
||||
<< "\ncoupled_trial_probe=" << o.coupledTrialProbe
|
||||
<< "\ncoupled_seed_control=" << o.coupledSeedControl
|
||||
<< "\ncoupled_tolerance=" << o.coupledTolerance
|
||||
@@ -513,6 +542,34 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
<< "\nschur_correction_sweeps=" << o.schurCorrectionSweeps << "\nschur_relaxation=" << o.schurRelaxation
|
||||
<< "\ncompiler=" << __VERSION__ << "\ncompiled=" << __DATE__ << ' ' << __TIME__ << '\n';
|
||||
metadata.flush();
|
||||
if (o.dampedCoupledProbe) {
|
||||
metadata << "coupled_trial_input=physical_snapshot_and_supplied_normalized_direction\n"
|
||||
<< "coupled_trial_admissibility=sampled_enthalpy_without_repair\n"
|
||||
<< "coupled_trial_alphas_are_independent=0\n"
|
||||
<< "probe_state=" << o.probeState << "\nprobe_direction=" << o.probeDirection << '\n';
|
||||
metadata.flush();
|
||||
solver::detail::StellarEquilibriumContextDiagnostics::WithState(context, [&](auto &s, const fem::FEM &f) {
|
||||
const auto physical = LoadVector(o.probeState, s.AcceptedPhysicalState().Size());
|
||||
mfem::Vector base;
|
||||
s.normalizedOperator->NormalizeState(physical, base);
|
||||
const auto direction = LoadVector(o.probeDirection, base.Size());
|
||||
experiment::plateau_diagnostics::ProbeDampedCoupled(s, base, direction, o.output, {
|
||||
.common = {.outerLinearTolerance = o.linearTolerance,
|
||||
.gravity = {.control = {.relativeTolerance = o.gravityTolerance, .absoluteTolerance = 1e-14,
|
||||
.maximumIterations = o.gravityIterations},
|
||||
.backend = {.restartLength = o.gravityRestart, .printLevel = -1}},
|
||||
.coupled = {.control = {.relativeTolerance = o.coupledTolerance, .absoluteTolerance = 1e-14,
|
||||
.maximumIterations = o.coupledIterations},
|
||||
.backend = {.restartLength = o.coupledRestart, .printLevel = -1},
|
||||
.maximumCorrectionRatio = o.coupledMaximumRatio},
|
||||
.gravityPreconditionPath = o.strategy.starts_with("curvature-")},
|
||||
.mode = o.coupledSearch,
|
||||
.search = {.maximumSteps = o.coupledBetaSteps, .reduction = o.coupledBetaReduction},
|
||||
.attempt = {.maximumExcessRatio = o.coupledExcessRatio}});
|
||||
experiment::plateau_diagnostics::MeasureCoupledTrialSnapshots(s, f, o.output, o.n, scales.K);
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
if (o.coupledTrialProbe) {
|
||||
metadata << "coupled_trial_input=physical_snapshot_and_supplied_normalized_direction\n"
|
||||
<< "coupled_trial_admissibility=sampled_enthalpy_without_repair\n"
|
||||
@@ -675,6 +732,10 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
"preparation_seconds_total,refresh_seconds_total,original_merit,corrected_merit,has_solve,"
|
||||
"linear_status,linear_iterations,rhs_norm,reported_true_norm,verified_true_norm,threshold,verified_gate,"
|
||||
"correction_norm,maximum_correction_norm\n";
|
||||
auto coupledSearchLog = File(o.output / "coupled_search.csv");
|
||||
coupledSearchLog << "iteration,trial,alpha,status,deferred_total,selected_beta,candidate_count,base_merit,predicted_merit,predicted_decrease,excess_ratio\n";
|
||||
auto coupledCandidatesLog = File(o.output / "coupled_candidates.csv");
|
||||
coupledCandidatesLog << "iteration,trial,candidate,beta,status,merit,accepted,preparation_seconds\n";
|
||||
auto stateConsistency = File(o.output / "trial_state_consistency.csv");
|
||||
stateConsistency << "iteration,trial,normalized_physical_roundtrip_error,relative_error\n";
|
||||
mfem::Vector captureBase;
|
||||
@@ -721,6 +782,16 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
coupledLog << e.iteration << ',' << e.trial << ',' << e.stepLength << ',' << (e.numericalStrategies != nullptr);
|
||||
if (e.numericalStrategies) {
|
||||
const auto &c = e.numericalStrategies->coupledTrial;
|
||||
coupledSearchLog << e.iteration << ',' << e.trial << ',' << e.stepLength << ',' << int(c.status) << ','
|
||||
<< c.deferredAttempts << ',' << c.selectedBeta << ',' << c.candidateCount << ',' << c.baseMerit << ','
|
||||
<< c.linearPredictionMerit << ',' << c.predictedDecrease << ',' << c.predictionExcessRatio << '\n';
|
||||
int candidateIndex = 0;
|
||||
for (const auto &candidate : c.candidates)
|
||||
coupledCandidatesLog << e.iteration << ',' << e.trial << ',' << candidateIndex++ << ','
|
||||
<< candidate.beta << ',' << int(candidate.status) << ',' << candidate.merit << ','
|
||||
<< candidate.accepted << ',' << candidate.preparationSeconds << '\n';
|
||||
coupledSearchLog.flush();
|
||||
coupledCandidatesLog.flush();
|
||||
coupledLog << ',' << static_cast<int>(c.status) << ',' << c.attempts << ',' << c.kept << ','
|
||||
<< c.fallbacks << ',' << c.iterations << ',' << c.preparations << ',' << c.restorations << ','
|
||||
<< c.solveSeconds << ',' << c.verificationSeconds << ',' << c.preparationSeconds << ','
|
||||
@@ -836,6 +907,12 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
auto report = [&] {
|
||||
if (o.strategy == "positivity") return run(nl::NewtonNumerics{nl::PolynomialEnthalpy{}, nl::MonolithicGravity{}});
|
||||
if (o.strategy == "curvature") return run(nl::NewtonNumerics{nl::SampledEnthalpy{}, nl::GravityCurvatureCorrection{gravity}});
|
||||
const nl::BacktrackedCoupledCurvatureCorrection backtracked{
|
||||
.correction = coupled.correction, .maximumAttemptsPerIteration = o.coupledAttempts,
|
||||
.search = {.maximumSteps = o.coupledBetaSteps, .reduction = o.coupledBetaReduction},
|
||||
.attempt = {.maximumExcessRatio = o.coupledExcessRatio}};
|
||||
if (o.strategy == "backtracked-coupled") return run(nl::NewtonNumerics{nl::SampledEnthalpy{}, nl::MonolithicGravity{}, backtracked});
|
||||
if (o.strategy == "curvature-backtracked-coupled") return run(nl::NewtonNumerics{nl::SampledEnthalpy{}, nl::GravityCurvatureCorrection{gravity}, backtracked});
|
||||
if (o.strategy == "coupled") return run(nl::NewtonNumerics{nl::SampledEnthalpy{}, nl::MonolithicGravity{}, coupled});
|
||||
if (o.strategy == "curvature-coupled") return run(nl::NewtonNumerics{nl::SampledEnthalpy{}, nl::GravityCurvatureCorrection{gravity}, coupled});
|
||||
if constexpr (FullDefaultStack)
|
||||
@@ -872,6 +949,7 @@ int RunContext(Context &context, const Options &o, const Scales &scales) {
|
||||
metric("last_lifted_relative_true_residual", strategyDiagnostics.lastLiftedRelativeTrueResidual);
|
||||
const auto &coupledDiagnostics = strategyDiagnostics.coupledTrial;
|
||||
metric("coupled_attempts", coupledDiagnostics.attempts);
|
||||
metric("coupled_deferred_attempts", coupledDiagnostics.deferredAttempts);
|
||||
metric("coupled_kept", coupledDiagnostics.kept);
|
||||
metric("coupled_fallbacks", coupledDiagnostics.fallbacks);
|
||||
metric("coupled_iterations", coupledDiagnostics.iterations);
|
||||
|
||||
Reference in New Issue
Block a user