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