@@ -27,6 +27,8 @@ Revision History:
2727#include " util/map.h"
2828#include " util/dependency.h"
2929#include " util/permutation.h"
30+ #include " util/scoped_timer.h"
31+ #include " util/cancel_eh.h"
3032#include " math/polynomial/algebraic_numbers.h"
3133#include " math/polynomial/polynomial_cache.h"
3234#include " nlsat/nlsat_solver.h"
@@ -311,7 +313,7 @@ namespace nlsat {
311313 m_explain.set_minimize_cores (min_cores);
312314 m_explain.set_factor (p.factor ());
313315 m_explain.set_add_all_coeffs (p.add_all_coeffs ());
314- m_explain.set_add_zero_disc (p.zero_disc ());
316+ m_explain.set_add_zero_disc (p.zero_disc ());
315317 m_am.updt_params (p.p );
316318 }
317319
@@ -1013,42 +1015,27 @@ namespace nlsat {
10131015 }
10141016 }
10151017
1016- void check_lemma (unsigned n, literal const * cls, bool is_valid, assumption_set a) {
1017- TRACE (nlsat, display (tout << " check lemma: " , n, cls) << " \n " ;
1018- display (tout););
1019- if (!m_debug_known_solution_file_name.empty ()) {
1020- debug_check_lemma_on_known_sat_values (n, cls);
1021- return ;
1022- }
1023- IF_VERBOSE (2 , display (verbose_stream () << " check lemma " << (is_valid?" valid: " :" consequence: " ), n, cls) << " \n " );
1024- for (clause* c : m_learned) IF_VERBOSE (1 , display (verbose_stream () << " lemma: " , *c) << " \n " );
1025- scoped_suspend_rlimit _limit (m_rlimit);
1026- ctx c (m_rlimit, m_ctx.m_params , m_ctx.m_incremental );
1027- solver solver2 (c);
1028- imp& checker = *(solver2.m_imp );
1029- checker.m_check_lemmas = false ;
1030- checker.m_log_lemmas = false ;
1031- checker.m_dump_mathematica = false ;
1032- checker.m_inline_vars = false ;
1033-
1018+ // Helper: Setup checker solver and translate atoms/clauses
1019+ void setup_checker (imp& checker, scoped_bool_vars& tr, unsigned n, literal const * cls, assumption_set a) {
10341020 auto pconvert = [&](poly* p) {
10351021 return convert (m_pm, p, checker.m_pm );
10361022 };
10371023
1038- // need to translate Boolean variables and literals
1039- scoped_bool_vars tr (checker);
1024+ // Register variables (must use mk_var to also create vars in polynomial manager)
10401025 for (var x = 0 ; x < m_is_int.size (); ++x) {
1041- checker.register_var (x, is_int (x));
1026+ checker.mk_var ( is_int (x));
10421027 }
1028+
1029+ // Translate Boolean variables and atoms
10431030 bool_var bv = 0 ;
10441031 tr.push_back (bv);
10451032 for (bool_var b = 1 ; b < m_atoms.size (); ++b) {
1046- atom* a = m_atoms[b];
1047- if (a == nullptr ) {
1033+ atom* at = m_atoms[b];
1034+ if (at == nullptr ) {
10481035 bv = checker.mk_bool_var ();
10491036 }
1050- else if (a ->is_ineq_atom ()) {
1051- ineq_atom& ia = *to_ineq_atom (a );
1037+ else if (at ->is_ineq_atom ()) {
1038+ ineq_atom& ia = *to_ineq_atom (at );
10521039 unsigned sz = ia.size ();
10531040 polynomial_ref_vector ps (checker.m_pm );
10541041 bool_vector is_even;
@@ -1058,67 +1045,108 @@ namespace nlsat {
10581045 }
10591046 bv = checker.mk_ineq_atom (ia.get_kind (), sz, ps.data (), is_even.data ());
10601047 }
1061- else if (a ->is_root_atom ()) {
1062- root_atom& r = *to_root_atom (a );
1048+ else if (at ->is_root_atom ()) {
1049+ root_atom& r = *to_root_atom (at );
10631050 if (r.x () >= max_var (r.p ())) {
1064- // permutation may be reverted after check completes,
1065- // but then root atoms are not used in lemmas.
10661051 bv = checker.mk_root_atom (r.get_kind (), r.x (), r.i (), pconvert (r.p ()));
10671052 }
1053+ else {
1054+ // root atom cannot be translated due to variable ordering
1055+ bv = checker.mk_bool_var ();
1056+ }
10681057 }
10691058 else {
10701059 UNREACHABLE ();
10711060 }
10721061 tr.push_back (bv);
10731062 }
1074- if (!is_valid) {
1075- for (clause* c : m_clauses) {
1076- if (!a && c->assumptions ()) {
1077- continue ;
1078- }
1079- literal_vector lits;
1080- for (literal lit : *c) {
1081- lits.push_back (literal (tr[lit.var ()], lit.sign ()));
1082- }
1083- checker.mk_external_clause (lits.size (), lits.data (), nullptr );
1063+
1064+ // Add original clauses (checking that lemma is a consequence)
1065+ for (clause* c : m_clauses) {
1066+ if (!a && c->assumptions ()) {
1067+ continue ;
1068+ }
1069+ literal_vector lits;
1070+ for (literal lit : *c) {
1071+ lits.push_back (literal (tr[lit.var ()], lit.sign ()));
10841072 }
1073+ checker.mk_external_clause (lits.size (), lits.data (), nullptr );
10851074 }
1075+
1076+ // Add negation of lemma literals
10861077 for (unsigned i = 0 ; i < n; ++i) {
10871078 literal lit = cls[i];
10881079 literal nlit (tr[lit.var ()], !lit.sign ());
10891080 checker.mk_external_clause (1 , &nlit, nullptr );
10901081 }
1091- lbool r = checker.check ();
1092- if (r == l_true) {
1093- for (bool_var b : tr) {
1094- literal lit (b, false );
1095- IF_VERBOSE (0 , checker.display (verbose_stream (), lit) << " := " << checker.value (lit) << " \n " );
1096- TRACE (nlsat, checker.display (tout, lit) << " := " << checker.value (lit) << " \n " ;);
1097- }
1098- for (clause* c : m_learned) {
1099- bool found = false ;
1100- for (literal lit: *c) {
1101- literal tlit (tr[lit.var ()], lit.sign ());
1102- found |= checker.value (tlit) == l_true;
1103- }
1104- if (!found) {
1105- IF_VERBOSE (0 , display (verbose_stream () << " violdated clause: " , *c) << " \n " );
1106- TRACE (nlsat, display (tout << " violdated clause: " , *c) << " \n " ;);
1107- }
1082+ }
1083+
1084+ // Helper: Display unsound lemma failure information
1085+ void display_unsound_lemma (imp& checker, scoped_bool_vars& tr, unsigned n, literal const * cls) {
1086+ verbose_stream () << " \n " ;
1087+ verbose_stream () << " ========== UNSOUND LEMMA DETECTED ==========\n " ;
1088+ verbose_stream () << " The following lemma is NOT implied by the original clauses:\n " ;
1089+ display (verbose_stream () << " Lemma: " , n, cls) << " \n\n " ;
1090+ verbose_stream () << " Reason: Found a satisfying assignment where:\n " ;
1091+ verbose_stream () << " - The original clauses are satisfied\n " ;
1092+ verbose_stream () << " - But ALL literals in the lemma are FALSE\n\n " ;
1093+
1094+ // Display variable values used in the lemma
1095+ verbose_stream () << " Variable values in counterexample:\n " ;
1096+ auto lemma_vars = collect_vars_on_clause (n, cls);
1097+ for (var x : lemma_vars) {
1098+ if (checker.m_assignment .is_assigned (x)) {
1099+ verbose_stream () << " " ;
1100+ m_display_var (verbose_stream (), x);
1101+ verbose_stream () << " = " ;
1102+ checker.m_am .display_decimal (verbose_stream (), checker.m_assignment .value (x));
1103+ verbose_stream () << " \n " ;
11081104 }
1109- for (clause* c : m_valids) {
1110- bool found = false ;
1111- for (literal lit: *c) {
1112- literal tlit (tr[lit.var ()], lit.sign ());
1113- found |= checker.value (tlit) == l_true;
1114- }
1115- if (!found) {
1116- IF_VERBOSE (0 , display (verbose_stream () << " violdated tautology clause: " , *c) << " \n " );
1117- TRACE (nlsat, display (tout << " violdated tautology clause: " , *c) << " \n " ;);
1118- }
1105+ }
1106+
1107+ verbose_stream () << " \n Lemma literals evaluated to FALSE:\n " ;
1108+ for (unsigned i = 0 ; i < n; ++i) {
1109+ literal lit = cls[i];
1110+ literal tlit (tr[lit.var ()], lit.sign ());
1111+ verbose_stream () << " " ;
1112+ display (verbose_stream (), lit);
1113+ verbose_stream () << " = " << checker.value (tlit) << " \n " ;
1114+ }
1115+ verbose_stream () << " =============================================\n " ;
1116+ verbose_stream () << " ABORTING: Unsound lemma detected!\n " ;
1117+ }
1118+
1119+ void check_lemma (unsigned n, literal const * cls, assumption_set a) {
1120+ TRACE (nlsat, display (tout << " check lemma: " , n, cls) << " \n " ;
1121+ display (tout););
1122+
1123+ try {
1124+ // Create a separate reslimit for the checker with 10 second timeout
1125+ reslimit checker_rlimit;
1126+ cancel_eh<reslimit> eh (checker_rlimit);
1127+ scoped_timer timer (10000 , &eh);
1128+
1129+ ctx c (checker_rlimit, m_ctx.m_params , m_ctx.m_incremental );
1130+ solver solver2 (c);
1131+ imp& checker = *(solver2.m_imp );
1132+ checker.m_check_lemmas = false ;
1133+ checker.m_log_lemmas = false ;
1134+ checker.m_dump_mathematica = false ;
1135+ checker.m_inline_vars = false ;
1136+
1137+ scoped_bool_vars tr (checker);
1138+ setup_checker (checker, tr, n, cls, a);
1139+ lbool r = checker.check ();
1140+ if (r == l_undef) // Checker timed out - skip this lemma check
1141+ return ;
1142+
1143+ if (r == l_true) {
1144+ display_unsound_lemma (checker, tr, n, cls);
1145+ exit (1 );
11191146 }
1120- throw default_exception (" lemma did not check" );
1121- UNREACHABLE ();
1147+ }
1148+ catch (...) {
1149+ // Ignore exceptions from the checker - just skip this lemma check
11221150 }
11231151 }
11241152
@@ -2096,7 +2124,7 @@ namespace nlsat {
20962124
20972125 if (m_check_lemmas)
20982126 for (clause* c : m_learned)
2099- check_lemma (c->size (), c->data (), false , nullptr );
2127+ check_lemma (c->size (), c->data (), nullptr );
21002128
21012129 assumptions.reset ();
21022130 assumptions.append (result);
@@ -2333,7 +2361,7 @@ namespace nlsat {
23332361 }
23342362
23352363 if (m_check_lemmas) {
2336- check_lemma (m_lazy_clause.size (), m_lazy_clause.data (), false , nullptr );
2364+ check_lemma (m_lazy_clause.size (), m_lazy_clause.data (), nullptr );
23372365 m_valids.push_back (mk_clause_core (m_lazy_clause.size (), m_lazy_clause.data (), false , nullptr ));
23382366 }
23392367
@@ -2578,7 +2606,7 @@ namespace nlsat {
25782606 tout << " found_decision: " << found_decision << " \n " ;);
25792607
25802608 if (m_check_lemmas) {
2581- check_lemma (m_lemma.size (), m_lemma.data (), false , m_lemma_assumptions.get ());
2609+ check_lemma (m_lemma.size (), m_lemma.data (), m_lemma_assumptions.get ());
25822610 }
25832611
25842612 // if (m_log_lemmas)
0 commit comments