Add optional debug information for dumping instantiations (#6950)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 4 Aug 2021 19:19:51 +0000 (14:19 -0500)
committerGitHub <noreply@github.com>
Wed, 4 Aug 2021 19:19:51 +0000 (19:19 +0000)
This complete the implementation of dump-instantiations-debug.

With this option, we can print the source of instantiations. For example:

$ cvc5 regress1/quantifiers/dump-inst-proof.smt2 --dump-instantiations-debug --produce-proofs
unsat
(instantiations (forall ((x Int)) (or (P x) (Q x)))
  (! ( 2 ) :source QUANTIFIERS_INST_E_MATCHING_SIMPLE ((not (= (P x) true))))
)
(instantiations (forall ((x Int)) (or (not (S x)) (not (Q x))))
  (! ( 2 ) :source QUANTIFIERS_INST_E_MATCHING_SIMPLE ((not (= (S x) false))))
)

14 files changed:
src/main/command_executor.cpp
src/options/main_options.toml
src/printer/printer.cpp
src/smt/proof_post_processor.cpp
src/smt/proof_post_processor.h
src/smt/smt_engine.cpp
src/smt/smt_engine.h
src/smt/unsat_core_manager.cpp
src/smt/unsat_core_manager.h
src/theory/inference_id.cpp
src/theory/inference_id.h
src/theory/quantifiers/ematching/trigger.cpp
src/theory/quantifiers/instantiation_list.cpp
src/theory/quantifiers/instantiation_list.h

index bf658dfe0e5669c0aae52dea523c7f707aa7bce4..5431a83487f35fe488f3364d24cb66494d936273 100644 (file)
@@ -174,7 +174,8 @@ bool CommandExecutor::doCommandSingleton(Command* cmd)
       getterCommands.emplace_back(new GetProofCommand());
     }
 
-    if (d_solver->getOptions().driver.dumpInstantiations
+    if ((d_solver->getOptions().driver.dumpInstantiations
+         || d_solver->getOptions().driver.dumpInstantiationsDebug)
         && GetInstantiationsCommand::isEnabled(d_solver.get(), res))
     {
       getterCommands.emplace_back(new GetInstantiationsCommand());
index 19bb97f347d29dd014b0248e9c501e46fd7d09f4..869d411bdf4d9997ed055f3327d0f7dd981b5e55 100644 (file)
@@ -112,6 +112,14 @@ public = true
   default    = "false"
   help       = "output instantiations of quantified formulas after every UNSAT/VALID response"
 
+[[option]]
+  name       = "dumpInstantiationsDebug"
+  category   = "regular"
+  long       = "dump-instantiations-debug"
+  type       = "bool"
+  default    = "false"
+  help       = "output instantiations of quantified formulas after every UNSAT/VALID response, with debug information"
+
 [[option]]
   name       = "dumpUnsatCores"
   category   = "regular"
@@ -134,4 +142,4 @@ public = true
   long       = "force-no-limit-cpu-while-dump"
   type       = "bool"
   default    = "false"
-  help       = "Force no CPU limit when dumping models and proofs"
\ No newline at end of file
+  help       = "Force no CPU limit when dumping models and proofs"
index b733f62ce4e543d408a90d229847283439b68bc3..69727e1a29bae01272f94fadca05d304981d99bf 100644 (file)
@@ -101,14 +101,29 @@ void Printer::toStream(std::ostream& out, const UnsatCore& core) const
 void Printer::toStream(std::ostream& out, const InstantiationList& is) const
 {
   out << "(instantiations " << is.d_quant << std::endl;
-  for (const std::vector<Node>& i : is.d_inst)
+  for (const InstantiationVec& i : is.d_inst)
   {
-    out << "  ( ";
-    for (const Node& n : i)
+    out << "  ";
+    if (i.d_id != theory::InferenceId::UNKNOWN)
+    {
+      out << "(! ";
+    }
+    out << "( ";
+    for (const Node& n : i.d_vec)
     {
       out << n << " ";
     }
-    out << ")" << std::endl;
+    out << ")";
+    if (i.d_id != theory::InferenceId::UNKNOWN)
+    {
+      out << " :source " << i.d_id;
+      if (!i.d_pfArg.isNull())
+      {
+        out << " " << i.d_pfArg;
+      }
+      out << ")";
+    }
+    out << std::endl;
   }
   out << ")" << std::endl;
 }
index ad9a8f8449e306a2f074d24e33f77c9d96da7fd7..66f9fde7ceca0c5d558fd78b0dd8b2edc22468fd 100644 (file)
@@ -1182,7 +1182,10 @@ bool ProofPostprocessCallback::addToTransChildren(Node eq,
 ProofPostprocessFinalCallback::ProofPostprocessFinalCallback(
     ProofNodeManager* pnm)
     : d_ruleCount(smtStatisticsRegistry().registerHistogram<PfRule>(
-        "finalProof::ruleCount")),
+          "finalProof::ruleCount")),
+      d_instRuleIds(
+          smtStatisticsRegistry().registerHistogram<theory::InferenceId>(
+              "finalProof::instRuleId")),
       d_totalRuleCount(
           smtStatisticsRegistry().registerInt("finalProof::totalRuleCount")),
       d_minPedanticLevel(
@@ -1227,6 +1230,20 @@ bool ProofPostprocessFinalCallback::shouldUpdate(std::shared_ptr<ProofNode> pn,
   // record stats for the rule
   d_ruleCount << r;
   ++d_totalRuleCount;
+  // take stats on the instantiations in the proof
+  if (r == PfRule::INSTANTIATE)
+  {
+    Node q = pn->getChildren()[0]->getResult();
+    const std::vector<Node>& args = pn->getArguments();
+    if (args.size() > q[0].getNumChildren())
+    {
+      InferenceId id;
+      if (getInferenceId(args[q[0].getNumChildren()], id))
+      {
+        d_instRuleIds << id;
+      }
+    }
+  }
   return false;
 }
 
index f9e93fa9159a83f1acae9c5e636736c3baee3107..056f7769594dd8f32467971b6e8495511b0d4c12 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "proof/proof_node_updater.h"
 #include "smt/witness_form.h"
+#include "theory/inference_id.h"
 #include "util/statistics_stats.h"
 
 namespace cvc5 {
@@ -256,6 +257,11 @@ class ProofPostprocessFinalCallback : public ProofNodeUpdaterCallback
  private:
   /** Counts number of postprocessed proof nodes for each kind of proof rule */
   HistogramStat<PfRule> d_ruleCount;
+  /**
+   * Counts number of postprocessed proof nodes of rule INSTANTIATE that were
+   * marked with the given inference id.
+   */
+  HistogramStat<theory::InferenceId> d_instRuleIds;
   /** Total number of postprocessed rule applications */
   IntStat d_totalRuleCount;
   /** The minimum pedantic level of any rule encountered */
index 55f3d1c5fb2f8f5da5702d50210a76ee73f9ac56..5f3920929a502a7a7b086d8247cd2446b7c9f295 100644 (file)
@@ -1561,7 +1561,7 @@ UnsatCore SmtEngine::getUnsatCore() {
 }
 
 void SmtEngine::getRelevantInstantiationTermVectors(
-    std::map<Node, std::vector<std::vector<Node>>>& insts)
+    std::map<Node, InstantiationList>& insts, bool getDebugInfo)
 {
   Assert(d_state->getMode() == SmtMode::UNSAT);
   // generate with new proofs
@@ -1570,7 +1570,7 @@ void SmtEngine::getRelevantInstantiationTermVectors(
   Assert(pe->getProof() != nullptr);
   std::shared_ptr<ProofNode> pfn =
       d_pfManager->getFinalProof(pe->getProof(), *d_asserts);
-  d_ucManager->getRelevantInstantiations(pfn, insts);
+  d_ucManager->getRelevantInstantiations(pfn, insts, getDebugInfo);
 }
 
 std::string SmtEngine::getProof()
@@ -1635,11 +1635,36 @@ void SmtEngine::printInstantiations( std::ostream& out ) {
   }
 
   // Second, extract and print the instantiations
-  std::map<Node, std::vector<std::vector<Node>>> insts;
-  getInstantiationTermVectors(insts);
-  for (const std::pair<const Node, std::vector<std::vector<Node>>>& i : insts)
+  std::map<Node, InstantiationList> rinsts;
+  if (d_env->getOptions().smt.produceProofs
+      && (!d_env->getOptions().smt.unsatCores
+          || d_env->getOptions().smt.unsatCoresMode
+                 == options::UnsatCoresMode::FULL_PROOF)
+      && getSmtMode() == SmtMode::UNSAT)
   {
-    if (i.second.empty())
+    // minimize instantiations based on proof manager
+    getRelevantInstantiationTermVectors(rinsts,
+                                        options::dumpInstantiationsDebug());
+  }
+  else
+  {
+    std::map<Node, std::vector<std::vector<Node>>> insts;
+    getInstantiationTermVectors(insts);
+    for (const std::pair<const Node, std::vector<std::vector<Node>>>& i : insts)
+    {
+      // convert to instantiation list
+      Node q = i.first;
+      InstantiationList& ilq = rinsts[q];
+      ilq.initialize(q);
+      for (const std::vector<Node>& ii : i.second)
+      {
+        ilq.d_inst.push_back(InstantiationVec(ii));
+      }
+    }
+  }
+  for (std::pair<const Node, InstantiationList>& i : rinsts)
+  {
+    if (i.second.d_inst.empty())
     {
       // no instantiations, skip
       continue;
@@ -1653,22 +1678,23 @@ void SmtEngine::printInstantiations( std::ostream& out ) {
     // must have a name
     if (d_env->getOptions().printer.printInstMode == options::PrintInstMode::NUM)
     {
-      out << "(num-instantiations " << name << " " << i.second.size() << ")"
-          << std::endl;
+      out << "(num-instantiations " << name << " " << i.second.d_inst.size()
+          << ")" << std::endl;
     }
     else
     {
+      // take the name
+      i.second.d_quant = name;
       Assert(d_env->getOptions().printer.printInstMode
              == options::PrintInstMode::LIST);
-      InstantiationList ilist(name, i.second);
-      out << ilist;
+      out << i.second;
     }
     printed = true;
   }
   // if we did not print anything, we indicate this
   if (!printed)
   {
-    out << "No instantiations" << std::endl;
+    out << "none" << std::endl;
   }
   if (d_env->getOptions().printer.instFormatMode == options::InstFormatMode::SZS)
   {
@@ -1681,21 +1707,10 @@ void SmtEngine::getInstantiationTermVectors(
 {
   SmtScope smts(this);
   finishInit();
-  if (d_env->getOptions().smt.produceProofs
-      && (!d_env->getOptions().smt.unsatCores
-          || d_env->getOptions().smt.unsatCoresMode == options::UnsatCoresMode::FULL_PROOF)
-      && getSmtMode() == SmtMode::UNSAT)
-  {
-    // minimize instantiations based on proof manager
-    getRelevantInstantiationTermVectors(insts);
-  }
-  else
-  {
-    QuantifiersEngine* qe =
-        getAvailableQuantifiersEngine("getInstantiationTermVectors");
-    // otherwise, just get the list of all instantiations
-    qe->getInstantiationTermVectors(insts);
-  }
+  QuantifiersEngine* qe =
+      getAvailableQuantifiersEngine("getInstantiationTermVectors");
+  // get the list of all instantiations
+  qe->getInstantiationTermVectors(insts);
 }
 
 bool SmtEngine::getSynthSolutions(std::map<Node, Node>& solMap)
index 68b1caad026a9642e62290f2fa7d716e33d74801..d00fa0c76d5e8385eb7027c16d5ae96908d8c357 100644 (file)
@@ -46,6 +46,7 @@ class LogicRequest;
 class StatisticsRegistry;
 class Printer;
 class ResourceManager;
+struct InstantiationList;
 
 /* -------------------------------------------------------------------------- */
 
@@ -673,7 +674,7 @@ class CVC5_EXPORT SmtEngine
    * refutation.
    */
   void getRelevantInstantiationTermVectors(
-      std::map<Node, std::vector<std::vector<Node>>>& insts);
+      std::map<Node, InstantiationList>& insts, bool getDebugInfo = false);
   /**
    * Get instantiation term vectors, which maps each instantiated quantified
    * formula to the list of instantiations for that quantified formula. This
index 22304f9e84f09650d03fa5e71b05c00fc3f12ae2..01025e505df32ba516b6bf2dc8294439ef6b87fb 100644 (file)
@@ -56,12 +56,13 @@ void UnsatCoreManager::getUnsatCore(std::shared_ptr<ProofNode> pfn,
 
 void UnsatCoreManager::getRelevantInstantiations(
     std::shared_ptr<ProofNode> pfn,
-    std::map<Node, std::vector<std::vector<Node>>>& insts)
+    std::map<Node, InstantiationList>& insts,
+    bool getDebugInfo)
 {
   std::unordered_map<ProofNode*, bool> visited;
   std::unordered_map<ProofNode*, bool>::iterator it;
   std::vector<std::shared_ptr<ProofNode>> visit;
-
+  std::map<Node, InstantiationList>::iterator itq;
   std::shared_ptr<ProofNode> cur;
   visit.push_back(pfn);
   do
@@ -82,8 +83,27 @@ void UnsatCoreManager::getRelevantInstantiations(
       Node q = cs[0]->getResult();
       // the instantiation is a prefix of the arguments up to the number of
       // variables
-      insts[q].push_back(
-          {instTerms.begin(), instTerms.begin() + q[0].getNumChildren()});
+      itq = insts.find(q);
+      if (itq == insts.end())
+      {
+        insts[q].initialize(q);
+        itq = insts.find(q);
+      }
+      itq->second.d_inst.push_back(InstantiationVec(
+          {instTerms.begin(), instTerms.begin() + q[0].getNumChildren()}));
+      if (getDebugInfo)
+      {
+        std::vector<Node> extraArgs(instTerms.begin() + q[0].getNumChildren(),
+                                    instTerms.end());
+        if (extraArgs.size() >= 1)
+        {
+          getInferenceId(extraArgs[0], itq->second.d_inst.back().d_id);
+        }
+        if (extraArgs.size() >= 2)
+        {
+          itq->second.d_inst.back().d_pfArg = extraArgs[1];
+        }
+      }
     }
     for (const std::shared_ptr<ProofNode>& cp : cs)
     {
index e064b83a79c8db17ed53c1301cfa2ec5dbe51820..eb71c67caac0acc0f424bcac41addaa4de3a6b0a 100644 (file)
@@ -21,6 +21,7 @@
 #include "context/cdlist.h"
 #include "expr/node.h"
 #include "proof/proof_node.h"
+#include "theory/quantifiers/instantiation_list.h"
 
 namespace cvc5 {
 
@@ -61,9 +62,9 @@ class UnsatCoreManager
    * matrix with each row corresponding to the terms with which the respective
    * quantified formula is instiated.
    */
-  void getRelevantInstantiations(
-      std::shared_ptr<ProofNode> pfn,
-      std::map<Node, std::vector<std::vector<Node>>>& insts);
+  void getRelevantInstantiations(std::shared_ptr<ProofNode> pfn,
+                                 std::map<Node, InstantiationList>& insts,
+                                 bool getDebugInfo = false);
 
 }; /* class UnsatCoreManager */
 
index 8ed30ea9851a363ff353f8ba0db497f64ad41d03..bf13b75b1dfae2fbc361417773675b0ca9625eb0 100644 (file)
@@ -16,6 +16,7 @@
 #include "theory/inference_id.h"
 
 #include <iostream>
+#include "proof/proof_checker.h"
 #include "util/rational.h"
 
 namespace cvc5 {
@@ -426,5 +427,16 @@ Node mkInferenceIdNode(InferenceId i)
   return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(i)));
 }
 
+bool getInferenceId(TNode n, InferenceId& i)
+{
+  uint32_t index;
+  if (!ProofRuleChecker::getUInt32(n, index))
+  {
+    return false;
+  }
+  i = static_cast<InferenceId>(index);
+  return true;
+}
+
 }  // namespace theory
 }  // namespace cvc5
index b2bfe36579ae650d76c00aaf98e8ac280f6a6313..e93803170e075a49800ebb4cfe19aaf46d6c59dc 100644 (file)
@@ -823,6 +823,9 @@ std::ostream& operator<<(std::ostream& out, InferenceId i);
 /** Make node from inference id */
 Node mkInferenceIdNode(InferenceId i);
 
+/** get an inference identifier from a node, return false if we fail */
+bool getInferenceId(TNode n, InferenceId& i);
+
 }  // namespace theory
 }  // namespace cvc5
 
index 3e32496290e47d7f4f229d3f41a8d8534c1df9e6..63aabbfd6ebcb98beb68c4d6c63cb01802fd11d4 100644 (file)
@@ -59,7 +59,6 @@ Trigger::Trigger(QuantifiersState& qs,
     Node np = ensureGroundTermPreprocessed(val, n, d_groundTerms);
     d_nodes.push_back(np);
   }
-  d_trNode = NodeManager::currentNM()->mkNode(SEXPR, d_nodes);
   if (Trace.isOn("trigger"))
   {
     QuantAttributes& qa = d_qreg.getQuantAttributes();
@@ -70,18 +69,19 @@ Trigger::Trigger(QuantifiersState& qs,
       Trace("trigger") << "   " << n << std::endl;
     }
   }
+  std::vector<Node> extNodes;
+  for (const Node& nt : d_nodes)
+  {
+    // note we must display the original form, so we go back to bound vars
+    Node ns = d_qreg.substituteInstConstantsToBoundVariables(nt, q);
+    extNodes.push_back(ns);
+  }
+  d_trNode = NodeManager::currentNM()->mkNode(SEXPR, extNodes);
   if (Output.isOn(options::OutputTag::TRIGGER))
   {
     QuantAttributes& qa = d_qreg.getQuantAttributes();
-    Output(options::OutputTag::TRIGGER)
-        << "(trigger " << qa.quantToString(q) << " (";
-    for (size_t i = 0, nnodes = d_nodes.size(); i < nnodes; i++)
-    {
-      // note we must display the original form, so we go back to bound vars
-      Node ns = d_qreg.substituteInstConstantsToBoundVariables(d_nodes[i], q);
-      Output(options::OutputTag::TRIGGER) << (i > 0 ? " " : "") << ns;
-    }
-    Output(options::OutputTag::TRIGGER) << "))" << std::endl;
+    Output(options::OutputTag::TRIGGER) << "(trigger " << qa.quantToString(q)
+                                        << " " << d_trNode << ")" << std::endl;
   }
   QuantifiersStatistics& stats = qs.getStats();
   if( d_nodes.size()==1 ){
index db2e167fbff55edf199df028e61efa8786d16af7..f4b52619a4fea33da4f5415d56e9b1502198c4a0 100644 (file)
 
 namespace cvc5 {
 
+InstantiationVec::InstantiationVec(const std::vector<Node>& vec,
+                                   theory::InferenceId id,
+                                   Node pfArg)
+    : d_vec(vec), d_id(id), d_pfArg(pfArg)
+{
+}
+
+void InstantiationList::initialize(Node q) { d_quant = q; }
 std::ostream& operator<<(std::ostream& out, const InstantiationList& ilist)
 {
   Printer::getPrinter(options::outputLanguage())->toStream(out, ilist);
index d97383ba0889411eee9c3a19fb277404252ab2e0..5e292375aa7b67f6d0818173d1e50ffc500b8dd4 100644 (file)
 #include <vector>
 
 #include "expr/node.h"
+#include "theory/inference_id.h"
 
 namespace cvc5 {
 
+struct InstantiationVec
+{
+ public:
+  InstantiationVec(const std::vector<Node>& vec,
+                   theory::InferenceId id = theory::InferenceId::UNKNOWN,
+                   Node pfArg = Node::null());
+  /** The vector of terms */
+  std::vector<Node> d_vec;
+  /** The inference id */
+  theory::InferenceId d_id;
+  /** The proof argument */
+  Node d_pfArg;
+};
+
 /** A list of instantiations for a quantified formula */
 struct InstantiationList
 {
-  InstantiationList(Node q, const std::vector<std::vector<Node> >& inst)
-      : d_quant(q), d_inst(inst)
-  {
-  }
+  /** Initialize */
+  void initialize(Node q);
   /** The quantified formula */
   Node d_quant;
   /** The instantiation list */
-  std::vector<std::vector<Node> > d_inst;
+  std::vector<InstantiationVec> d_inst;
 };
 
 /** Print the instantiation list to stream out */