fix(math): preserve certified sums and complete MKL algebra

This commit is contained in:
KOKO\Mimi
2026-10-14 15:40:52 +09:00
parent 59c8e93b07
commit 590a93793f
9 changed files with 236 additions and 95 deletions
+59 -52
View File
@@ -122,12 +122,11 @@ Result<NodalLift> MakeLift(const Matrix& mass,
.Multiply(basis);
double scale = 0.0;
double basis_error = 0.0;
const auto basis_gram =
dense_blas_internal::MultiplyTransposeLeft(basis, basis);
for (std::size_t i = 0; i < positive; ++i) {
for (std::size_t j = 0; j < positive; ++j) {
double gram = 0.0;
for (std::size_t a = 0; a < 6U; ++a) {
gram += basis(a, i) * basis(a, j);
}
const double gram = basis_gram(i, j);
basis_error = std::hypot(basis_error, gram - (i == j ? 1.0 : 0.0));
scale = (std::max)(scale, std::abs(mp(i, j)));
}
@@ -137,32 +136,34 @@ Result<NodalLift> MakeLift(const Matrix& mass,
return Result<NodalLift>::Failure(Failure("invalid-eigen-mass-space", node,
"Invalid positive mass basis."));
}
Matrix normalized_mp(positive, positive);
for (std::size_t i = 0; i < positive; ++i)
for (std::size_t j = 0; j < positive; ++j) {
normalized_mp(i, j) = mp(i, j) / scale;
if (!std::isfinite(normalized_mp(i, j)))
return Result<NodalLift>::Failure(
Failure("eigen-mass-conditioning", node,
"A positive-space mass pivot is not resolvable."));
}
Matrix lower(positive, positive);
for (std::size_t i = 0; i < positive; ++i) {
for (std::size_t j = 0; j <= i; ++j) {
double value = mp(i, j) / scale;
for (std::size_t k = 0; k < j; ++k) value -= lower(i, k) * lower(j, k);
if (i == j) {
if (!(value > 0.0) || !std::isfinite(value)) {
return Result<NodalLift>::Failure(
Failure("eigen-mass-conditioning", node,
"A positive-space mass pivot is not resolvable."));
}
lower(i, j) = std::sqrt(value);
} else {
lower(i, j) = value / lower(j, j);
}
}
}
Matrix inverse_lower(positive, positive);
for (std::size_t j = 0; j < positive; ++j) {
for (std::size_t i = 0; i < positive; ++i) {
double value = i == j ? 1.0 : 0.0;
for (std::size_t k = 0; k < i; ++k)
value -= lower(i, k) * inverse_lower(k, j);
inverse_lower(i, j) = value / lower(i, i);
}
if (!dense_blas_internal::CholeskyLowerAndInverse(normalized_mp, lower,
inverse_lower)) {
return Result<NodalLift>::Failure(
Failure("eigen-mass-conditioning", node,
"A positive-space mass pivot is not resolvable."));
}
Matrix transposed_lower(positive, positive);
Matrix scaled_inverse_transpose(positive, positive);
const double root_scale = std::sqrt(scale);
for (std::size_t i = 0; i < positive; ++i)
for (std::size_t j = 0; j < positive; ++j) {
transposed_lower(i, j) = lower(j, i);
scaled_inverse_transpose(i, j) = inverse_lower(j, i) / root_scale;
}
const auto inverse_mass = dense_blas_internal::MultiplyTransposeLeft(
inverse_lower, inverse_lower);
const auto rebuilt_mass = lower.Multiply(transposed_lower);
double norm_mp = 0.0;
double norm_inverse = 0.0;
double reconstruction = 0.0;
@@ -172,12 +173,8 @@ Result<NodalLift> MakeLift(const Matrix& mass,
double row_mp = 0.0;
double row_inverse = 0.0;
for (std::size_t j = 0; j < positive; ++j) {
double inverse = 0.0;
double rebuilt = 0.0;
for (std::size_t k = 0; k < positive; ++k) {
inverse += inverse_lower(k, i) * inverse_lower(k, j);
rebuilt += lower(i, k) * lower(j, k);
}
const double inverse = inverse_mass(i, j);
const double rebuilt = rebuilt_mass(i, j);
row_inverse += std::abs(inverse);
row_mp += std::abs(mp(i, j) / scale);
reconstruction = std::hypot(reconstruction, rebuilt - mp(i, j) / scale);
@@ -195,13 +192,9 @@ Result<NodalLift> MakeLift(const Matrix& mass,
"eigen-mass-conditioning", node,
"Nodal mass fails rcond_inf >= 2^-32 or Cholesky reconstruction."));
}
const double root_scale = std::sqrt(scale);
result.scaled_lift = basis.Multiply(scaled_inverse_transpose);
for (std::size_t c = 0; c < 6U; ++c) {
for (std::size_t j = 0; j < positive; ++j) {
for (std::size_t k = 0; k < positive; ++k) {
result.scaled_lift(c, j) +=
basis(c, k) * inverse_lower(j, k) / root_scale;
}
if (!std::isfinite(result.scaled_lift(c, j))) {
return Result<NodalLift>::Failure(Failure(
"eigen-mass-conditioning", node, "Nodal whitening is nonfinite."));
@@ -280,19 +273,23 @@ Result<EigenReduction> BuildEigenReduction(
free_count += lift.Value().record.free_count;
null_count += lift.Value().record.null_count;
const auto& z = lift.Value().null_vector;
Vector null_coordinate{6U};
Matrix null_column{6U, 1U};
for (std::size_t a = 0; a < 6U; ++a) {
null_coordinate[a] = z[a];
null_column(a, 0U) = z[a];
}
const auto mass_null_action = scaled.Multiply(null_coordinate);
const auto positive_null = dense_blas_internal::MultiplyTransposeLeft(
lift.Value().positive_basis, null_column);
for (std::size_t a = 0; a < 6U; ++a) {
null_norm = std::hypot(null_norm, z[a]);
expected_kz_norm = std::hypot(expected_kz_norm, drilling[node] * z[a]);
if (mask[6U * node + a] != 0U) continue;
double action = 0.0;
for (std::size_t b = 0; b < 6U; ++b) action += scaled(a, b) * z[b];
mass_null_error = std::hypot(mass_null_error, action);
mass_null_error = std::hypot(mass_null_error, mass_null_action[a]);
}
for (std::size_t j = 0; j < lift.Value().positive_basis.Columns(); ++j) {
double dot = 0.0;
for (std::size_t a = 0; a < 6U; ++a)
dot += lift.Value().positive_basis(a, j) * z[a];
pz_norm = std::hypot(pz_norm, dot);
pz_norm = std::hypot(pz_norm, positive_null(j, 0U));
}
lifts.push_back(std::move(lift.Value()));
}
@@ -318,10 +315,6 @@ Result<EigenReduction> BuildEigenReduction(
for (std::size_t a = 0; a < 6U; ++a) {
const auto row = node * 6U + a;
if (mask[row] != 0U) continue;
std::map<std::size_t, double> null_action;
if (lifts[node].record.null_count != 0U) {
null_action[node] = -drilling[node] * lifts[node].null_vector[a];
}
for (std::size_t pos = offsets[row]; pos < offsets[row + 1U]; ++pos) {
const auto col = columns[pos];
if (mask[col] != 0U) continue;
@@ -334,14 +327,28 @@ Result<EigenReduction> BuildEigenReduction(
"Scaled stiffness is nonfinite."));
}
stiffness_norm = std::hypot(stiffness_norm, value);
null_action[other] += value * lifts[other].null_vector[b];
auto block = blocks.try_emplace(other, 6U, 6U).first;
block->second(a, b) = value;
}
for (const auto& item : null_action)
kz_error = std::hypot(kz_error, item.second);
}
const auto& left = lifts[node];
std::map<std::size_t, Vector> null_actions;
for (const auto& entry : blocks) {
Vector null_coordinate{6U};
for (std::size_t b = 0; b < 6U; ++b)
null_coordinate[b] = lifts[entry.first].null_vector[b];
null_actions.emplace(entry.first, entry.second.Multiply(null_coordinate));
}
if (left.record.null_count != 0U) {
auto& local = null_actions.try_emplace(node, 6U).first->second;
for (std::size_t a = 0; a < 6U; ++a)
local[a] -= drilling[node] * left.null_vector[a];
}
for (std::size_t a = 0; a < 6U; ++a) {
if (mask[node * 6U + a] != 0U) continue;
for (const auto& action : null_actions)
kz_error = std::hypot(kz_error, action.second[a]);
}
std::map<std::size_t, Matrix> projected_blocks;
for (const auto& entry : blocks) {
projected_blocks.emplace(