in src/hit/common.cpp [59:102]
double relative_error(const vector<double> &expected, const vector<double> &actual) {
int len = expected.size();
if (len != actual.size()) {
LOG_AND_THROW_STREAM("Inputs to relative error do not have the same size: " << len
<< " != " << actual.size());
}
Vector expected_vec = Vector(expected);
Vector actual_vec = Vector(actual);
Vector diff_vec = expected_vec - actual_vec;
double expected_l2_norm = norm_2(expected_vec);
double actual_l2_norm = norm_2(actual_vec);
double diff_l2_norm = norm_2(diff_vec);
// if the expected result is the zero vector, we can't reasonably compare norms.
// We also can't just test if the expected vector norm is exactly 0 due to
// decoding precision in CKKS. In other words, decode(encode(<0,0,...>))
// may contain very small non-zero values. (Note that this has nothing to
// do with encryption noise.) The "actual" result, which typically comes
// from decryption a CKKS ciphertext, will have much larger coefficients.
// For example, decoding noise for the all-0 vector may result in coefficients
// with magnitude ~10^-30. Decryption of the all-0 vector will result in
// coefficients ~10^-11. Since these are vastly different scales, the relative
// norm is huge, even though these vectors both represent 0. As a result,
// we instead fuzz the norm test: if the expected vector norm is "small enough"
// we skip the comparison altogether. The magic constant below seems to work
// well in practice.
int log_norm_limit = 12;
double max_allowed_l2_norm = pow(2, -log_norm_limit);
if (expected_l2_norm <= max_allowed_l2_norm && actual_l2_norm <= max_allowed_l2_norm) {
return -1;
}
if (expected_l2_norm <= max_allowed_l2_norm) {
// An unexpected situation.
LOG(WARNING) << "The expected result's norm is nearly zero (2^" << setprecision(8) << log2(expected_l2_norm)
<< "), but the actual result's norm is non-zero (2^" << log2(actual_l2_norm) << ")";
}
if (diff_l2_norm > MAX_NORM) {
LOG(WARNING) << "Relative norm is somewhat large (2^" << setprecision(8) << log2(diff_l2_norm)
<< "); there may be an error in the computation.";
}
return diff_l2_norm;
}