Files
FESADev/tests/reference/reference_tolerance_policy.cpp
T
2026-08-18 02:29:26 +09:00

116 lines
4.2 KiB
C++

#include "reference_tolerance_policy.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace fesa::test {
namespace {
bool IsFiniteFamily(const ReferenceToleranceFamily& family) {
if (family.identity.empty() || family.components.empty() ||
family.rows.empty()) {
return false;
}
return std::all_of(family.rows.begin(), family.rows.end(),
[](const ReferenceToleranceRow& row) {
return std::isfinite(row.fesa_value) &&
std::isfinite(row.reference_value);
});
}
}
ReferenceToleranceEvaluation ReferenceTolerancePolicy::Evaluate(
const std::vector<ReferenceToleranceFamily>& families) {
ReferenceToleranceEvaluation evaluation{true, {}, true, {}};
evaluation.families.reserve(families.size());
for (const auto& family : families) {
if (!IsFiniteFamily(family)) {
return {false, "nonfinite-comparison-value", false, {}};
}
ReferenceToleranceFamilyDecision decision{};
decision.identity = family.identity;
decision.components = family.components;
decision.blocking = family.blocking;
decision.row_count = family.rows.size();
decision.rows.reserve(family.rows.size());
for (const auto& row : family.rows) {
decision.reference_scale =
(std::max)(decision.reference_scale, std::abs(row.reference_value));
}
decision.near_zero_band = kNearZeroRatio * decision.reference_scale;
double error_norm = 0.0;
for (std::size_t index = 0U; index < family.rows.size(); ++index) {
const auto& row = family.rows[index];
const double absolute_error =
std::abs(row.fesa_value - row.reference_value);
if (!std::isfinite(absolute_error)) {
return {false, "nonfinite-comparison-value", false, {}};
}
error_norm = std::hypot(error_norm, absolute_error);
if (!std::isfinite(error_norm)) {
return {false, "nonfinite-comparison-value", false, {}};
}
if (absolute_error > decision.maximum_absolute_error) {
decision.maximum_absolute_error = absolute_error;
decision.worst_row = index;
}
ReferenceToleranceRowDecision row_decision{};
row_decision.absolute_error = absolute_error;
if (decision.reference_scale == 0.0) {
++decision.near_zero_count;
row_decision.threshold = 0.0;
row_decision.branch = ReferenceToleranceBranch::kZeroScaleExact;
row_decision.passed = absolute_error == 0.0;
} else if (std::abs(row.reference_value) <= decision.near_zero_band) {
++decision.near_zero_count;
row_decision.threshold = decision.near_zero_band;
row_decision.branch = ReferenceToleranceBranch::kNearZero;
row_decision.passed = absolute_error <= row_decision.threshold;
} else {
row_decision.threshold =
kRelativeTolerance * std::abs(row.reference_value);
row_decision.relative_error =
absolute_error / std::abs(row.reference_value);
row_decision.relative_error_applicable = true;
row_decision.branch = ReferenceToleranceBranch::kRelative;
row_decision.passed = row_decision.relative_error <= kRelativeTolerance;
}
decision.rows.push_back(row_decision);
}
const bool rows_passed = std::all_of(
decision.rows.begin(), decision.rows.end(),
[](const ReferenceToleranceRowDecision& row) { return row.passed; });
if (decision.reference_scale == 0.0) {
decision.relative_rms = 0.0;
decision.rms_passed = rows_passed;
if (!rows_passed) {
decision.diagnostic_code = "zero-reference-scale-nonzero-error";
}
} else {
const double rms =
error_norm / std::sqrt(static_cast<double>(family.rows.size()));
decision.relative_rms = rms / decision.reference_scale;
if (!std::isfinite(decision.relative_rms)) {
return {false, "nonfinite-comparison-value", false, {}};
}
decision.rms_passed = decision.relative_rms <= kRelativeRmsTolerance;
}
decision.passed = rows_passed && decision.rms_passed;
if (decision.blocking && !decision.passed) {
evaluation.passed = false;
}
evaluation.families.push_back(std::move(decision));
}
return evaluation;
}
}