From 059bad909ad4b5f0b70378970475149822cccaa6 Mon Sep 17 00:00:00 2001
From: Nikolaj Bjorner <nbjorner@microsoft.com>
Date: Thu, 31 Aug 2017 07:33:55 -0700
Subject: [PATCH] prune dead states from automata

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
---
 src/ast/simplifier/base_simplifier.h |  1 +
 src/ast/simplifier/simplifier.cpp    |  9 +++-
 src/math/automata/automaton.h        | 72 +++++++++++++++++++++-------
 src/smt/asserted_formulas.cpp        |  3 +-
 src/smt/smt_quantifier.cpp           |  7 ++-
 5 files changed, 70 insertions(+), 22 deletions(-)

diff --git a/src/ast/simplifier/base_simplifier.h b/src/ast/simplifier/base_simplifier.h
index 73a04d605..87c372636 100644
--- a/src/ast/simplifier/base_simplifier.h
+++ b/src/ast/simplifier/base_simplifier.h
@@ -37,6 +37,7 @@ protected:
                tout << mk_pp(n, m) << "\n";
                tout << mk_pp(r, m) << "\n";
                tout << mk_pp(p, m) << "\n";);
+        TRACE("cache", tout << mk_pp(n, m) << " -> " << mk_pp(r, m) << "\n";);
         SASSERT(is_rewrite_proof(n, r, p));
     }
     void reset_cache() { m_cache.reset(); }
diff --git a/src/ast/simplifier/simplifier.cpp b/src/ast/simplifier/simplifier.cpp
index c02753440..e7f842203 100644
--- a/src/ast/simplifier/simplifier.cpp
+++ b/src/ast/simplifier/simplifier.cpp
@@ -591,8 +591,10 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
                 if (m_ac_cache.find(to_app(arg), new_arg)) {
                     SASSERT(new_arg != 0);
                     new_args.push_back(new_arg);
-                    if (arg != new_arg)
+                    if (arg != new_arg) {
+                        TRACE("ac", tout << mk_pp(arg, m) << " -> " << mk_pp(new_arg, m) << "\n";);
                         has_new_arg = true;
+                    }
                     if (m.fine_grain_proofs()) {
                         proof * pr = 0;
                         m_ac_pr_cache.find(to_app(arg), pr);
@@ -610,8 +612,10 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
                 proof * pr;
                 get_cached(arg, new_arg, pr);
                 new_args.push_back(new_arg);
-                if (arg != new_arg)
+                if (arg != new_arg) {
+                    TRACE("ac", tout << "cached: " << mk_pp(arg, m) << " -> " << mk_pp(new_arg, m) << "\n";);
                     has_new_arg = true;
+                }
                 if (m.fine_grain_proofs() && pr != 0)
                     new_arg_prs.push_back(pr);
             }
@@ -627,6 +631,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
             else {
                 app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr());
                 m_ac_cache.insert(curr, new_curr);
+                TRACE("ac", tout << mk_pp(curr, m) << " -> " << mk_pp(new_curr, m) << "\n";);
                 if (m.fine_grain_proofs()) {
                     proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
                     m_ac_pr_cache.insert(curr, p);
diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h
index 12aa120b3..07d6d31ec 100644
--- a/src/math/automata/automaton.h
+++ b/src/math/automata/automaton.h
@@ -25,6 +25,7 @@ Revision History:
 #include "util/util.h"
 #include "util/vector.h"
 #include "util/uint_set.h"
+#include "util/trace.h"
 
 template<class T>
 class default_value_manager {
@@ -383,12 +384,12 @@ public:
                     else if (1 == in_degree(dst) && (!is_final_state(dst) || is_final_state(src)) && init() != dst) {
                         moves const& mvs = m_delta[dst];
                         moves mvs1;
-                        for (unsigned k = 0; k < mvs.size(); ++k) {
-                            mvs1.push_back(move(m, src, mvs[k].dst(), mvs[k].t()));
+                        for (move const& mv : mvs) {
+                            mvs1.push_back(move(m, src, mv.dst(), mv.t()));
                         }
-                        for (unsigned k = 0; k < mvs1.size(); ++k) {
-                            remove(dst, mvs1[k].dst(), mvs1[k].t());
-                            add(mvs1[k]);
+                        for (move const& mv : mvs1) {
+                            remove(dst, mv.dst(), mv.t());
+                            add(mv);
                         }
                     }
                     //
@@ -401,13 +402,13 @@ public:
                         unsigned_vector src0s;
                         moves const& mvs = m_delta_inv[dst];
                         moves mvs1;
-                        for (unsigned k = 0; k < mvs.size(); ++k) {
-                            SASSERT(mvs[k].is_epsilon());
-                            mvs1.push_back(move(m, mvs[k].src(), dst1, t));
+                        for (move const& mv1 : mvs) {
+                            SASSERT(mv1.is_epsilon());
+                            mvs1.push_back(move(m, mv1.src(), dst1, t));
                         }
-                        for (unsigned k = 0; k < mvs1.size(); ++k) {
-                            remove(mvs1[k].src(), dst, 0);
-                            add(mvs1[k]);
+                        for (move const& mv1 : mvs1) {
+                            remove(mv1.src(), dst, 0);
+                            add(mv1);
                         }
                         remove(dst, dst1, t);    
                         --j;
@@ -419,12 +420,12 @@ public:
                     else if (1 == out_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) {
                         moves const& mvs = m_delta_inv[src];
                         moves mvs1;
-                        for (unsigned k = 0; k < mvs.size(); ++k) {
-                            mvs1.push_back(move(m, mvs[k].src(), dst, mvs[k].t()));
+                        for (move const& mv : mvs) {
+                            mvs1.push_back(move(m, mv.src(), dst, mv.t()));
                         }
-                        for (unsigned k = 0; k < mvs1.size(); ++k) {
-                            remove(mvs1[k].src(), src, mvs1[k].t());
-                            add(mvs1[k]);
+                        for (move const& mv : mvs1) {
+                            remove(mv.src(), src, mv.t());
+                            add(mv);
                         }
                     }
                     else {
@@ -447,6 +448,7 @@ public:
                 break;
             }
         }
+        sinkify_dead_states();
     }
 
     bool is_sequence(unsigned& length) const {
@@ -564,6 +566,40 @@ public:
     }
 private:
 
+    void sinkify_dead_states() {
+        uint_set dead_states;
+        for (unsigned i = 0; i < m_delta.size(); ++i) {
+            if (!m_final_states.contains(i)) {
+                dead_states.insert(i);
+            }
+        }
+        bool change = true;
+        unsigned_vector to_remove;
+        while (change) {
+            change = false;
+            to_remove.reset();
+            for (unsigned s : dead_states) {
+                moves const& mvs = m_delta[s];
+                for (move const& mv : mvs) {
+                    if (!dead_states.contains(mv.dst())) {
+                        to_remove.push_back(s);
+                        break;
+                    }
+                }
+            }
+            change = !to_remove.empty();
+            for (unsigned s : to_remove) {
+                dead_states.remove(s);
+            }
+            to_remove.reset();
+        }
+        TRACE("seq", tout << "remove: " << dead_states << "\n";);
+        for (unsigned s : dead_states) {
+            CTRACE("seq", !m_delta[s].empty(), tout << "live state " << s << "\n";); 
+            m_delta[s].reset();
+        }
+    }
+
     void remove_dead_states() {
         unsigned_vector remap;
         for (unsigned i = 0; i < m_delta.size(); ++i) {
@@ -669,8 +705,8 @@ private:
     }
 
     static void append_final(unsigned offset, automaton const& a, unsigned_vector& final) {
-        for (unsigned i = 0; i < a.m_final_states.size(); ++i) {
-            final.push_back(a.m_final_states[i]+offset);
+        for (unsigned s : a.m_final_states) {
+            final.push_back(s+offset);
         }
     }
  
diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp
index dabae9fbd..d9630c979 100644
--- a/src/smt/asserted_formulas.cpp
+++ b/src/smt/asserted_formulas.cpp
@@ -645,7 +645,7 @@ void asserted_formulas::propagate_values() {
                 new_prs2.push_back(pr);
         }
     }
-    TRACE("propagate_values", tout << "found: " << found << "\n";);
+    TRACE("propagate_values", tout << "found: " << found << "\n" << new_exprs2 << "\n";);
     // If C is not empty, then reduce R using the updated simplifier cache with entries
     // x -> n for each constraint 'x = n' in C.
     if (found) {
@@ -656,6 +656,7 @@ void asserted_formulas::propagate_values() {
             expr_ref new_n(m);
             proof_ref new_pr(m);
             m_simplifier(n, new_n, new_pr);
+            TRACE("propagate_values", tout << mk_pp(n, m) << " -> " << new_n << "\n";);
             if (n == new_n.get()) {
                 push_assertion(n, pr, new_exprs1, new_prs1);
             }
diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp
index 84ed7c8cd..0492aff23 100644
--- a/src/smt/smt_quantifier.cpp
+++ b/src/smt/smt_quantifier.cpp
@@ -492,6 +492,7 @@ namespace smt {
 
         virtual void assign_eh(quantifier * q) {
             m_active = true;
+            ast_manager& m = m_context->get_manager();
             if (!m_fparams->m_ematching) {
                 return;
             }
@@ -514,7 +515,11 @@ namespace smt {
                 app * mp = to_app(q->get_pattern(i));
                 SASSERT(m_context->get_manager().is_pattern(mp));
                 bool unary = (mp->get_num_args() == 1);
-                if (!unary && j >= num_eager_multi_patterns) {
+                if (m.is_rec_fun_def(q) && i > 0) {
+                    // add only the first pattern
+                    TRACE("quantifier", tout << "skip recursive function body " << mk_ismt2_pp(mp, m) << "\n";);
+                }
+                else if (!unary && j >= num_eager_multi_patterns) {
                     TRACE("quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n"
                           << "j: " << j << " unary: " << unary << " m_params.m_qi_max_eager_multipatterns: " << m_fparams->m_qi_max_eager_multipatterns
                           << " num_eager_multi_patterns: " << num_eager_multi_patterns << "\n";);