From 01cf0427b4f788e854005f15fcbe3db6f6a6155b Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Fri, 27 Sep 2024 11:36:10 +0100
Subject: [PATCH] fix #7404, relates to #7400.

---
 src/tactic/core/simplify_tactic.cpp   |  4 ++++
 src/tactic/core/simplify_tactic.h     |  1 +
 src/tactic/smtlogics/qfnra_tactic.cpp | 19 ++++++++++++-------
 src/tactic/tactic.cpp                 | 26 ++++++++++++++++++++++++++
 src/tactic/tactic.h                   |  1 +
 src/tactic/tactical.cpp               | 14 +++++++++++---
 6 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/src/tactic/core/simplify_tactic.cpp b/src/tactic/core/simplify_tactic.cpp
index f05b4c4fc62..ee5cd0f73c9 100644
--- a/src/tactic/core/simplify_tactic.cpp
+++ b/src/tactic/core/simplify_tactic.cpp
@@ -98,6 +98,7 @@ void simplify_tactic::operator()(goal_ref const & in,
         (*m_imp)(*(in.get()));
         in->inc_depth();
         result.push_back(in.get());
+        m_clean = false;
     }
     catch (rewriter_exception & ex) {
         throw tactic_exception(ex.msg());
@@ -106,10 +107,13 @@ void simplify_tactic::operator()(goal_ref const & in,
 
 
 void simplify_tactic::cleanup() {
+    if (m_clean)
+        return;
     ast_manager & m = m_imp->m();
     params_ref p = std::move(m_params);
     m_imp->~imp();
     new (m_imp) imp(m, p);
+    m_clean = true;
 }
 
 void simplify_tactic::collect_statistics(statistics& st) const {
diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h
index 7baabb8d6e8..af0806162b1 100644
--- a/src/tactic/core/simplify_tactic.h
+++ b/src/tactic/core/simplify_tactic.h
@@ -69,6 +69,7 @@ There are several options to control its behavior.
 #include "tactic/tactical.h"
 
 class simplify_tactic : public tactic {
+    bool       m_clean = true;
     struct     imp;
     imp *      m_imp;
     params_ref m_params;
diff --git a/src/tactic/smtlogics/qfnra_tactic.cpp b/src/tactic/smtlogics/qfnra_tactic.cpp
index f95ae8a8c05..ab28ffab72b 100644
--- a/src/tactic/smtlogics/qfnra_tactic.cpp
+++ b/src/tactic/smtlogics/qfnra_tactic.cpp
@@ -89,7 +89,7 @@ tactic * mk_qfnra_very_small_solver(ast_manager& m, params_ref const& p) {
         p_i.set_bool("shuffle_vars", true);
         // if ((i & 1) == 0)
         //     p_i.set_bool("randomize", false);
-        ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 3 * 1000));
+        ts.push_back(mk_lazy_tactic(m, p_i, [&](ast_manager& m, params_ref const& p) { return try_for(mk_qfnra_nlsat_tactic(m, p_i), 3 * 1000); }));
     }
     {
         ts.push_back(mk_qfnra_nlsat_tactic(m, p));
@@ -147,7 +147,7 @@ tactic * mk_qfnra_small_solver(ast_manager& m, params_ref const& p) {
         p_i.set_bool("shuffle_vars", true);
         // if ((i & 1) == 0)
         //     p_i.set_bool("randomize", false);
-        ts.push_back(try_for(mk_qfnra_nlsat_tactic(m, p_i), 5 * 1000));
+        ts.push_back(mk_lazy_tactic(m, p_i, [&](ast_manager& m, params_ref const& p) { return try_for(mk_qfnra_nlsat_tactic(m, p_i), 5 * 1000); }));
     }
     {
         ts.push_back(mk_qfnra_nlsat_tactic(m, p));
@@ -308,15 +308,20 @@ const double SMALL_THRESHOLD = 80.0;
 const double MIDDLE_THRESHOLD = 300.0;
 const double LARGE_THRESHOLD = 600.0;
 tactic * mk_qfnra_mixed_solver(ast_manager& m, params_ref const& p) {
+    auto very_small_t = mk_lazy_tactic(m, p, [&](ast_manager& m, params_ref const& p) {return mk_qfnra_very_small_solver(m, p); });
+    auto small_t = mk_lazy_tactic(m, p, [&](ast_manager& m, params_ref const& p) {return mk_qfnra_small_solver(m, p); });
+    auto middle_t = mk_lazy_tactic(m, p, [&](ast_manager& m, params_ref const& p) {return mk_qfnra_middle_solver(m, p); });
+    auto large_t = mk_lazy_tactic(m, p, [&](ast_manager& m, params_ref const& p) {return mk_qfnra_large_solver(m, p); });
+    auto very_large_t = mk_lazy_tactic(m, p, [&](ast_manager& m, params_ref const& p) {return mk_qfnra_very_large_solver(m, p); });
     return cond(mk_lt(mk_memory_probe(), mk_const_probe(VERY_SMALL_THRESHOLD)), 
-                mk_qfnra_very_small_solver(m, p),
+        very_small_t,
                 cond(mk_lt(mk_memory_probe(), mk_const_probe(SMALL_THRESHOLD)), 
-                     mk_qfnra_small_solver(m, p),
+                    small_t,
                      cond(mk_lt(mk_memory_probe(), mk_const_probe(MIDDLE_THRESHOLD)),
-                          mk_qfnra_middle_solver(m, p),
+                         middle_t,
                           cond(mk_lt(mk_memory_probe(), mk_const_probe(LARGE_THRESHOLD)),
-                               mk_qfnra_large_solver(m, p),
-                               mk_qfnra_very_large_solver(m, p)
+                             large_t,
+                             very_large_t
                         )
                     )
                 )
diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp
index e4e7b246aef..19a370caa9f 100644
--- a/src/tactic/tactic.cpp
+++ b/src/tactic/tactic.cpp
@@ -95,6 +95,32 @@ tactic * mk_skip_tactic() {
     return alloc(skip_tactic);
 }
 
+class lazy_tactic : public tactic {
+    ast_manager& m;
+    params_ref p;
+    std::function<tactic* (ast_manager& m, params_ref const& p)> m_mk_tactic;
+    tactic* m_tactic = nullptr;
+    void ensure_tactic() { if (!m_tactic) m_tactic = m_mk_tactic(m, p); }
+public:
+    lazy_tactic(ast_manager& m, params_ref const& p, std::function<tactic* (ast_manager&, params_ref const&)> mk_tactic) : m(m), p(p), m_mk_tactic(mk_tactic) {}
+    ~lazy_tactic() override { dealloc(m_tactic); }
+    void operator()(goal_ref const& in, goal_ref_buffer& result) override {
+        ensure_tactic();
+        (*m_tactic)(in, result);
+    }
+    void cleanup() override { if (m_tactic) m_tactic->cleanup(); }
+    char const* name() const override { return "lazy tactic"; }
+    void collect_statistics(statistics& st) const override { if (m_tactic) m_tactic->collect_statistics(st); }
+    void user_propagate_initialize_value(expr* var, expr* value) override { if (m_tactic) m_tactic->user_propagate_initialize_value(var, value); }
+    tactic* translate(ast_manager& m) override { ensure_tactic(); return m_tactic->translate(m); }
+    void reset() override { if (m_tactic) m_tactic->reset(); }
+};
+
+
+tactic* mk_lazy_tactic(ast_manager& m, params_ref const& p, std::function<tactic*(ast_manager& m, params_ref const& p)> mkt) {
+    return alloc(lazy_tactic, m, p, mkt);
+}
+
 class fail_tactic : public tactic {
 public:
     void operator()(goal_ref const & in, goal_ref_buffer & result) override {
diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h
index c6927642476..698146c0d3d 100644
--- a/src/tactic/tactic.h
+++ b/src/tactic/tactic.h
@@ -135,6 +135,7 @@ class skip_tactic : public tactic {
 tactic * mk_skip_tactic();
 tactic * mk_fail_tactic();
 tactic * mk_fail_if_undecided_tactic();
+tactic*  mk_lazy_tactic(ast_manager& m, params_ref const& p, std::function<tactic*(ast_manager& m, params_ref const& p)>);
 
 /*
   ADD_TACTIC("skip", "do nothing tactic.", "mk_skip_tactic()")
diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp
index db0a6d44d76..78e15aef7a7 100644
--- a/src/tactic/tactical.cpp
+++ b/src/tactic/tactical.cpp
@@ -30,6 +30,7 @@ class binary_tactical : public tactic {
 protected:
     tactic_ref      m_t1;
     tactic_ref      m_t2;
+    bool            m_clean = true;
     
 public:
 
@@ -61,8 +62,11 @@ class binary_tactical : public tactic {
     }
         
     void cleanup() override {
+        if (m_clean)
+            return;
         m_t1->cleanup();
         m_t2->cleanup();
+        m_clean = true;
     }
     
     void reset() override {
@@ -103,7 +107,8 @@ class and_then_tactical : public binary_tactical {
 
     char const* name() const override { return "and_then"; }
 
-    void operator()(goal_ref const & in, goal_ref_buffer& result) override { 
+    void operator()(goal_ref const & in, goal_ref_buffer& result) override {
+        m_clean = false;
 
         bool proofs_enabled = in->proofs_enabled();
         bool cores_enabled  = in->unsat_core_enabled();
@@ -872,6 +877,7 @@ tactic * par_and_then(unsigned num, tactic * const * ts) {
 class unary_tactical : public tactic {
 protected:
     tactic_ref m_t;
+    bool m_clean = true;
 
 
 public:
@@ -880,11 +886,12 @@ class unary_tactical : public tactic {
         SASSERT(t);  
     }
 
-    void operator()(goal_ref const & in, goal_ref_buffer& result) override { 
+    void operator()(goal_ref const & in, goal_ref_buffer& result) override {
+        m_clean = false;
         m_t->operator()(in, result);
     }
    
-    void cleanup(void) override { m_t->cleanup(); }
+    void cleanup(void) override { if (!m_clean) m_t->cleanup(); m_clean = true; }
     void collect_statistics(statistics & st) const override { m_t->collect_statistics(st); }
     void reset_statistics() override { m_t->reset_statistics(); }    
     void updt_params(params_ref const & p) override { m_t->updt_params(p); }
@@ -1158,6 +1165,7 @@ class cond_tactical : public binary_tactical {
     char const* name() const override { return "cond"; }
     
     void operator()(goal_ref const & in, goal_ref_buffer & result) override {
+        m_clean = false;
         if (m_p->operator()(*(in.get())).is_true()) 
             m_t1->operator()(in, result);
         else