in src/hit/api/evaluator/debug.cpp [145:226]
void DebugEval::print_stats(const CKKSCiphertext &ct) {
homomorphic_eval->print_stats(ct);
scale_estimator->print_stats(ct);
double norm = 0;
// decrypt to compute the approximate plaintext
vector<double> homom_plaintext = decrypt(ct, true);
vector<double> exact_plaintext = ct.raw_pt;
norm = relative_error(exact_plaintext, homom_plaintext);
if (abs(log2(ct.scale()) - log2(ct.backend_scale())) > 0.1) {
LOG_AND_THROW_STREAM("Internal error: HIT scale does not match SEAL scale: " << log2(ct.scale()) << " != "
<< ct.backend_scale());
}
VLOG(VLOG_EVAL) << setprecision(8) << " + Approximation norm: " << norm;
int max_print_size = 8;
stringstream verbose_info;
verbose_info << " + Homom Result: < ";
for (int i = 0; i < min(max_print_size, static_cast<int>(homom_plaintext.size())); i++) {
verbose_info << setprecision(8) << homom_plaintext[i] << ", ";
}
if (homom_plaintext.size() > max_print_size) {
verbose_info << "... ";
}
verbose_info << ">";
VLOG(VLOG_EVAL) << verbose_info.str();
if (norm > MAX_NORM) {
max_print_size = 32;
stringstream expect_debug_result;
expect_debug_result << " + DEBUG Expected result: <";
for (int i = 0; i < min(max_print_size, static_cast<int>(exact_plaintext.size())); i++) {
expect_debug_result << setprecision(8) << exact_plaintext[i];
if (i < exact_plaintext.size() - 1) {
expect_debug_result << ", ";
}
}
if (exact_plaintext.size() > max_print_size) {
expect_debug_result << "..., ";
}
expect_debug_result << ">";
LOG(ERROR) << expect_debug_result.str();
stringstream actual_debug_result;
actual_debug_result << " + DEBUG Actual result: <";
for (int i = 0; i < min(max_print_size, static_cast<int>(homom_plaintext.size())); i++) {
actual_debug_result << setprecision(8) << homom_plaintext[i];
if (i < exact_plaintext.size() - 1) {
actual_debug_result << ", ";
}
}
if (homom_plaintext.size() > max_print_size) {
actual_debug_result << "..., ";
}
actual_debug_result << ">";
LOG(ERROR) << actual_debug_result.str();
Plaintext encoded_plain;
homomorphic_eval->backend_encoder->encode(ct.raw_pt, ct.backend_ct.parms_id(), ct.scale(), encoded_plain);
vector<double> decoded_plain;
homomorphic_eval->backend_encoder->decode(encoded_plain, decoded_plain);
// the exact_plaintext and homom_plaintext should have the same length.
// decoded_plain is full-dimensional, however. This may not match
// the dimension of exact_plaintext if the plaintext in question is a
// vector, so we need to truncate the decoded value.
vector<double> truncated_decoded_plain(decoded_plain.begin(),
decoded_plain.begin() + exact_plaintext.size());
double norm2 = relative_error(exact_plaintext, truncated_decoded_plain);
double norm3 = relative_error(truncated_decoded_plain, homom_plaintext);
LOG(ERROR) << "Encoding norm: " << norm2;
LOG(ERROR) << "Encryption norm: " << norm3;
LOG_AND_THROW_STREAM("Plaintext and ciphertext divergence: " << norm << " > " << MAX_NORM << ". Scale is "
<< homomorphic_eval->context->log_scale()
<< " bits. See error log for more details.");
}
}