SMTStabilizer API
Public API documentation for SMTStabilizer
Loading...
Searching...
No Matches
kernel.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 XiangZhang
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21#include "kernel.h"
22
23#include <algorithm>
24#include <cstddef>
25#include <cstdint>
26#include <numeric>
27#include <random>
28#include <string>
29#include <unordered_map>
30#include <utility>
31#include <vector>
32
33#include "node/node_manager.h"
34#include "parser/kind.h"
35#include "parser/sort.h"
36#include "util/node_helper.h"
37
39
48Kernel::Kernel(node::NodeManager &nm, const bool &context_propagation, const bool &symmetry_breaking_perturbation) : d_context_propagation(context_propagation), d_symmetry_breaking_perturbation(symmetry_breaking_perturbation) {
49 std::vector<node::Node> visit(nm.assertions());
50 std::unordered_map<node::Node, size_t> node2index;
51 // node2index.emplace(nm.assertion(), 0);
52
53 while (!visit.empty()) {
54 auto node = visit.back();
55 auto [itr, success] = node2index.emplace(node, SIZE_MAX);
56 if (success) {
57 for (const auto &child : node->getChildren()) {
58 visit.emplace_back(child);
59 }
60 if (node->isUFApplication())
61 visit.emplace_back(nm.mkUFVNode(node->getName(), node->getSort()));
62 }
63 else if (itr->second == SIZE_MAX) {
64 // std::cout << node->toString() << ' ' << node->getSort()->isDec() << std::endl;
65 visit.pop_back();
66 itr->second = d_nodes.size();
67 d_nodes.emplace_back(node);
68 d_is_commutative.emplace_back(is_commutative(d_nodes.size() - 1, false));
69 d_is_symbol.emplace_back(node->isVar() || (node->isArray() && !node->isConstArray()) || node->isVUF() || node->isUFName());
70 d_graph.emplace_back();
71 d_reversed_graph.emplace_back();
72 if (d_is_symbol.back())
73 d_symbols.emplace_back(d_nodes.size() - 1);
74
75 d_hash_table.emplace_back(util::hash_node(node));
76 for (size_t i = 0, isz = node->getChildrenSize(); i < isz; ++i) {
77 const auto &child = node->getChild(i);
78 const size_t &child_index = node2index.at(child);
79 d_graph.back().emplace_back(child_index);
80 if (child->getKind() == stabilizer::parser::NODE_KIND::NT_FP_ADD ||
81 child->getKind() == stabilizer::parser::NODE_KIND::NT_FP_MUL ||
82 child->getKind() == stabilizer::parser::NODE_KIND::NT_EXISTS ||
84 d_reversed_graph.at(child_index).emplace_back(d_nodes.size() - 1, std::min<size_t>(i, 0));
85 else
86 d_reversed_graph.at(child_index).emplace_back(d_nodes.size() - 1, d_is_commutative.back() ? 0 : i);
87 }
88 if (node->isUFApplication()) {
89 const size_t &uf_index = node2index.at(nm.mkUFVNode(node->getName(), node->getSort()));
90 d_graph.back().emplace_back(uf_index);
91 d_reversed_graph.at(uf_index).emplace_back(d_nodes.size() - 1, node->getChildrenSize());
92 }
93 }
94 else
95 visit.pop_back();
96 }
97
98 d_processing.resize(d_nodes.size());
99 std::iota(d_processing.begin(), d_processing.end(), 0);
100 d_visited.resize(d_nodes.size(), false);
101 d_symbol_num = d_symbols.size();
102
103 for (const auto &assertion : nm.assertions()) {
104 util::hash_combine(d_hash_table.at(node2index.at(assertion)), static_cast<size_t>(parser::NODE_KIND::NT_AND));
105 }
106 // std::cout << d_reversed_graph.at(d_symbols.at(0)).size() << ' ' << d_reversed_graph.at(d_symbols.at(1)).size() << std::endl;
107 // std::cout << d_nodes.size() << std::endl;
108}
109
110bool Kernel::is_commutative(const size_t &i, const bool &from_cache) {
111 // return false;
112 if (from_cache)
113 return d_is_commutative.at(i);
114 const auto &node = d_nodes.at(i);
115 switch (node->getKind()) {
141
142 // Quantifiers are treated as commutative for hashing purposes
145
146 // FP_ADD, FP_MUL, SUB, DIV_REAL are commutative from index one
149 // case stabilizer::parser::NODE_KIND::NT_SUB:
150 // case stabilizer::parser::NODE_KIND::NT_DIV_REAL:
151 return true;
152 default:
153 return false;
154 }
155}
156
157void Kernel::propagate(const std::vector<size_t> &processing) {
159 static std::vector<size_t> pre_hash_table = d_hash_table;
160 for (const auto &node : processing)
161 pre_hash_table.at(node) = d_hash_table.at(node);
162 std::vector<size_t> v(processing);
163 for (const auto &node : v) {
164 const auto &children = d_graph.at(node);
165 if (children.empty()) continue;
166 if (is_commutative(node)) {
167 util::hash_communative_children(pre_hash_table.at(node), children, d_hash_table);
168 }
169 else {
170 util::hash_children(pre_hash_table.at(node), children, d_hash_table);
171 }
172 util::hash_parents(pre_hash_table.at(node), d_reversed_graph.at(node), d_hash_table);
173 }
174
175 for (const auto &node : v)
176 d_hash_table.at(node) = pre_hash_table.at(node);
177 }
178 else {
179 for (const auto &node : processing) {
180 const auto &children = d_graph.at(node);
181 if (children.empty()) continue;
182 if (is_commutative(node)) {
184 }
185 else {
186 util::hash_children(d_hash_table.at(node), children, d_hash_table);
187 }
188 }
189 for (auto it = processing.rbegin(); it != processing.rend(); ++it) {
190 const auto &node = *it;
191 const auto &parents = d_reversed_graph.at(node);
192 if (parents.empty())
193 continue;
194 util::hash_parents(d_hash_table.at(node), parents, d_hash_table);
195 }
196 }
197}
198
205void Kernel::context_propagate(const std::vector<size_t> &processing, const std::vector<size_t> &symbols) {
206 size_t hash_count = 0, prev_hash_count = 0;
207 std::vector<size_t> unique_hashes(symbols.size());
208 do {
209 prev_hash_count = hash_count;
210 propagate(processing);
211 std::transform(symbols.begin(), symbols.end(), unique_hashes.begin(), [this](size_t index) { return d_hash_table.at(index); });
212 std::sort(unique_hashes.begin(), unique_hashes.end());
213 hash_count = 1;
214 for (size_t i = 1, isz = unique_hashes.size(); i < isz; ++i)
215 if (unique_hashes[i] != unique_hashes[i - 1])
216 ++hash_count;
217 } while (prev_hash_count != hash_count);
218}
219
230 std::unordered_map<size_t, size_t> hash_count;
231
232 for (const auto &node : d_symbols) {
233 if (d_visited.at(node))
234 continue;
235 std::vector<size_t> processing;
236 std::vector<size_t> symbols;
237 d_visited.at(node) = true;
238 std::vector<size_t> stack{node};
239 while (!stack.empty()) {
240 auto current = stack.back();
241 stack.pop_back();
242 processing.emplace_back(current);
243 if (d_is_symbol.at(current)) {
244 symbols.emplace_back(current);
245 // std::cout << d_hash_table.at(current) << ' ' << d_nodes.at(current)->toString() << std::endl;
246 }
247 for (const auto &[father_index, oper_index] : d_reversed_graph.at(current)) {
248 if (!d_visited.at(father_index)) {
249 d_visited.at(father_index) = true;
250 stack.emplace_back(father_index);
251 }
252 }
253 for (const auto &child_index : d_graph.at(current)) {
254 if (!d_visited.at(child_index)) {
255 d_visited.at(child_index) = true;
256 stack.emplace_back(child_index);
257 }
258 }
259 }
260
261 size_t symbols_hash = 0;
262 util::hash_communative_children(symbols_hash, symbols, d_hash_table);
263 hash_count[symbols_hash]++;
264 size_t count = hash_count[symbols_hash];
265 for (const auto &node : processing) {
266 util::hash_combine(d_hash_table.at(node), symbols_hash);
267 util::hash_combine(d_hash_table.at(node), count);
268 }
269
270 std::vector<size_t> symbol_hashes(symbols.size());
271 std::transform(symbols.begin(), symbols.end(), symbol_hashes.begin(), [this](size_t index) { return d_hash_table.at(index); });
272 std::sort(symbol_hashes.begin(), symbol_hashes.end());
273 size_t idx_hash = 0;
274 bool is_select = false;
275 for (size_t i = 1, isz = symbol_hashes.size(); i < isz; ++i) {
276 if (symbol_hashes[i] == symbol_hashes[i - 1]) {
277 idx_hash = symbol_hashes.at(i);
278 is_select = true;
279 break;
280 }
281 }
282
283 if (is_select) {
284 size_t idx = 0;
285 for (size_t i = 0, isz = symbols.size(); i < isz; ++i) {
286 if (d_hash_table.at(symbols.at(i)) == idx_hash) {
287 idx = symbols.at(i);
288 break;
289 }
290 }
292 std::sort(processing.begin(), processing.end());
293 context_propagate(processing, symbols);
294 }
295 }
296 for (const auto &node : d_processing)
297 d_visited.at(node) = false;
298}
299
307 std::unordered_map<size_t, size_t> hash_count;
308 for (const auto &node : d_processing)
309 hash_count[d_hash_table.at(node)]++;
310 for (const auto &node : d_processing) {
311 if (hash_count.at(d_hash_table.at(node)) > 1)
312 d_visited.at(node) = true;
313 }
314
315 d_symbols.clear();
316 std::vector<size_t> unique_symbols;
317 std::vector<size_t> new_processing;
318 for (const auto &node : d_processing) {
319 if (!d_visited.at(node)) {
320 d_graph.at(node).clear();
321 d_reversed_graph.at(node).clear();
322 if (d_is_symbol.at(node))
323 unique_symbols.emplace_back(node);
324 continue;
325 }
326 new_processing.emplace_back(node);
327 d_graph.at(node).erase(std::remove_if(d_graph.at(node).begin(), d_graph.at(node).end(), [this](const size_t &child_index) {
328 return !d_visited.at(child_index);
329 }),
330 d_graph.at(node).end());
331 d_reversed_graph.at(node).erase(std::remove_if(d_reversed_graph.at(node).begin(), d_reversed_graph.at(node).end(), [this](const std::pair<size_t, size_t> &father_info) {
332 return !d_visited.at(father_info.first);
333 }),
334 d_reversed_graph.at(node).end());
335 if (d_is_symbol.at(node) && d_visited.at(node))
336 d_symbols.emplace_back(node);
337 }
338 std::sort(unique_symbols.begin(), unique_symbols.end(), [this](const size_t &a, const size_t &b) {
339 if (d_context_hash.at(a) == d_context_hash.at(b))
340 return d_hash_table.at(a) < d_hash_table.at(b);
341 return d_context_hash.at(a) < d_context_hash.at(b);
342 });
343 for (const auto &node : unique_symbols) {
344 d_unique_symbols.emplace_back(node);
345 }
346
347 for (const auto &node : d_processing)
348 d_visited.at(node) = false;
349 d_processing.swap(new_processing);
350 // std::cout << d_processing.size() << std::endl;
351}
352
360void Kernel::sort_propagate(std::unordered_map<std::string, node::Sort> &sort_key_map, std::vector<std::vector<parser::Parser::DTTypeDecl>> &datatype_blocks) {
361 std::unordered_map<std::string, node::Sort> new_sort_key_map;
362 std::unordered_map<node::Sort, size_t> tmp_dts;
363 size_t _cnt = 0;
364 for (auto &block : datatype_blocks) {
365 ++_cnt;
366 for (size_t i = 0, isz = block.size(); i < isz; ++i) {
367 auto &td = block.at(i);
368 if (sort_key_map.find(td.name) != sort_key_map.end()) {
369 tmp_dts.emplace(sort_key_map.at(td.name), _cnt);
370 sort_key_map.erase(td.name);
371 }
372 }
373 }
374
375 std::unordered_map<node::Sort, size_t> sort_idx;
376 std::vector<std::vector<size_t>> graph;
377 std::vector<std::vector<std::pair<size_t, size_t>>> reversed_graph;
378
379 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
380 if (d_nodes.at(i)->getSort()->isDec()) {
381 if (sort_idx.find(d_nodes.at(i)->getSort()) == sort_idx.end()) {
382 sort_idx[d_nodes.at(i)->getSort()] = sort_idx.size();
383 graph.emplace_back();
384 reversed_graph.emplace_back();
385 }
386 }
387 }
388
389 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
390 if (d_nodes.at(i)->getSort()->children.empty())
391 continue;
392 for (auto &child_sort : d_nodes.at(i)->getSort()->children) {
393 if ((child_sort->isDec() || child_sort->isDatatype()) && sort_idx.find(child_sort) == sort_idx.end()) {
394 new_sort_key_map.emplace("UNUSED_SORT", child_sort);
395 child_sort->setName("UNUSED_SORT");
396 }
397 }
398 }
399
400 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
401 graph.emplace_back(d_graph.at(i));
402 reversed_graph.emplace_back(d_reversed_graph.at(i));
403 for (auto &child : graph.back()) {
404 child += sort_idx.size();
405 }
406 for (auto &[father_index, oper_index] : reversed_graph.back()) {
407 father_index += sort_idx.size();
408 }
409 if (d_nodes.at(i)->getSort()->isDec()) {
410 size_t sort_index = sort_idx.at(d_nodes.at(i)->getSort());
411 // graph.at(sort_index).emplace_back(i + sort_idx.size());
412 // reversed_graph.at(i + sort_idx.size()).emplace_back(sort_index, 0);
413 graph.at(i + sort_idx.size()).emplace_back(sort_index);
414 reversed_graph.at(sort_index).emplace_back(i + sort_idx.size(), 0);
415 }
416 }
417
418 std::vector<size_t> hash_table(d_hash_table);
419 std::reverse(hash_table.begin(), hash_table.end());
420 hash_table.resize(hash_table.size() + sort_idx.size(), 0);
421 std::reverse(hash_table.begin(), hash_table.end());
422 for (const auto &[sort, index] : tmp_dts) {
423 if (sort_idx.find(sort) != sort_idx.end()) {
424 util::hash_combine(hash_table.at(sort_idx.at(sort)), index);
425 util::hash_combine(hash_table.at(sort_idx.at(sort)), index);
426 }
427 }
428 std::vector<uint8_t> is_commutative(d_is_commutative);
429 std::reverse(is_commutative.begin(), is_commutative.end());
430 is_commutative.resize(is_commutative.size() + sort_idx.size(), false);
431 std::reverse(is_commutative.begin(), is_commutative.end());
432
433 d_graph.swap(graph);
434 d_reversed_graph.swap(reversed_graph);
435 d_hash_table.swap(hash_table);
437
438 std::vector<size_t> processing(d_graph.size());
439 std::iota(processing.begin(), processing.end(), 0);
440
441 std::vector<size_t> symbols(sort_idx.size());
442 std::iota(symbols.begin(), symbols.end(), 0);
443 for (size_t i = 0, isz = d_symbols.size(); i < isz; ++i) {
444 auto idx = d_symbols.at(i);
445 if (!d_nodes.at(idx)->getSort()->isDec()) {
446 symbols.emplace_back(idx + sort_idx.size());
447 }
448 }
449 // std::cout << processing.size() << ' ' << symbols.size() << std::endl;
450 std::vector<size_t> unique_hashes(sort_idx.size());
451
452 // apply_nauty(d_graph, d_hash_table);
453 // exit(0);
454
455 do {
456 // std::cout << "!!!\n";
457 context_propagate(processing, symbols);
458 std::transform(symbols.begin(), symbols.begin() + sort_idx.size(), unique_hashes.begin(), [this](size_t index) { return d_hash_table.at(index); });
459 std::sort(unique_hashes.begin(), unique_hashes.end());
460 // for (size_t i = 0, isz = unique_hashes.size(); i < isz; ++i)
461 // std::cout << d_hash_table.at(i) << ' ';
462 // std::cout << std::endl;
463 // exit(0);
464 size_t hash_value = 0;
465 bool is_select = false;
466 for (size_t i = 1, isz = unique_hashes.size(); i < isz; ++i)
467 if (unique_hashes[i] == unique_hashes[i - 1]) {
468 hash_value = unique_hashes.at(i);
469 is_select = true;
470 break;
471 }
472 if (!is_select)
473 break;
474 size_t sort_index = 0;
475 for (size_t i = 0, isz = sort_idx.size(); i < isz; ++i) {
476 if (d_hash_table.at(symbols.at(i)) == hash_value) {
477 sort_index = symbols.at(i);
478 break;
479 }
480 }
481 // std::cout << d_hash_table.at(sort_index) << ' ' << hash_value << std::endl;
482 util::hash_combine(d_hash_table.at(sort_index), 0);
483 // std::cout << "!!!" << d_hash_table.at(sort_index) << std::endl;
484
485 } while (true);
486
487 std::vector<std::pair<size_t, node::Sort>> sort_hashes;
488 sort_hashes.reserve(sort_idx.size());
489 for (const auto &[sort, index] : sort_idx) {
490 sort_hashes.emplace_back(d_hash_table.at(index), sort);
491 }
492
493 std::sort(sort_hashes.begin(), sort_hashes.end());
494 std::unordered_map<std::string, std::string> dt_name_map;
495 size_t sort_count = 0, dt_count = 0;
496 for (size_t i = 0, isz = sort_hashes.size(); i < isz; ++i) {
497 if (sort_key_map.find(sort_hashes.at(i).second->name) == sort_key_map.end()) {
498 dt_name_map[sort_hashes.at(i).second->name] = "DT" + std::to_string(dt_count);
499 sort_hashes.at(i).second->setName("DT" + std::to_string(dt_count));
500 new_sort_key_map["DT" + std::to_string(dt_count)] = sort_hashes.at(i).second;
501 ++dt_count;
502 }
503 else {
504 sort_hashes.at(i).second->setName("SORT" + std::to_string(sort_count));
505 new_sort_key_map["SORT" + std::to_string(sort_count)] = sort_hashes.at(i).second;
506 ++sort_count;
507 }
508 }
509
510 std::unordered_map<std::string, size_t> uf_name_id;
511 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
512 if (d_nodes.at(i)->isUFName())
513 uf_name_id[d_nodes.at(i)->getName()] = i + sort_idx.size();
514 }
515
516 for (auto &block : datatype_blocks) {
517 for (size_t i = 0, isz = block.size(); i < isz; ++i) {
518 auto &td = block.at(i);
519 if (dt_name_map.find(td.name) != dt_name_map.end()) {
520 td.name = dt_name_map.at(td.name);
521 for (auto &cd : td.ctors) {
522 if (uf_name_id.find(cd.name) != uf_name_id.end()) {
523 util::hash_combine(d_hash_table.at(uf_name_id.at(cd.name)), std::hash<std::string>{}(td.name));
524 // for (auto &sd : cd.selectors) {
525 // if (uf_name_id.find(sd.name) != uf_name_id.end()) {
526 // util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), d_hash_table.at(uf_name_id.at(cd.name)));
527 // }
528 // }
529 for (size_t i = 0, isz = cd.selectors.size(); i < isz; ++i) {
530 auto &sd = cd.selectors.at(i);
531 if (uf_name_id.find(sd.name) != uf_name_id.end()) {
532 util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), d_hash_table.at(uf_name_id.at(cd.name)));
533 util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), i);
534 }
535 }
536 }
537 else {
538 // for (auto &sd : cd.selectors) {
539 // if (uf_name_id.find(sd.name) != uf_name_id.end()) {
540 // util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), std::hash<std::string>{}(td.name));
541 // }
542 // }
543 for (size_t i = 0, isz = cd.selectors.size(); i < isz; ++i) {
544 auto &sd = cd.selectors.at(i);
545 if (uf_name_id.find(sd.name) != uf_name_id.end()) {
546 util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), std::hash<std::string>{}(td.name));
547 util::hash_combine(d_hash_table.at(uf_name_id.at(sd.name)), i);
548 }
549 }
550 }
551 }
552 }
553 }
554 }
555
556 sort_key_map.swap(new_sort_key_map);
557 d_graph.swap(graph);
558 d_reversed_graph.swap(reversed_graph);
559 // d_hash_table.swap(hash_table);
560 std::reverse(d_hash_table.begin(), d_hash_table.end());
561 d_hash_table.resize(d_hash_table.size() - sort_idx.size());
562 std::reverse(d_hash_table.begin(), d_hash_table.end());
564}
565
575void Kernel::apply(node::NodeManager &nm) {
576 std::vector<std::string> &function_names = nm.getFunctionNames();
577 std::unordered_map<std::string, std::shared_ptr<stabilizer::parser::DAGNode>> &function_key_map = nm.getFunKeyMap();
578
579 std::unordered_map<std::string, size_t> def_fun_id;
580 size_t def_fun_count = 0;
581 for (const auto &func_name : function_names) {
582 if (function_key_map.at(func_name)->isFuncDef())
583 def_fun_id[func_name] = def_fun_count++;
584 }
585
586 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
587 if (d_nodes.at(i)->isFuncDef()) {
588 util::hash_combine(d_hash_table.at(i), def_fun_id.at(d_nodes.at(i)->getName()));
589 }
590 }
593
594 if (std::any_of(d_nodes.begin(), d_nodes.end(), [this](const node::Node &node) { return node->getSort()->isDec() || node->getSort()->isDatatype(); })) {
595 sort_propagate(nm.getSortNames(), nm.getDatatypeBlocks());
596 }
597 else {
598 // exit(0);
599 nm.getSortNames().clear();
600 nm.getDatatypeBlocks().clear();
601 }
603 while (true) {
604 std::unordered_map<size_t, size_t> hash_count;
605 bool flag = true;
606 for (const auto &node : d_processing) {
607 hash_count[d_hash_table.at(node)]++;
608 if (hash_count[d_hash_table.at(node)] > 1) {
609 flag = false;
610 break;
611 }
612 }
613 if (flag)
614 break;
615 else
617 }
618 }
619 else {
620 while (d_unique_symbols.size() != d_symbol_num) {
623 }
624 }
625
626 // d_hash_table = d_context_hash;
627 std::unordered_map<std::string, size_t> &var_names = nm.getVarNames();
628 std::unordered_map<std::string, size_t> new_var_names;
629 std::unordered_map<std::string, size_t> &temp_var_names = nm.getTempVarNames();
630 std::unordered_map<std::string, size_t> new_temp_var_names;
631 std::unordered_map<std::string, std::string> var_names_map;
632
633 std::unordered_map<std::string, std::string> function_names_map;
634 size_t symbol_count = 0, uf_count = 0;
635 for (size_t i = 0, isz = d_unique_symbols.size(); i < isz; ++i) {
636 if (d_nodes.at(d_unique_symbols.at(i))->isUFName()) {
637 // std::cout << d_context_hash.at(d_unique_symbols.at(i)) << ' ' << d_hash_table.at(d_unique_symbols.at(i)) << std::endl;
638 d_hash_table.at(d_unique_symbols.at(i)) = ++uf_count;
640 std::string uf_name = d_nodes.at(d_unique_symbols.at(i))->getName();
641 d_nodes.at(d_unique_symbols.at(i))->setName("UF" + std::to_string(uf_count));
642 function_names_map[uf_name] = "UF" + std::to_string(uf_count);
643 }
644 else {
645 d_hash_table.at(d_unique_symbols.at(i)) = ++symbol_count;
646 std::string var_name = d_nodes.at(d_unique_symbols.at(i))->getName();
647 // std::cout << var_name << std::endl;
648 // if (symbol_count == 749)
649 // std::cout << var_name << std::endl;
650 // d_nodes.at(d_unique_symbols.at(i))->setName("|S<" + var_name + ">" + std::to_string(symbol_count) + "|");
651 // var_names_map[var_name] = "|S<" + var_name + ">" + std::to_string(symbol_count) + "|";
652 d_nodes.at(d_unique_symbols.at(i))->setName("S" + std::to_string(symbol_count));
653 var_names_map[var_name] = "S" + std::to_string(symbol_count);
654 }
655 }
656 // for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
657 // if (d_nodes.at(i)->isLe()) {
658 // std::cout << d_nodes.at(i)->toString() << ' '
659 // << parser::kindToString(d_nodes.at(i)->getKind()) << ' '
660 // << static_cast<size_t>(d_nodes.at(i)->getKind()) << ' '
661 // << d_nodes.at(i)->getChildrenSize() << ' '
662 // << d_nodes.at(i)->getSort()->toString() << ' '
663 // << d_hash_table.at(i) << ' '
664 // << d_hash_table.at(d_graph.at(i).front()) << ' '
665 // << d_hash_table.at(d_graph.at(i).back())
666 // << std::endl;
667 // }
668 // }
669 std::vector<std::pair<size_t, size_t>> dt_testers;
670 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
671 if (d_nodes.at(i)->getKind() == parser::NODE_KIND::NT_DT_TESTER) {
672 std::string vuf_name = d_nodes.at(i)->getName();
673 if (function_names_map.find(vuf_name) != function_names_map.end()) {
674 // std::cout << "!!!" << std::endl;
675 d_nodes.at(i)->setName(function_names_map.at(vuf_name));
676 }
677 else {
678 // std::cout << d_hash_table.at(i) << std::endl;
679 dt_testers.emplace_back(d_hash_table.at(i), i);
680 }
681 }
682 }
683
684 std::sort(dt_testers.begin(), dt_testers.end(), [](const auto &a, const auto &b) {
685 return a.first < b.first;
686 });
687
688 for (const auto &[hash_value, idx] : dt_testers) {
689 uf_count++;
690 d_hash_table.at(idx) = ++uf_count;
692 function_names_map[d_nodes.at(idx)->getName()] = "UF" + std::to_string(uf_count);
693 d_nodes.at(idx)->setName("UF" + std::to_string(uf_count));
694 }
695
696 size_t con_idx = 0;
697 auto new_blocks = nm.getDatatypeBlocks();
698 new_blocks.clear();
699 for (auto &block : nm.getDatatypeBlocks()) {
700 std::sort(block.begin(), block.end(), [](const auto &a, const auto &b) {
701 return a.name < b.name;
702 });
703 auto new_block = block;
704 new_block.clear();
705 for (auto &td : block) {
706 if (!td.name.starts_with("DT") && !td.name.starts_with("SORT")) {
707 continue;
708 }
709 auto new_ctors = td.ctors;
710 new_ctors.clear();
711 for (auto &cd : td.ctors) {
712 if (function_names_map.find(cd.name) != function_names_map.end()) {
713 cd.name = function_names_map.at(cd.name);
714 }
715 else {
716 cd.name = "";
717 }
718 auto new_selectors = cd.selectors;
719 new_selectors.clear();
720 for (auto &sd : cd.selectors) {
721 if (function_names_map.find(sd.name) != function_names_map.end()) {
722 sd.name = function_names_map.at(sd.name);
723 new_selectors.emplace_back(sd);
724 }
725 // else {
726 // sd.name = "";
727 // }
728 else if (sd.sort->name.starts_with("SORT") || sd.sort->name.starts_with("DT")) {
729 sd.name = "";
730 new_selectors.emplace_back(sd);
731 }
732 else if (sd.sort->isDec() || sd.sort->isDatatype()) {
733 sd.sort->setName("UNUSED_SORT");
734 nm.getSortNames().emplace("UNUSED_SORT", sd.sort);
735 sd.name = "";
736 new_selectors.emplace_back(sd);
737 }
738 else {
739 sd.name = "";
740 new_selectors.emplace_back(sd);
741 }
742 }
743
744 if (cd.name.empty()) {
745 bool selected = false;
746 for (const auto &sd : new_selectors) {
747 if (!sd.name.empty()) {
748 selected = true;
749 cd.selectors.swap(new_selectors);
750 break;
751 }
752 }
753 if (!selected)
754 continue;
755 }
756 else
757 cd.selectors.swap(new_selectors);
758 // if (selected == false) {
759 // cd.selectors.clear();
760 // }
761 // cd.selectors.swap(new_selectors);
762 // if (cd.name == "UF296") {
763 // for (const auto& sd : cd.selectors) {
764 // std::cout << sd.name << ' ' << sd.sort->toString() << std::endl;
765 // }
766 // }
767 // {
768 // std::unordered_map<node::Sort, std::vector<decltype(cd.selectors)::value_type>> buckets;
769 // buckets.reserve(cd.selectors.size());
770 // for (const auto &sd : cd.selectors) {
771 // buckets[sd.sort].push_back(sd);
772 // }
773
774 // for (auto &[s, vec] : buckets) {
775 // std::sort(vec.begin(), vec.end(), [](const auto &a, const auto &b) {
776 // return a.name < b.name;
777 // });
778 // }
779
780 // std::unordered_map<node::Sort, size_t> idx;
781 // idx.reserve(buckets.size());
782 // std::vector<decltype(cd.selectors)::value_type> reordered;
783 // reordered.reserve(cd.selectors.size());
784 // for (const auto &sd : cd.selectors) {
785 // auto &i = idx[sd.sort];
786 // reordered.emplace_back(buckets[sd.sort][i]);
787 // ++i;
788 // }
789 // cd.selectors.swap(reordered);
790 // }
791 size_t idx = 0;
792 for (size_t i = 0, isz = cd.selectors.size(); i < isz; ++i) {
793 if (cd.selectors.at(i).name.empty())
794 cd.selectors.at(i).name = "VAR" + std::to_string(idx++);
795 }
796 // std::sort(cd.selectors.begin(), cd.selectors.end(), [](const auto& a, const auto& b) {
797 // return a.name < b.name;
798 // });
799 new_ctors.emplace_back(cd);
800 }
801 bool need_two = td.ctors.size() >= 2;
802 bool has_zero = false;
803 bool only_zero = true;
804 for (const auto &cd : td.ctors) {
805 if (!cd.selectors.empty()) {
806 only_zero = false;
807 }
808 else {
809 has_zero = true;
810 }
811 if (has_zero && !only_zero) {
812 break;
813 }
814 }
815 td.ctors.swap(new_ctors);
816 std::sort(td.ctors.begin(), td.ctors.end(), [](const auto &a, const auto &b) {
817 if (a.name != b.name)
818 return a.name < b.name;
819 else if (a.selectors.size() == b.selectors.size()) {
820 for (size_t i = 0, isz = a.selectors.size(); i < isz; ++i) {
821 if (a.selectors.at(i).name != b.selectors.at(i).name)
822 return a.selectors.at(i).name < b.selectors.at(i).name;
823 }
824 return false;
825 }
826 else
827 return a.selectors.size() < b.selectors.size();
828 });
829
830 for (auto &cd : td.ctors) {
831 if (cd.name.empty())
832 cd.name = "CON" + std::to_string(con_idx++);
833 }
834
835 while (td.ctors.empty() || (td.ctors.size() < 2 && need_two)) {
836 td.ctors.emplace_back();
837 if (only_zero)
838 td.ctors.back().name = "CON" + std::to_string(con_idx++);
839 else if (td.ctors.empty() || !has_zero || td.ctors.front().selectors.empty()) {
840 td.ctors.back().name = "CON" + std::to_string(con_idx++);
841 td.ctors.back().selectors.emplace_back(td.ctors.back().name + "_TVAR0", std::make_shared<parser::Sort>(parser::SORT_KIND::SK_DEC, "UNUSED_SORT"));
842 td.ctors.back().selectors.back().sort->setName("UNUSED_SORT");
843 nm.getSortNames().emplace("UNUSED_SORT", td.ctors.back().selectors.back().sort);
844 }
845 else {
846 td.ctors.back().name = "CON" + std::to_string(con_idx++);
847 }
848 }
849 size_t idx = 0;
850 for (auto &cd : td.ctors) {
851 for (auto &sd : cd.selectors) {
852 if (sd.name.starts_with("VAR"))
853 sd.name = "DT_SEL" + std::to_string(idx++);
854 }
855 }
856 // std::sort(td.ctors.begin(), td.ctors.end(), [](const auto& a, const auto& b) {
857 // return a.name < b.name;
858 // });
859 new_block.emplace_back(td);
860 }
861 block.swap(new_block);
862 if (!block.empty())
863 new_blocks.emplace_back(block);
864
865 // std::sort(new_block.begin(), new_block.end(), [](const parser::Parser::DTTypeDecl& a, const parser::Parser::DTTypeDecl& b) {
866 // return a.name < b.name;
867 // });
868 }
869 nm.getDatatypeBlocks().swap(new_blocks);
870
871 for (const auto &[name, index] : var_names) {
872 auto itr = var_names_map.find(name);
873 if (itr != var_names_map.end()) {
874 new_var_names[itr->second] = index;
875 }
876 }
877 for (const auto &[name, index] : temp_var_names) {
878 auto itr = var_names_map.find(name);
879 if (itr != var_names_map.end()) {
880 new_temp_var_names[itr->second] = index;
881 }
882 }
883
884 var_names.swap(new_var_names);
885 temp_var_names.swap(new_temp_var_names);
886
887 std::unordered_map<node::Node, size_t> node2index;
888
889 std::vector<node::Node> func_dec, func_rec, func_def;
890
891 std::unordered_map<std::string, std::vector<size_t>> uf_buckets;
892 std::unordered_map<std::string, std::vector<size_t>> func_buckets;
893 for (size_t i = 0, isz = d_nodes.size(); i < isz; ++i) {
894 if (d_nodes.at(i)->isUFName()) {
895 continue;
896 }
897
898 // std::cout << d_nodes.at(i)->toString() << ' ' << d_hash_table.at(i) << std::endl;
899 if (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_UF_APPLY) {
900 // std::cout << d_nodes.at(i)->toString() << std::endl;
901 d_nodes.at(i)->setName(function_names_map.at(d_nodes.at(i)->getName()));
902 util::hash_combine(d_hash_table.at(i), std::hash<std::string>{}(d_nodes.at(i)->getName()));
903 // uf_buckets[d_nodes.at(i)->getName()].emplace_back(i);
904 // d_nodes.at(i)->setName("UF_" + d_nodes.at(i)->getChild(d_nodes.at(i)->getChildrenSize() - 1)->getName());
905 }
906
907 // std::cout << d_nodes.at(i)->toString() << ' ' << d_hash_table.at(i) << std::endl;
908
909 node2index.emplace(d_nodes.at(i), i);
910 auto children = d_nodes.at(i)->getChildren();
911
912 if (is_commutative(i)) {
913 bool del = (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FP_ADD ||
914 d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FP_MUL ||
915 d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_EXISTS ||
916 d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FORALL);
917 std::sort(children.begin() + del, children.end(), [this, &node2index](const node::Node &a, const node::Node &b) {
918 if (d_context_hash.at(node2index.at(a)) == d_context_hash.at(node2index.at(b)))
919 return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
920 else
921 return d_context_hash.at(node2index.at(a)) < d_context_hash.at(node2index.at(b));
922 // return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
923 });
924
925 d_nodes.at(i)->replace_children(children);
926 }
927
928 // std::vector<size_t> children_index(d_nodes.at(i)->getChildrenSize());
929 // std::transform(children.begin(), children.end(), children_index.begin(), [&node2index](const node::Node &child) {
930 // return node2index.at(child);
931 // });
932
933 if (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FUNC_DEC)
934 func_dec.emplace_back(d_nodes.at(i));
935 else if (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FUNC_REC)
936 func_rec.emplace_back(d_nodes.at(i));
937 else if (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FUNC_DEF)
938 func_def.emplace_back(d_nodes.at(i));
939 else if (d_nodes.at(i)->getKind() == stabilizer::parser::NODE_KIND::NT_FUNC_APPLY) {
940 // std::cout << d_nodes.at(i)->toString() << std::endl;
941 func_buckets[d_nodes.at(i)->getName()].emplace_back(i);
942 }
943 }
944
945 std::sort(func_dec.begin(), func_dec.end(), [this, &node2index](const node::Node &a, const node::Node &b) {
946 return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
947 });
948 std::sort(func_rec.begin(), func_rec.end(), [this, &node2index](const node::Node &a, const node::Node &b) {
949 return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
950 });
951 std::sort(func_def.begin(), func_def.end(), [this, &node2index](const node::Node &a, const node::Node &b) {
952 return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
953 });
954
955 for (size_t i = 0, isz = func_dec.size(); i < isz; i++) {
956 function_names_map[func_dec.at(i)->getName()] = "FDEC" + std::to_string(i);
957 func_dec.at(i)->setName("FDEC" + std::to_string(i));
958 }
959 for (size_t i = 0, isz = func_rec.size(); i < isz; i++) {
960 function_names_map[func_rec.at(i)->getName()] = "FREC" + std::to_string(i);
961 func_rec.at(i)->setName("FREC" + std::to_string(i));
962 }
963 for (size_t i = 0, isz = func_def.size(); i < isz; i++) {
964 for (const auto &index : func_buckets[func_def.at(i)->getName()]) {
965 d_nodes.at(index)->setName("FDEF" + std::to_string(i));
966 }
967
968 function_names_map[func_def.at(i)->getName()] = "FDEF" + std::to_string(i);
969 func_def.at(i)->setName("FDEF" + std::to_string(i));
970 util::hash_combine(d_hash_table.at(node2index.at(func_def.at(i))), std::hash<std::string>{}("FDEF" + std::to_string(i)));
971 }
972
973 std::unordered_map<std::string, std::shared_ptr<stabilizer::parser::DAGNode>> new_function_key_map;
974
975 std::vector<std::string> ndec, nrec, ndef;
976 for (const auto &name : function_names) {
977 auto itr = function_names_map.find(name);
978
979 if (itr != function_names_map.end()) {
980 if (function_key_map.at(name)->isFuncDec())
981 ndec.emplace_back(itr->second);
982 else if (function_key_map.at(name)->isFuncRec())
983 nrec.emplace_back(itr->second);
984 else if (function_key_map.at(name)->isFuncDef())
985 ndef.emplace_back(itr->second);
986
987 new_function_key_map[itr->second] = function_key_map.at(name);
988 new_function_key_map[itr->second]->setName(itr->second);
989 auto fun = new_function_key_map[itr->second];
990 if (fun->isFuncDef()) {
991 // if (fun->getChildrenSize() == 3)
992 // std::cout << fun->getChildrenSize() << ' ' << fun->getChild(1) << ' ' << fun->getChild(2) << std::endl;
993 for (size_t i = 1, isz = fun->getChildrenSize(); i < isz; ++i) {
994 fun->getChild(i)->setName("VAR" + std::to_string(i - 1));
995 }
996 }
997 }
998 }
999 std::vector<std::string> new_function_names;
1000 std::sort(ndec.begin(), ndec.end());
1001 for (const auto &name : ndec)
1002 new_function_names.emplace_back(name);
1003 std::sort(nrec.begin(), nrec.end());
1004 for (const auto &name : nrec)
1005 new_function_names.emplace_back(name);
1006 for (const auto &name : ndef)
1007 new_function_names.emplace_back(name);
1008
1009 function_names.swap(new_function_names);
1010 function_key_map.swap(new_function_key_map);
1011
1012 auto assertions = nm.assertions();
1013
1014 std::sort(assertions.begin(), assertions.end(), [this, &node2index](const node::Node &a, const node::Node &b) {
1015 if (d_context_hash.at(node2index.at(a)) == d_context_hash.at(node2index.at(b)))
1016 return d_hash_table.at(node2index.at(a)) < d_hash_table.at(node2index.at(b));
1017 else
1018 return d_context_hash.at(node2index.at(a)) < d_context_hash.at(node2index.at(b));
1019 });
1020
1021 // assertions = {nm.mkAnd(assertions)};
1022 nm.replace_assertions(assertions);
1023 return;
1024} // namespace stabilizer::kernel
1025
1026} // namespace stabilizer::kernel
std::vector< std::vector< size_t > > d_graph
Definition kernel.h:65
void rebuild_graph()
Keep only currently ambiguous graph regions for next iteration.
Definition kernel.cpp:306
std::vector< size_t > d_unique_symbols
Definition kernel.h:73
void apply(node::NodeManager &nm)
Run the full stabilization pass and mutate node manager state.
Definition kernel.cpp:575
std::vector< size_t > d_context_hash
Definition kernel.h:68
bool is_commutative(const size_t &i, const bool &from_cache=true)
Check whether node kind at index i is treated as commutative.
Definition kernel.cpp:110
std::vector< uint8_t > d_visited
Definition kernel.h:76
std::vector< std::vector< std::pair< size_t, size_t > > > d_reversed_graph
Definition kernel.h:66
void context_propagate(const std::vector< size_t > &processing, const std::vector< size_t > &symbols)
Run propagation to a fixed point over a selected subgraph.
Definition kernel.cpp:205
std::vector< size_t > d_hash_table
Definition kernel.h:67
void propagate(const std::vector< size_t > &processing)
One directional propagation pass on the selected nodes.
Definition kernel.cpp:157
std::vector< size_t > d_symbols
Definition kernel.h:72
std::vector< uint8_t > d_is_symbol
Definition kernel.h:78
void sort_propagate(std::unordered_map< std::string, node::Sort > &sort_key_map, std::vector< std::vector< parser::Parser::DTTypeDecl > > &datatype_blocks)
Incorporate sort/datatype structure into propagation and naming.
Definition kernel.cpp:360
bool d_symmetry_breaking_perturbation
Definition kernel.h:85
std::vector< node::Node > d_nodes
Definition kernel.h:69
std::vector< size_t > d_processing
Definition kernel.h:70
std::vector< uint8_t > d_is_commutative
Definition kernel.h:77
void specific_propagate()
Apply local perturbation to break remaining hash collisions.
Definition kernel.cpp:229
size_t hash_node(const node::Node &node)
Compute a stable structural hash seed for a parser/node DAG node.
void hash_combine(size_t &seed, const size_t &value)
Mix one value into a hash seed.
void hash_children(size_t &seed, const std::vector< size_t > &children, const std::vector< size_t > &hash_table)
Mix child-node hashes into a parent seed in argument order.
void hash_parents(size_t &seed, const std::vector< std::pair< size_t, size_t > > &parents, const std::vector< size_t > &hash_table)
Propagate a node hash contribution to parent nodes.
void hash_communative_children(size_t &seed, const std::vector< size_t > &children, const std::vector< size_t > &hash_table)
Mix child-node hashes as a commutative multiset.