116 lines
4.2 KiB
C++
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;
|
|
}
|
|
|
|
}
|