Branch data Line data Source code
1 : : /****************************************************************************** 2 : : * Top contributors (to current version): 3 : : * Andrew Reynolds, Aina Niemetz, Mathias Preiner 4 : : * 5 : : * This file is part of the cvc5 project. 6 : : * 7 : : * Copyright (c) 2009-2024 by the authors listed in the file AUTHORS 8 : : * in the top-level source directory and their institutional affiliations. 9 : : * All rights reserved. See the file COPYING in the top-level source 10 : : * directory for licensing information. 11 : : * **************************************************************************** 12 : : * 13 : : * A let binding. 14 : : */ 15 : : 16 : : #include "cvc5_private.h" 17 : : 18 : : #ifndef CVC5__PRINTER__LET_BINDING_H 19 : : #define CVC5__PRINTER__LET_BINDING_H 20 : : 21 : : #include <vector> 22 : : 23 : : #include "context/cdhashmap.h" 24 : : #include "context/cdlist.h" 25 : : #include "expr/node.h" 26 : : 27 : : namespace cvc5::internal { 28 : : 29 : : /** 30 : : * A flexible let binding class. This class provides functionalities for 31 : : * printing letified terms. A simple use case is the following for Node n 32 : : * and LetBinding lbind: 33 : : * ``` 34 : : * std::vector<Node> letList; 35 : : * lbind.letify(n, letList); 36 : : * ``` 37 : : * Now, letList contains a list of subterms of n that should be letified based 38 : : * on the threshold value passed to this class where a value n>0 indicates that 39 : : * terms with n or more occurrences should be letified. 40 : : * 41 : : * The above is equivalent to: 42 : : * ``` 43 : : * std::vector<Node> letList; 44 : : * lbind.pushScope(); 45 : : * lbind.process(n); 46 : : * lbind.letify(letList); 47 : : * ``` 48 : : * In fact, multiple terms can be passed to calls to process, in which case the 49 : : * counting is cumulative. 50 : : * 51 : : * All quantified formulas are treated as black boxes. This class can be used 52 : : * to letify terms with quantifiers, where multiple calls to pushScope / 53 : : * popScope can be used. In particular, consider: 54 : : * ``` 55 : : * std::vector<Node> letList1; 56 : : * lbind.letify(n1, letList1); 57 : : * std::vector<Node> letList2; 58 : : * lbind.letify(n2, letList2); 59 : : * ... 60 : : * lbind.popScope(); 61 : : * lbind.popScope(); 62 : : * ``` 63 : : * In a typical use case, n2 is the body of a quantified formula that is a 64 : : * subterm of n1. We have that letList2 is the list of subterms of n2 that 65 : : * should be letified, assuming that we have already have let definitions 66 : : * given by letList1. 67 : : * 68 : : * Internally, a let binding is a list and a map that can be printed as a let 69 : : * expression. In particular, the list d_letList is ordered such that 70 : : * d_letList[i] does not contain subterm d_letList[j] for j>i. 71 : : * It is intended that d_letList contains only unique nodes. Each node 72 : : * in d_letList is mapped to a unique identifier in d_letMap. 73 : : * 74 : : * Notice that this class will *not* use introduced let symbols when converting 75 : : * the bodies of quantified formulas. Consider the formula: 76 : : * (let ((Q (forall ((x Int)) (= x (+ a a))))) (and (= (+ a a) (+ a a)) Q Q)) 77 : : * where "let" above is from the user. When this is letified by this class, 78 : : * note that (+ a a) occurs as a subterm of Q, however it is not seen until 79 : : * after we have seen Q twice, since we traverse in reverse topological order. 80 : : * Since we do not traverse underneath quantified formulas, this means that Q 81 : : * may be marked as a term-to-letify before (+ a a), which leads to violation 82 : : * of the above invariant concerning containment. Thus, when converting, if 83 : : * a let symbol is introduced for (+ a a), we will not replace the occurrence 84 : : * of (+ a a) within Q. Instead, the user of this class is responsible for 85 : : * letifying the bodies of quantified formulas independently. 86 : : */ 87 : : class LetBinding 88 : : { 89 : : using NodeList = context::CDList<Node>; 90 : : using NodeIdMap = context::CDHashMap<Node, uint32_t>; 91 : : 92 : : public: 93 : : /** 94 : : * @param prefix The prefix to use for introduced variables 95 : : * @param thresh The threshold to use, that is, the number of times a term 96 : : * must appear before being letified. 97 : : * @param traverseBinders Whether we should traverse binders, that is, if 98 : : * this flag is true, we consider terms beneath binders as targets for 99 : : * letificiation. 100 : : * @param traverseSkolems Whether we should traverse skolems, that is, if 101 : : * this flag is true, we consider terms in skolem indices as targets for 102 : : * letificiation. 103 : : */ 104 : : LetBinding(const std::string& prefix, 105 : : uint32_t thresh = 2, 106 : : bool traverseBinders = false, 107 : : bool traverseSkolems = false); 108 : : /** Get threshold */ 109 : : uint32_t getThreshold() const; 110 : : /** 111 : : * This updates this let binding to consider the counts for node n. 112 : : */ 113 : : void process(Node n); 114 : : /** 115 : : * This pushes a scope, computes the letification for n, adds the (new) terms 116 : : * that must be letified in this context to letList. 117 : : * 118 : : * Notice that this method does not traverse inside of closures. 119 : : * 120 : : * @param n The node to letify 121 : : * @param letList The list of terms that should be letified within n. This 122 : : * list is ordered in such a way that letList[i] does not contain subterm 123 : : * letList[j] for j>i. 124 : : */ 125 : : void letify(Node n, std::vector<Node>& letList); 126 : : /** 127 : : * Same as above, without a node to letify. 128 : : */ 129 : : void letify(std::vector<Node>& letList); 130 : : /** Push scope */ 131 : : void pushScope(); 132 : : /** Pop scope for n, reverts the state change of the above method */ 133 : : void popScope(); 134 : : /** 135 : : * @return the identifier for node n, or 0 if it does not have one. 136 : : */ 137 : : uint32_t getId(Node n) const; 138 : : /** Get prefix. */ 139 : 16386700 : const std::string& getPrefix() const { return d_prefix; } 140 : : /** 141 : : * Convert n based on the state of the let binding. This replaces all 142 : : * letified subterms of n with a fresh variable whose name prefix is the 143 : : * given one. 144 : : * 145 : : * @param n The node to conver 146 : : * @param letTop Whether we letify n itself 147 : : * @return the converted node. 148 : : */ 149 : : Node convert(Node n, bool letTop = true) const; 150 : : 151 : : private: 152 : : /** The prefix */ 153 : : std::string d_prefix; 154 : : /** 155 : : * Compute the count of sub nodes in n, store in d_count. Additionally, 156 : : * store each node in the domain of d_count in an order in d_visitList 157 : : * such that d_visitList[i] does not contain sub d_visitList[j] for j>i. 158 : : */ 159 : : void updateCounts(Node n); 160 : : /** 161 : : * Convert a count to a let binding. 162 : : */ 163 : : void convertCountToLet(); 164 : : /** The dag threshold */ 165 : : uint32_t d_thresh; 166 : : /** Traverse binders? */ 167 : : bool d_traverseBinders; 168 : : /** Traverse skolems? */ 169 : : bool d_traverseSkolems; 170 : : /** An internal context */ 171 : : context::Context d_context; 172 : : /** Visit list */ 173 : : NodeList d_visitList; 174 : : /** Count */ 175 : : NodeIdMap d_count; 176 : : /** The let list */ 177 : : NodeList d_letList; 178 : : protected: 179 : : /** The let map */ 180 : : NodeIdMap d_letMap; 181 : : }; 182 : : 183 : : } // namespace cvc5::internal 184 : : 185 : : #endif