SMTStabilizer API
Public API documentation for SMTStabilizer
Loading...
Searching...
No Matches
dag.cpp
Go to the documentation of this file.
1/* -*- Source -*-
2 *
3 * The Directed Acyclic Graph (DAG) Class
4 *
5 * Author: Fuqi Jia <jiafq@ios.ac.cn>
6 *
7 * Copyright (C) 2025 Fuqi Jia
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 */
27// Modified by Xiang Zhang, 2026
28// Additional changes licensed under the MIT License
29#include "dag.h"
30
31#include <iomanip>
32#include <memory>
33#include <sstream>
34#include <stack>
35#include <unordered_map>
36#include <unordered_set>
37#include <vector>
38
39#include "parser/kind.h"
40
41namespace stabilizer::parser {
42
43void DAGNode::updateFuncDef(std::shared_ptr<Sort> out_sort,
44 std::shared_ptr<DAGNode> body,
45 const std::vector<std::shared_ptr<DAGNode>> &params,
46 bool is_rec) {
47 condAssert(out_sort == sort, "updateFuncDef: out_sort != sort");
48 (void)out_sort;
49 children.clear();
50 children.emplace_back(body);
51 for (auto &p : params) {
52 children.emplace_back(p);
53 }
55}
56
58 std::shared_ptr<Sort> out_sort, std::shared_ptr<DAGNode> body, const std::vector<std::shared_ptr<DAGNode>> &params, bool is_rec) {
59 condAssert(out_sort == sort, "updateApplyFunc: out_sort != sort");
60 (void)out_sort;
61 children.clear();
62 children.emplace_back(body);
63 for (auto &p : params) {
64 children.emplace_back(p);
65 }
67}
68
69std::string dumpConst(const std::string &name,
70 const std::shared_ptr<Sort> &sort) {
71 if (sort->isReal()) {
72 if (name[0] == '-') {
73 return "(- " + name.substr(1) + ")";
74 }
75 else {
76 return name;
77 }
78 }
79 else if (sort->isInt() || sort->isIntOrReal()) {
80 if (name[0] == '-') {
81 return "(- " + name.substr(1) + ")";
82 }
83 else {
84 return name;
85 }
86 }
87 else if (sort->isBool()) {
88 return name;
89 }
90 else if (sort->isBv()) {
91 return name;
92 }
93 else if (sort->isFp()) {
94 return name;
95 }
96 else if (sort->isStr()) {
97 // if (name.front() == '\"' && name.back() == '\"')
98 // return std::quoted(name)._M_string;
99 // else
100 // std::cout << name << ' ' << ConversionUtils::unescapeString(name) <<
101 // std::endl; return ConversionUtils::unescapeString(name);
102 return name;
103 }
104 else if (sort->isArray()) {
105 return name;
106 }
107 return name;
108}
109
110// High-performance iterative streaming version to avoid stack overflow
111void dumpSMTLIB2_streaming(const std::shared_ptr<DAGNode> &root,
112 std::ostream &out) {
113 if (!root)
114 return;
115
116 std::shared_ptr<DAGNode> actualRoot = root;
117
118 // Static kind string cache for performance
119 static std::unordered_map<NODE_KIND, const char *> kind_cache;
120 static bool cache_initialized = false;
121
122 if (!cache_initialized) {
123 kind_cache[NODE_KIND::NT_NOT] = "not";
124 kind_cache[NODE_KIND::NT_AND] = "and";
125 kind_cache[NODE_KIND::NT_OR] = "or";
126 kind_cache[NODE_KIND::NT_ADD] = "+";
127 kind_cache[NODE_KIND::NT_MUL] = "*";
128 kind_cache[NODE_KIND::NT_SUB] = "-";
129 kind_cache[NODE_KIND::NT_EQ] = "=";
130 kind_cache[NODE_KIND::NT_LE] = "<=";
131 kind_cache[NODE_KIND::NT_LT] = "<";
132 kind_cache[NODE_KIND::NT_GE] = ">=";
133 kind_cache[NODE_KIND::NT_GT] = ">";
134 kind_cache[NODE_KIND::NT_ITE] = "ite";
135 kind_cache[NODE_KIND::NT_IMPLIES] = "=>";
136 kind_cache[NODE_KIND::NT_XOR] = "xor";
137 kind_cache[NODE_KIND::NT_DIV_REAL] = "/";
138 kind_cache[NODE_KIND::NT_NEG] = "-";
139 kind_cache[NODE_KIND::NT_DISTINCT] = "distinct";
140 kind_cache[NODE_KIND::NT_FP_ADD] = "fp.add";
141 kind_cache[NODE_KIND::NT_FP_SUB] = "fp.sub";
142 kind_cache[NODE_KIND::NT_FP_MUL] = "fp.mul";
143 kind_cache[NODE_KIND::NT_FP_DIV] = "fp.div";
144 kind_cache[NODE_KIND::NT_FP_ABS] = "fp.abs";
145 kind_cache[NODE_KIND::NT_FP_NEG] = "fp.neg";
146 kind_cache[NODE_KIND::NT_FP_SQRT] = "fp.sqrt";
147 kind_cache[NODE_KIND::NT_FP_LE] = "fp.leq";
148 kind_cache[NODE_KIND::NT_FP_LT] = "fp.lt";
149 kind_cache[NODE_KIND::NT_FP_GE] = "fp.geq";
150 kind_cache[NODE_KIND::NT_FP_GT] = "fp.gt";
151 kind_cache[NODE_KIND::NT_FP_EQ] = "fp.eq";
152 cache_initialized = true;
153 }
154
155 // Optimized iterative implementation using minimal WorkItem structure
156 struct WorkItem {
157 DAGNode *node;
158 uint8_t action; // 0=process, 1=space, 2=close_paren, 3=close_paren_space,
159 // 4=text_output
160
161 WorkItem(DAGNode *n, uint8_t a = 0) : node(n), action(a) {}
162
163 // For text output
164 std::string text;
165 WorkItem(const std::string &t, uint8_t a = 4)
166 : node(nullptr), action(a), text(t) {}
167 };
168
169 // Pre-allocate stack with reasonable capacity to avoid frequent reallocations
170 std::vector<WorkItem> work_stack;
171 work_stack.reserve(65536); // Reserve space for deep expressions
172 work_stack.emplace_back(actualRoot.get(), 0);
173 while (!work_stack.empty()) {
174 WorkItem item = work_stack.back();
175 work_stack.pop_back();
176 // std::cout << (item.node == nullptr ? "nullptr" : item.node->toString())
177 // << std::endl;
178
179 if (item.action == 1) { // Write space
180 out << " ";
181 continue;
182 }
183 if (item.action == 2) { // Write closing paren
184 out << ")";
185 continue;
186 }
187 if (item.action == 3) { // Write ") " for special cases
188 out << ") ";
189 continue;
190 }
191 if (item.action == 4) { // Write text
192 out << item.text;
193 continue;
194 }
195
196 // Process node (action == 0)
197 DAGNode *node = item.node;
198 if (!node)
199 continue;
200
201 auto kind = node->getKind();
202 // std::cout << node->toString() << std::endl;
203 switch (kind) {
205 out << "true";
206 break;
208 out << "false";
209 break;
211 if (node->getName() == "(fp_bit_representation)" &&
212 node->getChildrenSize() == 3) {
213 auto sign = node->getChild(0).get();
214 auto exp = node->getChild(1).get();
215 auto mant = node->getChild(2).get();
216 out << "(fp ";
217 work_stack.emplace_back(nullptr, 2); // )
218 work_stack.emplace_back(mant, 0); // mant
219 work_stack.emplace_back(nullptr, 1); // space
220 work_stack.emplace_back(exp, 0); // exp
221 work_stack.emplace_back(nullptr, 1); // space
222 work_stack.emplace_back(sign, 0); // sign
223 }
224 else {
225 out << dumpConst(node->getName(), node->getSort());
226 }
227 break;
231 out << node->getName();
232 break;
234 out << "((as const ";
235 out << node->getSort()->toString();
236 out << ") ";
237 work_stack.emplace_back(nullptr, 2); // )
238 work_stack.emplace_back(node->getChild(0).get(), 0); // child0
239 break;
240 }
241
242 // Binary operations - optimized for common case
243 case NODE_KIND::NT_EQ:
244 case NODE_KIND::NT_LE:
245 case NODE_KIND::NT_LT:
246 case NODE_KIND::NT_GE:
247 case NODE_KIND::NT_GT:
252 if (node->getChildrenSize() == 2) {
253 auto child0 = node->getChild(0).get();
254 auto child1 = node->getChild(1).get();
255
256 const char *op = kind_cache[kind];
257 out << "(" << op << " ";
258
259 // Push in reverse order: close_paren, child1, space, child0
260 work_stack.emplace_back(nullptr, 2); // )
261 work_stack.emplace_back(child1, 0); // child1
262 work_stack.emplace_back(nullptr, 1); // space
263 work_stack.emplace_back(child0, 0); // child0
264 break;
265 }
266 // Fall through to n-ary case
267 [[fallthrough]];
268 }
269 // N-ary operations - most common case, highly optimized
271 case NODE_KIND::NT_OR:
273 const char *op = kind_cache[kind];
274 if (!op)
275 op = kindToString(kind).c_str();
276
277 out << "(" << op;
278 const auto &children = node->getChildren();
279
280 // Push closing paren first
281 work_stack.emplace_back(nullptr, 2);
282
283 // Push children in reverse order, each preceded by a space
284 for (int i = children.size() - 1; i >= 0; i--) {
285 auto child = children[i].get();
286 work_stack.emplace_back(child, 0);
287 work_stack.emplace_back(nullptr, 1); // space before child
288 }
289 break;
290 }
291
293 auto child0 = node->getChild(0).get();
294 auto child1 = node->getChild(1).get();
295 auto child2 = node->getChild(2).get();
296
297 out << "((_ re.loop ";
298
299 // Push: ), child0, ") ", child2, " ", child1
300 work_stack.emplace_back(nullptr, 2); // )
301 work_stack.emplace_back(child0, 0); // child0
302 work_stack.emplace_back(nullptr, 3); // ") " - special case
303 work_stack.emplace_back(child2, 0); // child2
304 work_stack.emplace_back(nullptr, 1); // space
305 work_stack.emplace_back(child1, 0); // child1
306 break;
307 }
308 // Unary operations
310 case NODE_KIND::NT_NEG: {
311 auto child = node->getChild(0).get();
312 const char *op = kind_cache[kind];
313
314 out << "(" << op << " ";
315 work_stack.emplace_back(nullptr, 2); // )
316 work_stack.emplace_back(child, 0); // child
317 break;
318 }
319
320 // Ternary operations
321 case NODE_KIND::NT_ITE: {
322 auto child0 = node->getChild(0).get();
323 auto child1 = node->getChild(1).get();
324 auto child2 = node->getChild(2).get();
325
326 out << "(ite ";
327 work_stack.emplace_back(nullptr, 2); // )
328 work_stack.emplace_back(child2, 0); // child2
329 work_stack.emplace_back(nullptr, 1); // space
330 work_stack.emplace_back(child1, 0); // child1
331 work_stack.emplace_back(nullptr, 1); // space
332 work_stack.emplace_back(child0, 0); // child0
333 break;
334 }
335
336 // Special processing operations
338 auto child0 = node->getChild(0).get();
339 auto child1 = node->getChild(1).get();
340 auto child2 = node->getChild(2).get();
341
342 out << "((_ extract ";
343 work_stack.emplace_back(nullptr, 2); // )
344 work_stack.emplace_back(child0, 0); // child0
345 work_stack.emplace_back(nullptr, 3); // ") "
346 work_stack.emplace_back(child2, 0); // child2
347 work_stack.emplace_back(nullptr, 1); // space
348 work_stack.emplace_back(child1, 0); // child1
349 break;
350 }
357 auto child0 = node->getChild(0).get();
358 auto child1 = node->getChild(1).get();
359
360 out << "((_ " << kindToString(kind) << " ";
361 work_stack.emplace_back(nullptr, 2); // )
362 work_stack.emplace_back(child0, 0); // child0
363 work_stack.emplace_back(nullptr, 3); // ") "
364 work_stack.emplace_back(child1, 0); // child1
365 break;
366 }
367 // Datatype tester: ((_ is C) t)
369 auto child0 = node->getChild(0).get();
370 // node->getName() stores constructor symbol C
371 out << "((_ is " << node->getName() << ") ";
372 work_stack.emplace_back(nullptr, 2); // )
373 if (child0->isLetBindVar()) {
374 work_stack.emplace_back(child0->getChild(0)->getChild(0).get(), 0);
375 }
376 else
377 work_stack.emplace_back(child0->getChild(0).get(), 0); // t
378 // std::cout << kindToString(child0->getKind()) << ' ' << child0->getChildrenSize() << std::endl;
379 break;
380 }
381 // Datatype updater: ((_ update S) t u)
383 auto child0 = node->getChild(0).get();
384 auto child1 = node->getChild(1).get();
385 // node->getName() stores selector symbol S
386 out << "((_ update " << node->getName() << ") ";
387 work_stack.emplace_back(nullptr, 2); // )
388 work_stack.emplace_back(child1, 0); // u
389 work_stack.emplace_back(nullptr, 1); // space
390 work_stack.emplace_back(child0, 0); // t
391 break;
392 }
393 // Tuple select/update/project parameterized forms
395 auto t = node->getChild(0).get();
396 auto idx = node->getChild(1).get();
397 out << "((_ tuple.select ";
398 work_stack.emplace_back(nullptr, 2); // )
399 work_stack.emplace_back(t, 0);
400 work_stack.emplace_back(nullptr, 3); // ") "
401 work_stack.emplace_back(idx, 0);
402 break;
403 }
405 auto t = node->getChild(0).get();
406 auto idx = node->getChild(1).get();
407 auto val = node->getChild(2).get();
408 out << "((_ tuple.update ";
409 work_stack.emplace_back(nullptr, 2); // )
410 work_stack.emplace_back(val, 0);
411 work_stack.emplace_back(nullptr, 1);
412 work_stack.emplace_back(t, 0);
413 work_stack.emplace_back(nullptr, 3); // ") "
414 work_stack.emplace_back(idx, 0);
415 break;
416 }
418 // children: [t, i1, i2, ...]
419 out << "((_ tuple.project ";
420 work_stack.emplace_back(nullptr, 2); // )
421 // print t then close, but we need to print indices first then node
422 auto t = node->getChild(0).get();
423 work_stack.emplace_back(t, 0);
424 work_stack.emplace_back(nullptr, 3); // ") "
425 // push indices in reverse with spaces
426 for (int i = static_cast<int>(node->getChildrenSize()) - 1; i >= 1; --i) {
427 auto idx = node->getChild(i).get();
428 work_stack.emplace_back(idx, 0);
429 if (i != 1)
430 work_stack.emplace_back(nullptr, 1);
431 }
432 break;
433 }
435 // print as (tuple a b ...)
436 out << "(tuple";
437 work_stack.emplace_back(nullptr, 2); // )
438 for (int i = static_cast<int>(node->getChildrenSize()) - 1; i >= 0; --i) {
439 work_stack.emplace_back(node->getChild(i).get(), 0);
440 work_stack.emplace_back(nullptr, 1);
441 }
442 break;
443 }
445 out << "tuple.unit";
446 break;
447 }
448
449 // Constants
451 out << "pi";
452 break;
454 out << "e";
455 break;
457 // If node has a name (e.g., "(_ +oo eb sb)"), use it; otherwise use
458 // default
459 if (!node->getName().empty() && node->getName()[0] == '(') {
460 out << node->getName();
461 }
462 else {
463 out << "inf";
464 }
465 break;
467 // If node has a name (e.g., "(_ +oo eb sb)"), use it; otherwise use
468 // default
469 if (!node->getName().empty() && node->getName()[0] == '(') {
470 out << node->getName();
471 }
472 else {
473 out << "+inf";
474 }
475 break;
477 // If node has a name (e.g., "(_ -oo eb sb)"), use it; otherwise use
478 // default
479 if (!node->getName().empty() && node->getName()[0] == '(') {
480 out << node->getName();
481 }
482 else {
483 out << "-inf";
484 }
485 break;
487 // If node has a name (e.g., "(_ NaN eb sb)"), use it; otherwise use
488 // default
489 if (!node->getName().empty() && node->getName()[0] == '(') {
490 out << node->getName();
491 }
492 else {
493 out << "NaN";
494 }
495 break;
497 out << "epsilon";
498 break;
500 out << "+epsilon";
501 break;
503 out << "-epsilon";
504 break;
506 out << "re.none";
507 break;
509 out << "re.all";
510 break;
512 out << "re.allchar";
513 break;
514
515 // Quantifiers - handle inline for performance
518 out << "(" << kindToString(kind) << " (";
519 for (size_t i = 1; i < node->getChildrenSize(); i++) {
520 auto current_child = node->getChild(i).get();
521 if (i == 1) {
522 out << "(" << current_child->getName() << " "
523 << current_child->getSort()->toString() << ")";
524 }
525 else {
526 out << " (" << current_child->getName() << " "
527 << current_child->getSort()->toString() << ")";
528 }
529 }
530 out << ") ";
531 work_stack.emplace_back(nullptr, 2); // )
532 work_stack.emplace_back(node->getChild(0).get(), 0); // body
533 break;
534 }
535
537 out << node->getName();
538 break;
539
540 // Function related
545 out << node->getName();
546 break;
547
548 // Function applications
550 // For NT_FUNC_APPLY, children[0] is function, children[1..] are
551 // parameters If no parameters (size <= 1), output just the name without
552 // parentheses
553 if (node->getChildrenSize() <= 1) {
554 out << node->getName();
555 }
556 else {
557 out << "(" << node->getName();
558 work_stack.emplace_back(nullptr, 2); // )
559 for (int i = node->getChildrenSize() - 1; i >= 1; i--) {
560 auto current_child = node->getChild(i).get();
561 work_stack.emplace_back(current_child, 0);
562 work_stack.emplace_back(nullptr, 1); // space
563 }
564 }
565 break;
566 }
568 // For pattern applications, all children are patterns
569 // If no patterns (size == 0), output just the name without parentheses
570 const auto &children = node->getChildren();
571 if (children.empty()) {
572 out << node->getName();
573 }
574 else {
575 out << node->getName();
576 work_stack.emplace_back(nullptr, 2); // )
577 for (int i = children.size() - 1; i >= 0; --i) { // Start from 0
578 auto current_child = children[i].get();
579 if (current_child->isLetBindVar()) {
580 work_stack.emplace_back(")", 4); // )
581 }
582 work_stack.emplace_back(current_child, 0);
583 if (current_child->isLetBindVar()) {
584 work_stack.emplace_back("(", 4); // (
585 }
586 if (i)
587 work_stack.emplace_back(nullptr, 1); // space
588 }
589 work_stack.emplace_back(" (", 4); // " ("
590 }
591 break;
592 }
593
595 out << node->getName() << ' ';
596 if (node->getChild(0)->isLetBindVar()) {
597 out << "(";
598 work_stack.emplace_back(")", 4); // )
599 }
600 work_stack.emplace_back(node->getChild(0).get(), 0);
601 break;
602 }
603
605 out << node->getName() << ' ';
606 out << node->getChild(0)->getName();
607 break;
608 }
609
611 const auto &children = node->getChildren();
612 out << "(! ";
613 work_stack.emplace_back(nullptr, 2); // )
614 for (int i = children.size() - 1; i >= 0; --i) { // Start from 0
615 if (i && children[i]->isLetBindVar()) {
616 work_stack.emplace_back(")", 4); // )
617 }
618 auto current_child = children[i].get();
619 work_stack.emplace_back(current_child, 0);
620 if (i && current_child->isLetBindVar()) {
621 work_stack.emplace_back("(", 4); // (
622 }
623 if (i)
624 work_stack.emplace_back(nullptr, 1); // space
625 }
626
627 break;
628 }
629
630 case NODE_KIND::NT_LET: {
631 condAssert(node->getChildrenSize() > 0,
632 "NT_LET should have at least one child");
633
634 // Output: (let ((var1 value1) (var2 value2) ...) body)
635 // Stack order (reverse): ) + body + ") " + bindings + "(let ("
636
637 work_stack.emplace_back(")", 4); // final )
638 work_stack.emplace_back(node->getChild(0).get(), 0); // body
639 work_stack.emplace_back(") ", 4); // close binding list and add space
640
641 // Process bindings in reverse order for stack
642 for (int i = node->getChildrenSize() - 1; i >= 1; i--) {
643 work_stack.emplace_back(")", 4); // ) for this binding
644 work_stack.emplace_back(node->getChild(i)->getChild(0).get(),
645 0); // binding value
646 work_stack.emplace_back(" ", 4); // space before value
647 work_stack.emplace_back(node->getChild(i)->getPureName(),
648 4); // variable name
649 work_stack.emplace_back("(", 4); // ( for this binding
650 if (i > 1) {
651 work_stack.emplace_back(" ", 4); // space between bindings
652 }
653 }
654
655 work_stack.emplace_back("(let (", 4); // opening
656 break;
657 }
659 // For let-chain: (let ((var1 val1)) (let ((var2 val2)) body))
660 // This creates nested let expressions
661
662 size_t num_bindings = node->getChildrenSize() - 1;
663
664 // Add closing parentheses for all let expressions (in reverse order)
665 for (size_t i = 0; i < num_bindings; i++) {
666 work_stack.emplace_back(")", 4); // ) for each let
667 }
668
669 // Add body
670 work_stack.emplace_back(node->getChild(node->getChildrenSize() - 1).get(),
671 0);
672
673 // Process let bindings in reverse order
674 for (int i = num_bindings - 1; i >= 0; i--) {
675 condAssert(node->getChild(i)->isLetBindVarList(),
676 "NT_LET_CHAIN: child is not LET_BIND_VAR_LIST");
677 auto var_list = node->getChild(i);
678
679 work_stack.emplace_back(") ", 4); // close binding list and add space
680
681 // Process variables in this binding list (reverse order)
682 for (int j = var_list->getChildrenSize() - 1; j >= 0; j--) {
683 work_stack.emplace_back(")", 4); // ) for binding
684 work_stack.emplace_back(var_list->getChild(j)->getChild(0).get(),
685 0); // binding value
686 work_stack.emplace_back(" ", 4); // space before value
687 work_stack.emplace_back(var_list->getChild(j)->getPureName(),
688 4); // variable name
689 work_stack.emplace_back("(", 4); // ( for binding
690 if (j > 0) {
691 work_stack.emplace_back(" ", 4); // space between bindings
692 }
693 }
694
695 work_stack.emplace_back("(let (", 4); // let opening
696 }
697
698 break;
699 }
701 out << node->getPureName();
702 break;
703 }
705 // Output: ( (var1 value1) (var2 value2) ... )
706 // Stack order (reverse): ) + bindings + "( "
707
708 work_stack.emplace_back(")", 4); // final )
709
710 // Process bindings in reverse order for stack
711 for (int i = node->getChildrenSize() - 1; i >= 1; i--) {
712 work_stack.emplace_back(")", 4); // ) for this binding
713 work_stack.emplace_back(node->getChild(i)->getChild(0).get(),
714 0); // binding value
715 work_stack.emplace_back(" ", 4); // space before value
716 work_stack.emplace_back(node->getChild(i)->getPureName(),
717 4); // variable name
718 work_stack.emplace_back("(", 4); // ( for this binding
719 if (i > 1) {
720 work_stack.emplace_back(" ", 4); // space between bindings
721 }
722 }
723
724 work_stack.emplace_back("( ", 4); // opening "( "
725 break;
726 }
727
730 // For UF applications, all children are parameters (no function
731 // definition in children) If no parameters (size == 0), output just the
732 // name without parentheses
733 const auto &children = node->getChildren();
734 if (children.empty()) {
735 out << node->getName();
736 }
737 else {
738 out << "(" << node->getName();
739 work_stack.emplace_back(nullptr, 2); // )
740 for (int i = children.size() - 1; i >= 0; i--) { // Start from 0
741 auto current_child = children[i].get();
742 work_stack.emplace_back(current_child, 0);
743 work_stack.emplace_back(nullptr, 1); // space
744 }
745 }
746 break;
747 }
748
750 // For recursive function applications, children[0] is the function
751 // definition, children[1..] are parameters Only output the parameters,
752 // not the function definition If no parameters (size <= 1), output just
753 // the name without parentheses
754 const auto &children = node->getChildren();
755 if (children.size() <= 1) {
756 out << node->getName();
757 }
758 else {
759 out << "(" << node->getName();
760 work_stack.emplace_back(nullptr, 2); // )
761 for (int i = children.size() - 1; i >= 1; i--) { // Start from 1, not 0
762 auto current_child = children[i].get();
763 work_stack.emplace_back(current_child, 0);
764 work_stack.emplace_back(nullptr, 1); // space
765 }
766 }
767 break;
768 }
769
770 // FLOATING POINT OPERATIONS
778 std::string kind_str = kindToString(kind);
779 out << "(" << kind_str;
780 work_stack.emplace_back(nullptr, 2); // )
781 const auto &children = node->getChildren();
782 for (int i = children.size() - 1; i >= 0; i--) {
783 auto current_child = children[i].get();
784 work_stack.emplace_back(current_child, 0);
785 work_stack.emplace_back(nullptr, 1); // space
786 }
787 break;
788 }
789
792 // fp.sqrt and fp.roundToIntegral require rounding mode and input:
793 // (fp.sqrt <rm> <arg>)
794 std::string kind_str = kindToString(kind);
795 out << "(" << kind_str;
796 work_stack.emplace_back(nullptr, 2); // )
797 const auto &children = node->getChildren();
798 for (int i = children.size() - 1; i >= 0; i--) {
799 auto current_child = children[i].get();
800 work_stack.emplace_back(current_child, 0);
801 work_stack.emplace_back(nullptr, 1); // space
802 }
803 break;
804 }
805
816 std::string kind_str = kindToString(kind);
817 auto child = node->getChild(0).get();
818 out << "(" << kind_str << " ";
819 work_stack.emplace_back(nullptr, 2); // )
820 work_stack.emplace_back(child, 0); // child
821 break;
822 }
823
825 auto child0 = node->getChild(0).get();
826 auto child1 = node->getChild(1).get();
827 out << "(fp.rem ";
828 work_stack.emplace_back(nullptr, 2); // )
829 work_stack.emplace_back(child1, 0); // child1
830 work_stack.emplace_back(nullptr, 1); // space
831 work_stack.emplace_back(child0, 0); // child0
832 break;
833 }
834
839 case NODE_KIND::NT_FP_EQ: {
840 std::string kind_str = kindToString(kind);
841 // auto child0 = node->getChild(0).get();
842 // auto child1 = node->getChild(1).get();
843 // out << "(" << kind_str << " ";
844 // work_stack.emplace_back(nullptr, 2); // )
845 // work_stack.emplace_back(child1, 0); // child1
846 // work_stack.emplace_back(nullptr, 1); // space
847 // work_stack.emplace_back(child0, 0); // child0
848 out << "(" << kind_str;
849 work_stack.emplace_back(nullptr, 2); // )
850 const auto &children = node->getChildren();
851 for (int i = children.size() - 1; i >= 0; i--) {
852 auto current_child = children[i].get();
853 work_stack.emplace_back(current_child, 0);
854 work_stack.emplace_back(nullptr, 1); // space
855 }
856 break;
857 }
858
861 // Format: ((_ fp.to_ubv size) rm fp) or ((_ fp.to_sbv size) rm fp)
862 std::string kind_str = kindToString(kind);
863 auto rm = node->getChild(0).get();
864 auto fp = node->getChild(1).get();
865 auto size = node->getChild(2).get();
866 out << "((_ " << kind_str << " ";
867 work_stack.emplace_back(nullptr, 2); // )
868 work_stack.emplace_back(fp, 0); // fp
869 work_stack.emplace_back(nullptr, 1); // space
870 work_stack.emplace_back(rm, 0); // rm
871 work_stack.emplace_back(nullptr, 3); // ") "
872 work_stack.emplace_back(size, 0); // size
873 break;
874 }
875
877 auto eb = node->getChild(0).get();
878 auto sb = node->getChild(1).get();
879
880 if (node->getChildrenSize() == 4) {
881 // 4-parameter version: ((_ to_fp eb sb) rm param)
882 auto rm = node->getChild(2).get();
883 auto param = node->getChild(3).get();
884 out << "((_ to_fp ";
885 work_stack.emplace_back(nullptr, 2); // )
886 work_stack.emplace_back(param, 0); // param
887 work_stack.emplace_back(nullptr, 1); // space
888 work_stack.emplace_back(rm, 0); // rm
889 work_stack.emplace_back(nullptr, 3); // ") "
890 work_stack.emplace_back(sb, 0); // sb
891 work_stack.emplace_back(nullptr, 1); // space
892 work_stack.emplace_back(eb, 0); // eb
893 }
894 else if (node->getChildrenSize() == 3) {
895 // 3-parameter version: ((_ to_fp eb sb) param)
896 auto param = node->getChild(2).get();
897 out << "((_ to_fp ";
898 work_stack.emplace_back(nullptr, 2); // )
899 work_stack.emplace_back(param, 0); // param
900 work_stack.emplace_back(nullptr, 3); // ") "
901 work_stack.emplace_back(sb, 0); // sb
902 work_stack.emplace_back(nullptr, 1); // space
903 work_stack.emplace_back(eb, 0); // eb
904 }
905 else {
906 // Invalid number of children
907 out << "INVALID_FP_TO_FP_NODE";
908 }
909 break;
910 }
911
913 auto eb = node->getChild(0).get();
914 auto sb = node->getChild(1).get();
915 auto rm = node->getChild(2).get();
916 auto param = node->getChild(3).get();
917 out << "((_ to_fp_unsigned ";
918 work_stack.emplace_back(nullptr, 2); // )
919 work_stack.emplace_back(param, 0); // param
920 work_stack.emplace_back(nullptr, 1); // space
921 work_stack.emplace_back(rm, 0); // rm
922 work_stack.emplace_back(nullptr, 3); // ") "
923 work_stack.emplace_back(sb, 0); // sb
924 work_stack.emplace_back(nullptr, 1); // space
925 work_stack.emplace_back(eb, 0); // eb
926 break;
927 }
928
930 auto expr = node->getChild(0).get();
931 auto index = node->getChild(1).get();
932 out << "(root-obj ";
933 work_stack.emplace_back(nullptr, 2); // )
934 work_stack.emplace_back(index, 0); // index
935 work_stack.emplace_back(nullptr, 1); // space
936 work_stack.emplace_back(expr, 0); // expr
937 break;
938 }
939
941 out << "(root-of-with-interval (coeffs ";
942
943 // Stack order (LIFO - Last In First Out):
944 // We want output: (root-of-with-interval (coeffs c1 c2 ...) lower upper)
945 // So we push in reverse order:
946
947 // 1. Push closing parenthesis for root-of-with-interval (will be printed
948 // last)
949 work_stack.emplace_back(nullptr, 2); // )
950
951 // 2. Push upper bound
952 work_stack.emplace_back(node->getChild(node->getChildrenSize() - 1).get(),
953 0);
954 work_stack.emplace_back(nullptr, 1); // space
955
956 // 3. Push lower bound
957 work_stack.emplace_back(node->getChild(node->getChildrenSize() - 2).get(),
958 0);
959 work_stack.emplace_back(nullptr, 1); // space
960
961 // 4. Push closing parenthesis for coeffs list
962 work_stack.emplace_back(nullptr, 2); // )
963
964 // 5. Push coefficients in reverse order (so they print in correct order)
965 for (int i = node->getChildrenSize() - 3; i >= 0; i--) {
966 work_stack.emplace_back(node->getChild(i).get(), 0);
967 work_stack.emplace_back(nullptr, 1); // space
968 }
969
970 break;
971 }
972
973 default: {
974 // Fallback for other cases - iterative version
975 std::string kind_str = kindToString(kind);
976 const auto &children = node->getChildren();
977
978 if (children.empty()) {
979 out << kind_str;
980 }
981 else if (children.size() == 1) {
982 auto child = children[0].get();
983 out << "(" << kind_str << " ";
984 work_stack.emplace_back(nullptr, 2); // )
985 work_stack.emplace_back(child, 0); // child
986 }
987 else {
988 out << "(" << kind_str;
989 work_stack.emplace_back(nullptr, 2); // )
990 for (int i = children.size() - 1; i >= 0; i--) {
991 auto child = children[i].get();
992 work_stack.emplace_back(child, 0);
993 work_stack.emplace_back(nullptr, 1); // space
994 }
995 }
996 break;
997 }
998 }
999 }
1000}
1001
1002// Wrapper function that returns string for compatibility
1003std::string dumpSMTLIB2(const std::shared_ptr<DAGNode> &root) {
1004 std::ostringstream oss;
1005 dumpSMTLIB2_streaming(root, oss); // enable cache by default
1006 return oss.str();
1007}
1008
1009std::string dumpFuncDef(const std::shared_ptr<DAGNode> &node) {
1010 std::string res = "(define-fun " + node->getName() + " (";
1011 // For NT_FUNC_DEF, children[0] is body, children[1..n] are parameters
1012 for (size_t i = 1; i < node->getChildrenSize(); i++) {
1013 if (i == 1)
1014 res += "(" + node->getChild(i)->getName() + " " +
1015 node->getChild(i)->getSort()->toString() + ")";
1016 else
1017 res += " (" + node->getChild(i)->getName() + " " +
1018 node->getChild(i)->getSort()->toString() + ")";
1019 }
1020 // Use node's declared sort, not body's inferred sort (which may be widened to
1021 // IntOrReal)
1022 res += ") " + node->getSort()->toString() + " ";
1023 res += dumpSMTLIB2(node->getChild(0)) + ")";
1024 return res;
1025}
1026std::string dumpFuncRec(const std::shared_ptr<DAGNode> &node) {
1027 std::string res = "(define-fun-rec " + node->getName() + " (";
1028 // For NT_FUNC_REC, children[0] is body, children[1..n] are parameters
1029 for (size_t i = 1; i < node->getChildrenSize(); i++) {
1030 if (i == 1)
1031 res += "(" + node->getChild(i)->getName() + " " +
1032 node->getChild(i)->getSort()->toString() + ")";
1033 else
1034 res += " (" + node->getChild(i)->getName() + " " +
1035 node->getChild(i)->getSort()->toString() + ")";
1036 }
1037 // Use node's declared sort, not body's inferred sort (which may be widened to
1038 // IntOrReal)
1039 res += ") " + node->getSort()->toString() + " ";
1040 res += dumpSMTLIB2(node->getChild(0)) + ")";
1041 return res;
1042}
1043std::string dumpFuncDec(const std::shared_ptr<DAGNode> &node) {
1044 std::string res = "(declare-fun " + node->getName() + " (";
1045 // For NT_FUNC_DEC, children[0] is NULL_NODE, children[1..n] are parameter
1046 // type nodes
1047 for (size_t i = 1; i < node->getChildrenSize(); i++) {
1048 if (i == 1)
1049 res += node->getChild(i)->getSort()->toString();
1050 else
1051 res += " " + node->getChild(i)->getSort()->toString();
1052 }
1053 // The return type is stored in the node's sort, not in children[0]
1054 res += ") " + node->getSort()->toString() + ")";
1055 return res;
1056}
1057
1058// NodeManager
1059
1060// Static constant nodes are now defined inline in the header file
1061
1063 // 1. Reserve before inserting anything
1064 nodes.reserve(65536);
1065 for (size_t i = 0; i < NUM_KINDS; i++) {
1066 NODE_KIND kind = static_cast<NODE_KIND>(i);
1067 if (static_kinds.count(kind) > 0) {
1068 node_buckets[i].reserve(1);
1069 }
1070 else {
1071 node_buckets[i].reserve(8192);
1072 }
1073 }
1074 // 2. Initialize static constant nodes and insert them into buckets
1076}
1077
1079
1080std::shared_ptr<DAGNode> NodeManager::getNode(const size_t index) const {
1081 return nodes[index];
1082}
1083size_t NodeManager::getIndex(const std::shared_ptr<DAGNode> &node) const {
1084 auto bucket_index = static_cast<size_t>(node->getKind());
1085 auto &kind_bucket = node_buckets[bucket_index];
1086 size_t node_hash = node->hashCode();
1087
1088 auto hash_it = kind_bucket.find(node_hash);
1089 if (hash_it != kind_bucket.end()) {
1090 for (const auto &pair : hash_it->second) {
1091 if (pair.first.get() == node.get() || pair.first->isEquivalentTo(*node)) {
1092 return pair.second;
1093 }
1094 }
1095 }
1096 return -1;
1097}
1098size_t NodeManager::size() const { return nodes.size(); }
1099
1101 // Clear non-static nodes only (preserve static constants)
1102 for (size_t i = static_node_count; i < nodes.size(); i++) {
1103 nodes[i]->clear();
1104 }
1105 nodes.clear();
1106
1107 // Clear hash buckets and re-insert static nodes
1108 for (auto &bucket : node_buckets) {
1109 bucket.clear();
1110 }
1111}
1112
1113std::shared_ptr<DAGNode>
1114NodeManager::insertNodeToBucket(const std::shared_ptr<DAGNode> &node) {
1115 auto bucket_index = static_cast<size_t>(node->getKind());
1116 auto &kind_bucket = node_buckets[bucket_index];
1117
1118 // if (node->isVRoundingMode())
1119 // node->setName(node->getSort()->name);
1120 // pre-calculate hash code to avoid repeated calculation
1121 size_t node_hash = node->hashCode();
1122 // if (node->isCRoundingMode())
1123 // std::cout << node_hash << std::endl;
1124
1125 // secondary hash lookup: first use hash code to locate the small bucket
1126 auto hash_it = kind_bucket.find(node_hash);
1127 if (hash_it != kind_bucket.end()) {
1128 // linear search in the small bucket
1129 for (const auto &pair : hash_it->second) {
1130 // optimize comparison order: the most likely different ones are in front,
1131 // to avoid the expensive isEquivalentTo
1132 if (pair.first.get() == node.get()) {
1133 // completely the same pointer, return directly
1134 auto node_ptr = nodes[pair.second];
1135 node_ptr->incUseCount();
1136 return node_ptr;
1137 }
1138 // fast structure comparison (avoid the expensive isEquivalentTo call)
1139 if (pair.first->getKind() == node->getKind() &&
1140 pair.first->getChildrenSize() == node->getChildrenSize() &&
1141 pair.first->getName() == node->getName()) {
1142 // only call the expensive isEquivalentTo when the structure matches
1143 // completely
1144 if (pair.first->isEquivalentTo(*node)) {
1145 auto node_ptr = nodes[pair.second];
1146 node_ptr->incUseCount();
1147 return node_ptr;
1148 }
1149 }
1150 }
1151 }
1152
1153 // size_t h1 = std::hash<std::string>{}(node->getSort()->toString());
1154 // size_t h2 = static_cast<size_t>(node->getKind());
1155 // size_t h3 = node->getName().empty() ? 0 :
1156 // std::hash<std::string>{}(node->getName()); size_t h4 =
1157 // node->getChildrenSize();
1158 // // size_t h5 = children_hash.empty() ? 0 :
1159 // std::hash<std::string>{}(children_hash); std::cout << node->getName() << '
1160 // ' << h1 << ' ' << h2 << ' ' << h3 << ' ' << h4 << ' ' /*<<h5*/ <<
1161 // std::endl; std::cout << node->getSort()->toString() << ' ' <<
1162 // kindToString(node->getKind()) << ' ' << node->getName() << ' ' <<
1163 // node->hashCode() << std::endl;
1164
1165 // node does not exist, insert new node
1166 size_t new_index = nodes.size();
1167 kind_bucket[node_hash].emplace_back(node, new_index);
1168 nodes.emplace_back(node);
1169 node->incUseCount();
1170 return node;
1171}
1172
1202
1203std::shared_ptr<DAGNode>
1204NodeManager::createNode(std::shared_ptr<Sort> sort, NODE_KIND kind, std::string name, std::vector<std::shared_ptr<DAGNode>> children) {
1205 // if (kind == NODE_KIND::NT_BV_OR) {
1206 // for (auto& child : children) {
1207 // std::cout << child->getName() << ' ';
1208 // }
1209 // std::cout << std::endl;
1210 // }
1211 auto node = std::make_shared<DAGNode>(sort, kind, name, children);
1212 return insertNodeToBucket(node);
1213}
1214
1215std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1216 NODE_KIND kind,
1217 std::string name) {
1218 auto node = std::make_shared<DAGNode>(sort, kind, name);
1219 return insertNodeToBucket(node);
1220}
1221
1222std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1223 NODE_KIND kind) {
1224 auto node = std::make_shared<DAGNode>(sort, kind);
1225 return insertNodeToBucket(node);
1226}
1227
1228std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort) {
1229 auto node = std::make_shared<DAGNode>(sort);
1230 return insertNodeToBucket(node);
1231}
1232
1233std::shared_ptr<DAGNode> NodeManager::createNode() {
1234 auto node = std::make_shared<DAGNode>();
1235 return insertNodeToBucket(node);
1236}
1237
1238std::shared_ptr<DAGNode> NodeManager::createNode(NODE_KIND kind,
1239 std::string name) {
1240 auto node = std::make_shared<DAGNode>(kind, name);
1241 return insertNodeToBucket(node);
1242}
1243
1244std::shared_ptr<DAGNode> NodeManager::createNode(NODE_KIND kind) {
1245 auto node = std::make_shared<DAGNode>(kind);
1246 return insertNodeToBucket(node);
1247}
1248
1249std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1250 const Integer &v) {
1251 auto node = std::make_shared<DAGNode>(sort, v);
1252 return insertNodeToBucket(node);
1253}
1254
1255std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1256 const Real &v) {
1257 auto node = std::make_shared<DAGNode>(sort, v);
1258 return insertNodeToBucket(node);
1259}
1260
1261std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1262 const double &v) {
1263 auto node = std::make_shared<DAGNode>(sort, v);
1264 return insertNodeToBucket(node);
1265}
1266
1267std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1268 const int &v) {
1269 auto node = std::make_shared<DAGNode>(sort, v);
1270 return insertNodeToBucket(node);
1271}
1272
1273std::shared_ptr<DAGNode> NodeManager::createNode(std::shared_ptr<Sort> sort,
1274 const bool &v) {
1275 auto node = std::make_shared<DAGNode>(sort, v);
1276 return insertNodeToBucket(node);
1277}
1278
1279std::shared_ptr<DAGNode> NodeManager::createNode(const std::string &n) {
1280 auto node = std::make_shared<DAGNode>(n);
1281 return insertNodeToBucket(node);
1282}
1283
1284} // namespace stabilizer::parser
#define condAssert(cond, msg)
Definition asserting.h:35
std::vector< std::shared_ptr< DAGNode > > getChildren() const
Get the children of the node.
Definition dag.h:977
std::vector< std::shared_ptr< DAGNode > > children
Definition dag.h:81
void updateFuncDef(std::shared_ptr< Sort > out_sort, std::shared_ptr< DAGNode > body, const std::vector< std::shared_ptr< DAGNode > > &params, bool is_rec=false)
Update the function definition.
Definition dag.cpp:43
std::string getName() const
Get the name of the node.
Definition dag.h:921
size_t getChildrenSize() const
Get the number of children of the node.
Definition dag.h:970
void updateApplyFunc(std::shared_ptr< Sort > out_sort, std::shared_ptr< DAGNode > body, const std::vector< std::shared_ptr< DAGNode > > &params, bool is_rec=false)
Update the function application.
Definition dag.cpp:57
std::shared_ptr< Sort > getSort() const
Get the sort of the node.
Definition dag.h:915
std::string getPureName() const
Definition dag.h:900
NODE_KIND getKind() const
Get the kind of the node.
Definition dag.h:939
std::shared_ptr< DAGNode > getChild(int i) const
Get the child of the node.
Definition dag.h:987
std::shared_ptr< Sort > sort
Definition dag.h:77
static const std::shared_ptr< DAGNode > E_NODE
Definition dag.h:1279
static const std::shared_ptr< DAGNode > INT_INF_NODE
Definition dag.h:1315
static const std::shared_ptr< DAGNode > STR_NEG_INF_NODE
Definition dag.h:1311
std::shared_ptr< DAGNode > createNode()
Definition dag.cpp:1233
size_t getIndex(const std::shared_ptr< DAGNode > &node) const
Definition dag.cpp:1083
static const std::shared_ptr< DAGNode > POS_EPSILON_NODE
Definition dag.h:1295
std::array< std::unordered_map< size_t, std::vector< std::pair< std::shared_ptr< DAGNode >, size_t > > >, NUM_KINDS > node_buckets
Definition dag.h:1206
static const std::shared_ptr< DAGNode > PI_NODE
Definition dag.h:1281
static const std::shared_ptr< DAGNode > UNKNOWN_NODE
Definition dag.h:1269
static const std::shared_ptr< DAGNode > NAN_NODE
Definition dag.h:1291
static const std::shared_ptr< DAGNode > REAL_INF_NODE
Definition dag.h:1325
static const std::shared_ptr< DAGNode > STR_POS_INF_NODE
Definition dag.h:1307
std::vector< std::shared_ptr< DAGNode > > nodes
Definition dag.h:1199
static const std::shared_ptr< DAGNode > ERROR_NODE
Definition dag.h:1273
std::shared_ptr< DAGNode > getNode(const size_t index) const
Definition dag.cpp:1080
static const std::shared_ptr< DAGNode > REAL_NEG_INF_NODE
Definition dag.h:1331
static const std::shared_ptr< DAGNode > NEG_EPSILON_NODE
Definition dag.h:1299
static const std::shared_ptr< DAGNode > REAL_POS_INF_NODE
Definition dag.h:1327
static const std::shared_ptr< DAGNode > TRUE_NODE
Definition dag.h:1275
static const std::shared_ptr< DAGNode > STR_INF_NODE
Definition dag.h:1305
static const std::shared_ptr< DAGNode > NULL_NODE
Definition dag.h:1267
static const std::shared_ptr< DAGNode > INT_POS_INF_NODE
Definition dag.h:1317
static const std::shared_ptr< DAGNode > INT_NEG_INF_NODE
Definition dag.h:1321
static const std::shared_ptr< DAGNode > EPSILON_NODE
Definition dag.h:1293
static const std::shared_ptr< DAGNode > FALSE_NODE
Definition dag.h:1277
std::shared_ptr< DAGNode > insertNodeToBucket(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1114
std::string kindToString(const NODE_KIND &nk)
Definition kind.cpp:35
const std::unordered_set< NODE_KIND > static_kinds
Definition kind.h:367
std::string dumpFuncDef(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1009
std::string dumpSMTLIB2(const std::shared_ptr< DAGNode > &root)
Definition dag.cpp:1003
std::string dumpConst(const std::string &name, const std::shared_ptr< Sort > &sort)
Definition dag.cpp:69
std::string dumpFuncDec(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1043
void dumpSMTLIB2_streaming(const std::shared_ptr< DAGNode > &root, std::ostream &out)
Definition dag.cpp:111
constexpr size_t NUM_KINDS
Definition kind.h:366
std::string dumpFuncRec(const std::shared_ptr< DAGNode > &node)
Definition dag.cpp:1026