cpp-pd-code-simplify-interface 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +34 -0
- cpp_pd_code_simplify_interface/data/src/native_interface.cpp +32 -54
- cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +840 -78
- cpp_pd_code_simplify_interface/main.py +47 -5
- {cpp_pd_code_simplify_interface-0.1.2.dist-info → cpp_pd_code_simplify_interface-0.1.4.dist-info}/METADATA +7 -5
- {cpp_pd_code_simplify_interface-0.1.2.dist-info → cpp_pd_code_simplify_interface-0.1.4.dist-info}/RECORD +8 -8
- {cpp_pd_code_simplify_interface-0.1.2.dist-info → cpp_pd_code_simplify_interface-0.1.4.dist-info}/WHEEL +0 -0
- {cpp_pd_code_simplify_interface-0.1.2.dist-info → cpp_pd_code_simplify_interface-0.1.4.dist-info}/entry_points.txt +0 -0
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
#include <deque>
|
|
7
7
|
#include <limits>
|
|
8
8
|
#include <map>
|
|
9
|
+
#include <atomic>
|
|
10
|
+
#include <exception>
|
|
11
|
+
#include <mutex>
|
|
9
12
|
#include <numeric>
|
|
10
13
|
#include <queue>
|
|
11
14
|
#include <random>
|
|
12
15
|
#include <set>
|
|
13
16
|
#include <sstream>
|
|
14
17
|
#include <stdexcept>
|
|
18
|
+
#include <thread>
|
|
15
19
|
#include <unordered_map>
|
|
16
20
|
#include <unordered_set>
|
|
17
21
|
|
|
@@ -998,11 +1002,10 @@ std::set<int> normalized_removed_crossings(const PDCode& code, const std::vector
|
|
|
998
1002
|
return removed;
|
|
999
1003
|
}
|
|
1000
1004
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1005
|
+
std::vector<int> heuristic_distances_to_target(
|
|
1006
|
+
const DualGraph& graph,
|
|
1007
|
+
int target,
|
|
1008
|
+
int cutoff);
|
|
1006
1009
|
|
|
1007
1010
|
void collect_simple_paths_dfs(
|
|
1008
1011
|
const DualGraph& graph,
|
|
@@ -1010,12 +1013,19 @@ void collect_simple_paths_dfs(
|
|
|
1010
1013
|
int target,
|
|
1011
1014
|
int cutoff,
|
|
1012
1015
|
int max_paths,
|
|
1016
|
+
int current_weight,
|
|
1017
|
+
const std::vector<int>& distance,
|
|
1013
1018
|
std::vector<char>& visited,
|
|
1014
1019
|
std::vector<int>& current_path,
|
|
1015
1020
|
std::vector<std::vector<int>>& paths) {
|
|
1021
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1016
1022
|
if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
|
|
1017
1023
|
return;
|
|
1018
1024
|
}
|
|
1025
|
+
if (current < 0 || current >= static_cast<int>(distance.size()) ||
|
|
1026
|
+
distance[current] == infinity || current_weight + distance[current] >= cutoff) {
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1019
1029
|
|
|
1020
1030
|
for (int edge_index : graph.adjacency[current]) {
|
|
1021
1031
|
const GraphEdge& edge = graph.edges[edge_index];
|
|
@@ -1023,32 +1033,37 @@ void collect_simple_paths_dfs(
|
|
|
1023
1033
|
if (visited[next]) {
|
|
1024
1034
|
continue;
|
|
1025
1035
|
}
|
|
1036
|
+
const int next_weight = current_weight + edge.weight;
|
|
1037
|
+
if (next_weight >= cutoff) {
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
1040
|
+
if (next < 0 || next >= static_cast<int>(distance.size()) ||
|
|
1041
|
+
distance[next] == infinity || next_weight + distance[next] >= cutoff) {
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1026
1044
|
|
|
1027
1045
|
current_path.push_back(next);
|
|
1028
1046
|
visited[next] = true;
|
|
1029
1047
|
|
|
1030
1048
|
if (next == target) {
|
|
1031
|
-
|
|
1032
|
-
for (std::size_t i = 0; i + 1 < current_path.size(); ++i) {
|
|
1033
|
-
const GraphEdge* path_edge = graph.edge(current_path[i], current_path[i + 1]);
|
|
1034
|
-
if (path_edge == nullptr) {
|
|
1035
|
-
throw std::logic_error("Missing dual edge while weighing a path");
|
|
1036
|
-
}
|
|
1037
|
-
path_weight += path_edge->weight;
|
|
1038
|
-
if (path_weight >= cutoff) {
|
|
1039
|
-
break;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
if (path_weight < cutoff) {
|
|
1043
|
-
paths.push_back(current_path);
|
|
1044
|
-
}
|
|
1049
|
+
paths.push_back(current_path);
|
|
1045
1050
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1046
1051
|
visited[next] = false;
|
|
1047
1052
|
current_path.pop_back();
|
|
1048
1053
|
return;
|
|
1049
1054
|
}
|
|
1050
1055
|
} else {
|
|
1051
|
-
collect_simple_paths_dfs(
|
|
1056
|
+
collect_simple_paths_dfs(
|
|
1057
|
+
graph,
|
|
1058
|
+
next,
|
|
1059
|
+
target,
|
|
1060
|
+
cutoff,
|
|
1061
|
+
max_paths,
|
|
1062
|
+
next_weight,
|
|
1063
|
+
distance,
|
|
1064
|
+
visited,
|
|
1065
|
+
current_path,
|
|
1066
|
+
paths);
|
|
1052
1067
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1053
1068
|
visited[next] = false;
|
|
1054
1069
|
current_path.pop_back();
|
|
@@ -1077,8 +1092,19 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1077
1092
|
|
|
1078
1093
|
std::vector<char> visited(graph.faces.size(), false);
|
|
1079
1094
|
std::vector<int> current_path{source};
|
|
1095
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1080
1096
|
visited[source] = true;
|
|
1081
|
-
collect_simple_paths_dfs(
|
|
1097
|
+
collect_simple_paths_dfs(
|
|
1098
|
+
graph,
|
|
1099
|
+
source,
|
|
1100
|
+
target,
|
|
1101
|
+
cutoff,
|
|
1102
|
+
max_paths,
|
|
1103
|
+
0,
|
|
1104
|
+
distance,
|
|
1105
|
+
visited,
|
|
1106
|
+
current_path,
|
|
1107
|
+
paths);
|
|
1082
1108
|
return paths;
|
|
1083
1109
|
}
|
|
1084
1110
|
|
|
@@ -1446,6 +1472,375 @@ bool do_check(
|
|
|
1446
1472
|
return true;
|
|
1447
1473
|
}
|
|
1448
1474
|
|
|
1475
|
+
class DisjointSet {
|
|
1476
|
+
public:
|
|
1477
|
+
int find(int value) {
|
|
1478
|
+
auto inserted = parent_.insert(std::make_pair(value, value));
|
|
1479
|
+
int parent = inserted.first->second;
|
|
1480
|
+
if (parent != value) {
|
|
1481
|
+
parent = find(parent);
|
|
1482
|
+
parent_[value] = parent;
|
|
1483
|
+
}
|
|
1484
|
+
return parent;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
void unite(int first, int second) {
|
|
1488
|
+
int first_root = find(first);
|
|
1489
|
+
int second_root = find(second);
|
|
1490
|
+
if (first_root == second_root) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (second_root < first_root) {
|
|
1494
|
+
std::swap(first_root, second_root);
|
|
1495
|
+
}
|
|
1496
|
+
parent_[second_root] = first_root;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
private:
|
|
1500
|
+
std::map<int, int> parent_;
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
std::map<std::pair<int, int>, std::string> green_crossing_levels(
|
|
1504
|
+
const SimplificationResult& result) {
|
|
1505
|
+
std::set<std::pair<int, int>> path_edges;
|
|
1506
|
+
for (std::size_t i = 0; i + 1 < result.green_path.size(); ++i) {
|
|
1507
|
+
path_edges.insert(std::make_pair(result.green_path[i], result.green_path[i + 1]));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
std::map<std::pair<int, int>, std::string> levels;
|
|
1511
|
+
for (const GreenCrossing& crossing : result.green_crossings) {
|
|
1512
|
+
const std::pair<int, int> edge_key =
|
|
1513
|
+
std::make_pair(crossing.from_face, crossing.to_face);
|
|
1514
|
+
if (path_edges.count(edge_key) == 0) {
|
|
1515
|
+
throw std::invalid_argument("Simplification witness has a green crossing outside the green path");
|
|
1516
|
+
}
|
|
1517
|
+
const auto inserted = levels.insert(std::make_pair(edge_key, crossing.strand_level));
|
|
1518
|
+
if (!inserted.second && inserted.first->second != crossing.strand_level) {
|
|
1519
|
+
throw std::invalid_argument("Simplification witness has conflicting green crossing levels");
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
for (const auto& edge_key : path_edges) {
|
|
1523
|
+
if (levels.count(edge_key) == 0) {
|
|
1524
|
+
throw std::invalid_argument("Simplification witness is missing a green crossing level");
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return levels;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
void clear_witness(SimplificationResult& result) {
|
|
1531
|
+
result.found = false;
|
|
1532
|
+
result.red_path.clear();
|
|
1533
|
+
result.green_path.clear();
|
|
1534
|
+
result.green_crossings.clear();
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
int crossing_graph_component_count(const PDCode& code) {
|
|
1538
|
+
const int crossing_count = static_cast<int>(code.size());
|
|
1539
|
+
if (crossing_count == 0) {
|
|
1540
|
+
return 0;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
std::vector<int> parent(crossing_count);
|
|
1544
|
+
std::iota(parent.begin(), parent.end(), 0);
|
|
1545
|
+
std::function<int(int)> find = [&](int value) -> int {
|
|
1546
|
+
if (parent[value] != value) {
|
|
1547
|
+
parent[value] = find(parent[value]);
|
|
1548
|
+
}
|
|
1549
|
+
return parent[value];
|
|
1550
|
+
};
|
|
1551
|
+
auto unite = [&](int first, int second) {
|
|
1552
|
+
int first_root = find(first);
|
|
1553
|
+
int second_root = find(second);
|
|
1554
|
+
if (first_root == second_root) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (second_root < first_root) {
|
|
1558
|
+
std::swap(first_root, second_root);
|
|
1559
|
+
}
|
|
1560
|
+
parent[second_root] = first_root;
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1563
|
+
std::map<int, std::vector<int>> label_crossings;
|
|
1564
|
+
for (int crossing_index = 0; crossing_index < crossing_count; ++crossing_index) {
|
|
1565
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1566
|
+
label_crossings[code[crossing_index][strand]].push_back(crossing_index);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
for (const auto& item : label_crossings) {
|
|
1570
|
+
if (item.second.size() != 2) {
|
|
1571
|
+
std::ostringstream message;
|
|
1572
|
+
message << "PD label " << item.first << " appears " << item.second.size()
|
|
1573
|
+
<< " times; each label must appear exactly twice";
|
|
1574
|
+
throw std::invalid_argument(message.str());
|
|
1575
|
+
}
|
|
1576
|
+
unite(item.second[0], item.second[1]);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
std::set<int> roots;
|
|
1580
|
+
for (int crossing_index = 0; crossing_index < crossing_count; ++crossing_index) {
|
|
1581
|
+
roots.insert(find(crossing_index));
|
|
1582
|
+
}
|
|
1583
|
+
return static_cast<int>(roots.size());
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
bool is_planar_pd_code(const PDCode& code) {
|
|
1587
|
+
if (code.empty()) {
|
|
1588
|
+
return true;
|
|
1589
|
+
}
|
|
1590
|
+
Diagram diagram(code);
|
|
1591
|
+
DualGraph graph(diagram);
|
|
1592
|
+
const int vertices = static_cast<int>(code.size());
|
|
1593
|
+
const int edges = vertices * 2;
|
|
1594
|
+
const int faces = static_cast<int>(graph.faces.size());
|
|
1595
|
+
const int graph_components = crossing_graph_component_count(code);
|
|
1596
|
+
return vertices - edges + faces == 2 * graph_components;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
} // namespace
|
|
1600
|
+
|
|
1601
|
+
MidSimplificationApplyResult apply_simplification_witness(
|
|
1602
|
+
const PDCode& code,
|
|
1603
|
+
const SimplificationResult& result,
|
|
1604
|
+
std::size_t known_crossingless_components) {
|
|
1605
|
+
if (!result.found) {
|
|
1606
|
+
throw std::invalid_argument("Cannot apply a missing simplification witness");
|
|
1607
|
+
}
|
|
1608
|
+
if (result.red_path.size() < 2) {
|
|
1609
|
+
throw std::invalid_argument("Simplification witness red path is too short");
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
Diagram diagram(code);
|
|
1613
|
+
DualGraph graph(diagram);
|
|
1614
|
+
std::set<int> removed_crossings;
|
|
1615
|
+
std::map<int, int> red_entry_by_crossing;
|
|
1616
|
+
for (std::size_t i = 0; i + 1 < result.red_path.size(); ++i) {
|
|
1617
|
+
removed_crossings.insert(result.red_path[i].crossing);
|
|
1618
|
+
red_entry_by_crossing[result.red_path[i].crossing] = result.red_path[i].strand;
|
|
1619
|
+
}
|
|
1620
|
+
if (removed_crossings.size() != result.red_path.size() - 1) {
|
|
1621
|
+
throw std::invalid_argument("Simplification witness repeats a removed red crossing");
|
|
1622
|
+
}
|
|
1623
|
+
if (removed_crossings.count(result.red_path.back().crossing) != 0) {
|
|
1624
|
+
throw std::invalid_argument("Simplification witness ends inside the removed red arc");
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
const std::map<std::pair<int, int>, std::string> levels = green_crossing_levels(result);
|
|
1628
|
+
DisjointSet dsu;
|
|
1629
|
+
const int endpoint_count = static_cast<int>(code.size() * 4);
|
|
1630
|
+
const int new_crossing_count =
|
|
1631
|
+
result.green_path.empty() ? 0 : static_cast<int>(result.green_path.size()) - 1;
|
|
1632
|
+
const int new_base = endpoint_count;
|
|
1633
|
+
|
|
1634
|
+
auto new_node = [&](int crossing_index, int strand) {
|
|
1635
|
+
return new_base + crossing_index * 4 + strand;
|
|
1636
|
+
};
|
|
1637
|
+
auto is_removed_node = [&](int node) {
|
|
1638
|
+
return node < endpoint_count && removed_crossings.count(node / 4) != 0;
|
|
1639
|
+
};
|
|
1640
|
+
auto is_removed_red_node = [&](int node) {
|
|
1641
|
+
if (!is_removed_node(node)) {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
const int crossing = node / 4;
|
|
1645
|
+
const int strand = node % 4;
|
|
1646
|
+
const int red_strand = red_entry_by_crossing.at(crossing);
|
|
1647
|
+
return strand == red_strand || strand == positive_mod(red_strand + 2, 4);
|
|
1648
|
+
};
|
|
1649
|
+
|
|
1650
|
+
std::set<int> crossed_labels;
|
|
1651
|
+
struct CrossedEdge {
|
|
1652
|
+
int interface_from = -1;
|
|
1653
|
+
int interface_to = -1;
|
|
1654
|
+
std::string level;
|
|
1655
|
+
};
|
|
1656
|
+
std::vector<CrossedEdge> crossed_edges;
|
|
1657
|
+
crossed_edges.reserve(new_crossing_count);
|
|
1658
|
+
for (int i = 0; i < new_crossing_count; ++i) {
|
|
1659
|
+
const int from_face = result.green_path[i];
|
|
1660
|
+
const int to_face = result.green_path[i + 1];
|
|
1661
|
+
const GraphEdge* edge = graph.edge(from_face, to_face);
|
|
1662
|
+
if (edge == nullptr) {
|
|
1663
|
+
throw std::invalid_argument("Simplification witness green path crosses a missing dual edge");
|
|
1664
|
+
}
|
|
1665
|
+
const int interface_from = graph.interface_for_face(*edge, from_face);
|
|
1666
|
+
const int interface_to = graph.interface_for_face(*edge, to_face);
|
|
1667
|
+
if (is_removed_red_node(interface_from) || is_removed_red_node(interface_to)) {
|
|
1668
|
+
throw std::invalid_argument("Simplification witness crosses an edge removed with the red arc");
|
|
1669
|
+
}
|
|
1670
|
+
const int label = code[interface_from / 4][interface_from % 4];
|
|
1671
|
+
if (!crossed_labels.insert(label).second) {
|
|
1672
|
+
throw std::invalid_argument("Simplification witness crosses the same PD edge more than once");
|
|
1673
|
+
}
|
|
1674
|
+
const auto level = levels.find(std::make_pair(from_face, to_face));
|
|
1675
|
+
if (level == levels.end()) {
|
|
1676
|
+
throw std::invalid_argument("Simplification witness is missing a green crossing level");
|
|
1677
|
+
}
|
|
1678
|
+
CrossedEdge crossed;
|
|
1679
|
+
crossed.interface_from = interface_from;
|
|
1680
|
+
crossed.interface_to = interface_to;
|
|
1681
|
+
crossed.level = level->second;
|
|
1682
|
+
crossed_edges.push_back(std::move(crossed));
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
std::map<int, std::vector<int>> label_endpoints;
|
|
1686
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1687
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1688
|
+
label_endpoints[code[crossing_index][strand]].push_back(crossing_index * 4 + strand);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
for (const auto& item : label_endpoints) {
|
|
1692
|
+
if (item.second.size() != 2) {
|
|
1693
|
+
std::ostringstream message;
|
|
1694
|
+
message << "PD label " << item.first << " appears " << item.second.size() << " times";
|
|
1695
|
+
throw std::invalid_argument(message.str());
|
|
1696
|
+
}
|
|
1697
|
+
if (crossed_labels.count(item.first) == 0) {
|
|
1698
|
+
dsu.unite(item.second[0], item.second[1]);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
for (const auto& item : red_entry_by_crossing) {
|
|
1703
|
+
const int crossing = item.first;
|
|
1704
|
+
const int strand = item.second;
|
|
1705
|
+
dsu.unite(crossing * 4 + positive_mod(strand + 1, 4),
|
|
1706
|
+
crossing * 4 + positive_mod(strand + 3, 4));
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
int green_anchor = endpoint_key(result.red_path.front());
|
|
1710
|
+
for (int i = 0; i < static_cast<int>(crossed_edges.size()); ++i) {
|
|
1711
|
+
const CrossedEdge& crossed = crossed_edges[i];
|
|
1712
|
+
int existing_from_pos = -1;
|
|
1713
|
+
int existing_to_pos = -1;
|
|
1714
|
+
int green_in_pos = -1;
|
|
1715
|
+
int green_out_pos = -1;
|
|
1716
|
+
if (crossed.level == "over") {
|
|
1717
|
+
existing_from_pos = 0;
|
|
1718
|
+
existing_to_pos = 2;
|
|
1719
|
+
green_in_pos = 3;
|
|
1720
|
+
green_out_pos = 1;
|
|
1721
|
+
} else if (crossed.level == "under") {
|
|
1722
|
+
existing_from_pos = 1;
|
|
1723
|
+
green_in_pos = 0;
|
|
1724
|
+
green_out_pos = 2;
|
|
1725
|
+
existing_to_pos = 3;
|
|
1726
|
+
} else {
|
|
1727
|
+
throw std::invalid_argument("Unknown green crossing strand level: " + crossed.level);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
dsu.unite(crossed.interface_from, new_node(i, existing_from_pos));
|
|
1731
|
+
dsu.unite(crossed.interface_to, new_node(i, existing_to_pos));
|
|
1732
|
+
dsu.unite(green_anchor, new_node(i, green_in_pos));
|
|
1733
|
+
green_anchor = new_node(i, green_out_pos);
|
|
1734
|
+
}
|
|
1735
|
+
dsu.unite(green_anchor, endpoint_key(result.red_path.back()));
|
|
1736
|
+
|
|
1737
|
+
std::vector<int> active_nodes;
|
|
1738
|
+
active_nodes.reserve(endpoint_count + new_crossing_count * 4);
|
|
1739
|
+
for (int node = 0; node < endpoint_count; ++node) {
|
|
1740
|
+
if (!is_removed_node(node)) {
|
|
1741
|
+
active_nodes.push_back(node);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
for (int crossing = 0; crossing < new_crossing_count; ++crossing) {
|
|
1745
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1746
|
+
active_nodes.push_back(new_node(crossing, strand));
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
std::map<int, std::vector<int>> grouped;
|
|
1751
|
+
for (int node : active_nodes) {
|
|
1752
|
+
grouped[dsu.find(node)].push_back(node);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
std::vector<std::vector<int>> groups;
|
|
1756
|
+
for (auto& item : grouped) {
|
|
1757
|
+
std::sort(item.second.begin(), item.second.end());
|
|
1758
|
+
groups.push_back(item.second);
|
|
1759
|
+
}
|
|
1760
|
+
std::sort(groups.begin(), groups.end(), [](const std::vector<int>& lhs, const std::vector<int>& rhs) {
|
|
1761
|
+
return lhs.front() < rhs.front();
|
|
1762
|
+
});
|
|
1763
|
+
|
|
1764
|
+
std::map<int, int> label_by_node;
|
|
1765
|
+
int next_label = 0;
|
|
1766
|
+
for (const std::vector<int>& nodes : groups) {
|
|
1767
|
+
if (nodes.size() != 2) {
|
|
1768
|
+
std::ostringstream message;
|
|
1769
|
+
message << "Applied simplification produced a non-PD edge with "
|
|
1770
|
+
<< nodes.size() << " active endpoints";
|
|
1771
|
+
throw std::runtime_error(message.str());
|
|
1772
|
+
}
|
|
1773
|
+
for (int node : nodes) {
|
|
1774
|
+
label_by_node[node] = next_label;
|
|
1775
|
+
}
|
|
1776
|
+
++next_label;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
PDCode output;
|
|
1780
|
+
output.reserve(code.size() - removed_crossings.size() + static_cast<std::size_t>(new_crossing_count));
|
|
1781
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1782
|
+
if (removed_crossings.count(crossing_index) != 0) {
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
Crossing crossing{};
|
|
1786
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1787
|
+
crossing[strand] = label_by_node.at(crossing_index * 4 + strand);
|
|
1788
|
+
}
|
|
1789
|
+
output.push_back(crossing);
|
|
1790
|
+
}
|
|
1791
|
+
for (int crossing_index = 0; crossing_index < new_crossing_count; ++crossing_index) {
|
|
1792
|
+
Crossing crossing{};
|
|
1793
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1794
|
+
crossing[strand] = label_by_node.at(new_node(crossing_index, strand));
|
|
1795
|
+
}
|
|
1796
|
+
output.push_back(crossing);
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
const std::size_t total_components =
|
|
1800
|
+
analyze_components(code, known_crossingless_components).total_components();
|
|
1801
|
+
output = renumber_full_dfs(output);
|
|
1802
|
+
if (!is_planar_pd_code(output)) {
|
|
1803
|
+
throw std::invalid_argument("Applied simplification produced a non-planar PD code");
|
|
1804
|
+
}
|
|
1805
|
+
const std::size_t crossing_components = analyze_components(output).components_with_crossings();
|
|
1806
|
+
|
|
1807
|
+
MidSimplificationApplyResult applied;
|
|
1808
|
+
applied.code = std::move(output);
|
|
1809
|
+
applied.crossingless_components =
|
|
1810
|
+
total_components > crossing_components ? total_components - crossing_components : 0;
|
|
1811
|
+
return applied;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
namespace {
|
|
1815
|
+
|
|
1816
|
+
bool witness_has_applicable_surgery(const PDCode& code, const SimplificationResult& result) {
|
|
1817
|
+
try {
|
|
1818
|
+
(void)apply_simplification_witness(code, result, 0);
|
|
1819
|
+
return true;
|
|
1820
|
+
} catch (const std::exception&) {
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
void emit_progress(const SimplifierOptions& options, const std::string& message) {
|
|
1826
|
+
if (!options.verbose) {
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
if (options.progress) {
|
|
1830
|
+
options.progress(message);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
std::string search_mode_for_options(const SimplifierOptions& options) {
|
|
1835
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
1836
|
+
return "heuristic";
|
|
1837
|
+
}
|
|
1838
|
+
if (options.max_paths == -1) {
|
|
1839
|
+
return "bruteforce";
|
|
1840
|
+
}
|
|
1841
|
+
return "bounded";
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1449
1844
|
} // namespace
|
|
1450
1845
|
|
|
1451
1846
|
PDCode parse_pd_code(const std::string& text) {
|
|
@@ -1486,13 +1881,13 @@ PDCode parse_pd_code(const std::string& text) {
|
|
|
1486
1881
|
|
|
1487
1882
|
std::string format_pd_code(const PDCode& code) {
|
|
1488
1883
|
std::ostringstream out;
|
|
1489
|
-
out <<
|
|
1884
|
+
out << "PD[";
|
|
1490
1885
|
for (std::size_t i = 0; i < code.size(); ++i) {
|
|
1491
1886
|
if (i != 0) {
|
|
1492
|
-
out <<
|
|
1887
|
+
out << ',';
|
|
1493
1888
|
}
|
|
1494
|
-
out <<
|
|
1495
|
-
<< code[i][2] <<
|
|
1889
|
+
out << "X[" << code[i][0] << ',' << code[i][1] << ','
|
|
1890
|
+
<< code[i][2] << ',' << code[i][3] << ']';
|
|
1496
1891
|
}
|
|
1497
1892
|
out << ']';
|
|
1498
1893
|
return out.str();
|
|
@@ -1826,75 +2221,442 @@ PDSimplificationResult simplify_pd_code(
|
|
|
1826
2221
|
return result;
|
|
1827
2222
|
}
|
|
1828
2223
|
|
|
2224
|
+
namespace {
|
|
2225
|
+
|
|
2226
|
+
int detected_worker_count() {
|
|
2227
|
+
const unsigned int reported = std::thread::hardware_concurrency();
|
|
2228
|
+
if (reported == 0) {
|
|
2229
|
+
return 1;
|
|
2230
|
+
}
|
|
2231
|
+
if (reported <= 2) {
|
|
2232
|
+
return static_cast<int>(reported);
|
|
2233
|
+
}
|
|
2234
|
+
return static_cast<int>(reported - 1);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
int selected_bruteforce_worker_count(int max_threads, int task_count) {
|
|
2238
|
+
if (task_count <= 1) {
|
|
2239
|
+
return 1;
|
|
2240
|
+
}
|
|
2241
|
+
int requested = max_threads == -1 ? detected_worker_count() : max_threads;
|
|
2242
|
+
if (requested < 1) {
|
|
2243
|
+
requested = 1;
|
|
2244
|
+
}
|
|
2245
|
+
if (max_threads == -1 && task_count < 32) {
|
|
2246
|
+
return 1;
|
|
2247
|
+
}
|
|
2248
|
+
return std::max(1, std::min(requested, task_count));
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
bool should_skip_parallel_red_path(
|
|
2252
|
+
int red_index,
|
|
2253
|
+
const std::atomic<int>* best_found_index) {
|
|
2254
|
+
return best_found_index != nullptr &&
|
|
2255
|
+
red_index > best_found_index->load(std::memory_order_relaxed);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
void record_parallel_found_index(
|
|
2259
|
+
int red_index,
|
|
2260
|
+
std::atomic<int>* best_found_index) {
|
|
2261
|
+
if (best_found_index == nullptr) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
int observed = best_found_index->load(std::memory_order_relaxed);
|
|
2265
|
+
while (red_index < observed &&
|
|
2266
|
+
!best_found_index->compare_exchange_weak(
|
|
2267
|
+
observed,
|
|
2268
|
+
red_index,
|
|
2269
|
+
std::memory_order_relaxed,
|
|
2270
|
+
std::memory_order_relaxed)) {
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
struct RedPathSearchOutcome {
|
|
2275
|
+
bool completed = false;
|
|
2276
|
+
bool skipped = false;
|
|
2277
|
+
bool found = false;
|
|
2278
|
+
std::size_t tested_green_paths = 0;
|
|
2279
|
+
SimplificationResult witness;
|
|
2280
|
+
};
|
|
2281
|
+
|
|
2282
|
+
RedPathSearchOutcome search_single_red_path(
|
|
2283
|
+
const PDCode& code,
|
|
2284
|
+
const Diagram& diagram,
|
|
2285
|
+
const DualGraph& base_graph,
|
|
2286
|
+
const std::vector<Endpoint>& red_path,
|
|
2287
|
+
const SimplifierOptions& options,
|
|
2288
|
+
const std::string& path_search_mode,
|
|
2289
|
+
int red_index,
|
|
2290
|
+
const std::atomic<int>* best_found_index) {
|
|
2291
|
+
RedPathSearchOutcome outcome;
|
|
2292
|
+
outcome.witness.path_search_mode = path_search_mode;
|
|
2293
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2294
|
+
outcome.skipped = true;
|
|
2295
|
+
return outcome;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
DualGraph graph = base_graph;
|
|
2299
|
+
const Endpoint start = red_path.front();
|
|
2300
|
+
const Endpoint end = red_path.back();
|
|
2301
|
+
const int start_face = graph.edge_to_face[endpoint_key(start)];
|
|
2302
|
+
const int start_opposite_face = graph.edge_to_face[endpoint_key(diagram.opposite(start))];
|
|
2303
|
+
const int end_face = graph.edge_to_face[endpoint_key(end)];
|
|
2304
|
+
const int end_opposite_face = graph.edge_to_face[endpoint_key(diagram.opposite(end))];
|
|
2305
|
+
const std::array<int, 2> sources{start_face, start_opposite_face};
|
|
2306
|
+
const std::array<int, 2> destinations{end_face, end_opposite_face};
|
|
2307
|
+
|
|
2308
|
+
for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
|
|
2309
|
+
const Endpoint endpoint = red_path[i];
|
|
2310
|
+
const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
|
|
2311
|
+
const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
|
|
2312
|
+
if (GraphEdge* edge = graph.mutable_edge(right_region, left_region)) {
|
|
2313
|
+
edge->weight = kBlockedWeight;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
std::vector<std::vector<int>> paths;
|
|
2318
|
+
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
2319
|
+
for (int source : sources) {
|
|
2320
|
+
for (int destination : destinations) {
|
|
2321
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2322
|
+
outcome.skipped = true;
|
|
2323
|
+
return outcome;
|
|
2324
|
+
}
|
|
2325
|
+
std::vector<std::vector<int>> found_paths;
|
|
2326
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2327
|
+
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2328
|
+
} else {
|
|
2329
|
+
found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
|
|
2330
|
+
}
|
|
2331
|
+
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
2332
|
+
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
2333
|
+
break;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
for (const auto& green_path : paths) {
|
|
2339
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2340
|
+
outcome.skipped = true;
|
|
2341
|
+
return outcome;
|
|
2342
|
+
}
|
|
2343
|
+
++outcome.tested_green_paths;
|
|
2344
|
+
if (green_path.size() >= red_path.size()) {
|
|
2345
|
+
continue;
|
|
2346
|
+
}
|
|
2347
|
+
if (do_check(diagram, graph, red_path, green_path, Direction::Left, outcome.witness)) {
|
|
2348
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, outcome.witness)) {
|
|
2349
|
+
outcome.found = true;
|
|
2350
|
+
outcome.completed = true;
|
|
2351
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2352
|
+
return outcome;
|
|
2353
|
+
}
|
|
2354
|
+
clear_witness(outcome.witness);
|
|
2355
|
+
}
|
|
2356
|
+
if (do_check(diagram, graph, red_path, green_path, Direction::Right, outcome.witness)) {
|
|
2357
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, outcome.witness)) {
|
|
2358
|
+
outcome.found = true;
|
|
2359
|
+
outcome.completed = true;
|
|
2360
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2361
|
+
return outcome;
|
|
2362
|
+
}
|
|
2363
|
+
clear_witness(outcome.witness);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
outcome.completed = true;
|
|
2368
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2369
|
+
return outcome;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
SimplificationResult merge_red_path_outcomes(
|
|
2373
|
+
const std::vector<RedPathSearchOutcome>& outcomes,
|
|
2374
|
+
const std::string& path_search_mode) {
|
|
2375
|
+
SimplificationResult result;
|
|
2376
|
+
result.path_search_mode = path_search_mode;
|
|
2377
|
+
|
|
2378
|
+
int first_found = -1;
|
|
2379
|
+
for (int i = 0; i < static_cast<int>(outcomes.size()); ++i) {
|
|
2380
|
+
if (outcomes[i].found) {
|
|
2381
|
+
first_found = i;
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
const int limit = first_found >= 0 ? first_found : static_cast<int>(outcomes.size()) - 1;
|
|
2387
|
+
for (int i = 0; i <= limit; ++i) {
|
|
2388
|
+
if (!outcomes[i].completed && !outcomes[i].found) {
|
|
2389
|
+
std::ostringstream message;
|
|
2390
|
+
message << "Parallel brute-force search did not complete red path " << i;
|
|
2391
|
+
throw std::runtime_error(message.str());
|
|
2392
|
+
}
|
|
2393
|
+
++result.tested_red_paths;
|
|
2394
|
+
result.tested_green_paths += outcomes[i].tested_green_paths;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
if (first_found >= 0) {
|
|
2398
|
+
result = outcomes[first_found].witness;
|
|
2399
|
+
result.path_search_mode = path_search_mode;
|
|
2400
|
+
result.tested_red_paths = static_cast<std::size_t>(first_found + 1);
|
|
2401
|
+
result.tested_green_paths = 0;
|
|
2402
|
+
for (int i = 0; i <= first_found; ++i) {
|
|
2403
|
+
result.tested_green_paths += outcomes[i].tested_green_paths;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return result;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
SimplificationResult find_simplification_parallel_bruteforce(
|
|
2410
|
+
const PDCode& code,
|
|
2411
|
+
const Diagram& diagram,
|
|
2412
|
+
const DualGraph& base_graph,
|
|
2413
|
+
const std::vector<std::vector<Endpoint>>& red_lines,
|
|
2414
|
+
const SimplifierOptions& options,
|
|
2415
|
+
const std::string& path_search_mode,
|
|
2416
|
+
int worker_count) {
|
|
2417
|
+
std::vector<RedPathSearchOutcome> outcomes(red_lines.size());
|
|
2418
|
+
std::atomic<int> next_index(0);
|
|
2419
|
+
std::atomic<int> best_found_index(static_cast<int>(red_lines.size()));
|
|
2420
|
+
std::atomic<bool> failed(false);
|
|
2421
|
+
std::exception_ptr first_exception;
|
|
2422
|
+
std::mutex exception_mutex;
|
|
2423
|
+
|
|
2424
|
+
auto worker = [&]() {
|
|
2425
|
+
while (!failed.load(std::memory_order_relaxed)) {
|
|
2426
|
+
const int index = next_index.fetch_add(1, std::memory_order_relaxed);
|
|
2427
|
+
if (index >= static_cast<int>(red_lines.size())) {
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
if (should_skip_parallel_red_path(index, &best_found_index)) {
|
|
2431
|
+
outcomes[index].skipped = true;
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
try {
|
|
2435
|
+
RedPathSearchOutcome outcome = search_single_red_path(
|
|
2436
|
+
code,
|
|
2437
|
+
diagram,
|
|
2438
|
+
base_graph,
|
|
2439
|
+
red_lines[index],
|
|
2440
|
+
options,
|
|
2441
|
+
path_search_mode,
|
|
2442
|
+
index,
|
|
2443
|
+
&best_found_index);
|
|
2444
|
+
if (outcome.found) {
|
|
2445
|
+
record_parallel_found_index(index, &best_found_index);
|
|
2446
|
+
}
|
|
2447
|
+
outcomes[index] = std::move(outcome);
|
|
2448
|
+
} catch (...) {
|
|
2449
|
+
failed.store(true, std::memory_order_relaxed);
|
|
2450
|
+
std::lock_guard<std::mutex> lock(exception_mutex);
|
|
2451
|
+
if (!first_exception) {
|
|
2452
|
+
first_exception = std::current_exception();
|
|
2453
|
+
}
|
|
2454
|
+
break;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
std::vector<std::thread> workers;
|
|
2460
|
+
workers.reserve(static_cast<std::size_t>(worker_count));
|
|
2461
|
+
for (int i = 0; i < worker_count; ++i) {
|
|
2462
|
+
workers.emplace_back(worker);
|
|
2463
|
+
}
|
|
2464
|
+
for (std::thread& thread : workers) {
|
|
2465
|
+
thread.join();
|
|
2466
|
+
}
|
|
2467
|
+
if (first_exception) {
|
|
2468
|
+
std::rethrow_exception(first_exception);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
return merge_red_path_outcomes(outcomes, path_search_mode);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
} // namespace
|
|
2475
|
+
|
|
1829
2476
|
SimplificationResult find_simplification(
|
|
1830
2477
|
const PDCode& code,
|
|
1831
2478
|
const SimplifierOptions& options) {
|
|
1832
2479
|
SimplificationResult result;
|
|
1833
|
-
|
|
1834
|
-
result.path_search_mode = "heuristic";
|
|
1835
|
-
} else if (options.max_paths == -1) {
|
|
1836
|
-
result.path_search_mode = "bruteforce";
|
|
1837
|
-
} else {
|
|
1838
|
-
result.path_search_mode = "bounded";
|
|
1839
|
-
}
|
|
2480
|
+
result.path_search_mode = search_mode_for_options(options);
|
|
1840
2481
|
Diagram diagram(code);
|
|
1841
|
-
DualGraph
|
|
2482
|
+
DualGraph base_graph(diagram);
|
|
1842
2483
|
const auto red_lines = possible_red_lines(diagram);
|
|
2484
|
+
const bool brute_force_mode = options.max_paths == -1 && options.ban_heuristic;
|
|
2485
|
+
const int worker_count = brute_force_mode
|
|
2486
|
+
? selected_bruteforce_worker_count(
|
|
2487
|
+
options.max_threads,
|
|
2488
|
+
static_cast<int>(red_lines.size()))
|
|
2489
|
+
: 1;
|
|
2490
|
+
if (worker_count > 1) {
|
|
2491
|
+
return find_simplification_parallel_bruteforce(
|
|
2492
|
+
code,
|
|
2493
|
+
diagram,
|
|
2494
|
+
base_graph,
|
|
2495
|
+
red_lines,
|
|
2496
|
+
options,
|
|
2497
|
+
result.path_search_mode,
|
|
2498
|
+
worker_count);
|
|
2499
|
+
}
|
|
1843
2500
|
|
|
1844
2501
|
for (const auto& red_path : red_lines) {
|
|
1845
2502
|
++result.tested_red_paths;
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
edge->weight = kBlockedWeight;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
std::vector<std::vector<int>> paths;
|
|
1867
|
-
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
1868
|
-
for (int source : sources) {
|
|
1869
|
-
for (int destination : destinations) {
|
|
1870
|
-
std::vector<std::vector<int>> found_paths;
|
|
1871
|
-
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
1872
|
-
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
1873
|
-
} else {
|
|
1874
|
-
found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
|
|
1875
|
-
}
|
|
1876
|
-
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
1877
|
-
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
1878
|
-
break;
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
2503
|
+
const RedPathSearchOutcome outcome = search_single_red_path(
|
|
2504
|
+
code,
|
|
2505
|
+
diagram,
|
|
2506
|
+
base_graph,
|
|
2507
|
+
red_path,
|
|
2508
|
+
options,
|
|
2509
|
+
result.path_search_mode,
|
|
2510
|
+
-1,
|
|
2511
|
+
nullptr);
|
|
2512
|
+
result.tested_green_paths += outcome.tested_green_paths;
|
|
2513
|
+
if (outcome.found) {
|
|
2514
|
+
SimplificationResult found = outcome.witness;
|
|
2515
|
+
found.path_search_mode = result.path_search_mode;
|
|
2516
|
+
found.tested_red_paths = result.tested_red_paths;
|
|
2517
|
+
found.tested_green_paths = result.tested_green_paths;
|
|
2518
|
+
return found;
|
|
1881
2519
|
}
|
|
2520
|
+
}
|
|
1882
2521
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2522
|
+
return result;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
ReductionResult reduce_pd_code(
|
|
2526
|
+
const PDCode& code,
|
|
2527
|
+
std::size_t known_crossingless_components,
|
|
2528
|
+
const SimplifierOptions& options,
|
|
2529
|
+
int reduction_round) {
|
|
2530
|
+
{
|
|
2531
|
+
std::ostringstream message;
|
|
2532
|
+
message << "start input_crossings=" << code.size()
|
|
2533
|
+
<< " known_crossingless_components=" << known_crossingless_components
|
|
2534
|
+
<< " reduction_round=" << reduction_round
|
|
2535
|
+
<< " max_paths=" << options.max_paths
|
|
2536
|
+
<< " max_thread=" << options.max_threads
|
|
2537
|
+
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2538
|
+
emit_progress(options, message.str());
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
|
|
2542
|
+
ReductionResult output;
|
|
2543
|
+
output.code = prepared.code;
|
|
2544
|
+
output.crossingless_components = prepared.crossingless_components;
|
|
2545
|
+
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2546
|
+
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2547
|
+
{
|
|
2548
|
+
std::ostringstream message;
|
|
2549
|
+
message << "pre_simplify input_crossings=" << code.size()
|
|
2550
|
+
<< " output_crossings=" << output.code.size()
|
|
2551
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2552
|
+
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2553
|
+
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2554
|
+
emit_progress(options, message.str());
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
|
|
2558
|
+
SimplifierOptions search_options = options;
|
|
2559
|
+
search_options.require_applicable = true;
|
|
2560
|
+
const int round = output.mid_simplification_rounds + 1;
|
|
2561
|
+
{
|
|
2562
|
+
std::ostringstream message;
|
|
2563
|
+
message << "round " << round
|
|
2564
|
+
<< " search_start crossings=" << output.code.size()
|
|
2565
|
+
<< " mode=" << search_mode_for_options(search_options)
|
|
2566
|
+
<< " max_thread=" << search_options.max_threads;
|
|
2567
|
+
emit_progress(options, message.str());
|
|
2568
|
+
}
|
|
2569
|
+
SimplificationResult search = find_simplification(output.code, search_options);
|
|
2570
|
+
output.tested_red_paths += search.tested_red_paths;
|
|
2571
|
+
output.tested_green_paths += search.tested_green_paths;
|
|
2572
|
+
output.last_path_search_mode = search.path_search_mode;
|
|
2573
|
+
{
|
|
2574
|
+
std::ostringstream message;
|
|
2575
|
+
message << "round " << round
|
|
2576
|
+
<< " search_done found=" << (search.found ? "yes" : "no")
|
|
2577
|
+
<< " mode=" << search.path_search_mode
|
|
2578
|
+
<< " tested_red=" << search.tested_red_paths
|
|
2579
|
+
<< " tested_green=" << search.tested_green_paths;
|
|
2580
|
+
emit_progress(options, message.str());
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
if (!search.found && reduction_round < 0 &&
|
|
2584
|
+
options.max_paths == -1 && !options.ban_heuristic) {
|
|
2585
|
+
SimplifierOptions brute_options = options;
|
|
2586
|
+
brute_options.max_paths = -1;
|
|
2587
|
+
brute_options.ban_heuristic = true;
|
|
2588
|
+
brute_options.require_applicable = true;
|
|
2589
|
+
{
|
|
2590
|
+
std::ostringstream message;
|
|
2591
|
+
message << "round " << round
|
|
2592
|
+
<< " brute_fallback_start crossings=" << output.code.size()
|
|
2593
|
+
<< " max_thread=" << brute_options.max_threads;
|
|
2594
|
+
emit_progress(options, message.str());
|
|
2595
|
+
}
|
|
2596
|
+
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
2597
|
+
output.tested_red_paths += brute.tested_red_paths;
|
|
2598
|
+
output.tested_green_paths += brute.tested_green_paths;
|
|
2599
|
+
output.last_path_search_mode = brute.path_search_mode;
|
|
2600
|
+
{
|
|
2601
|
+
std::ostringstream message;
|
|
2602
|
+
message << "round " << round
|
|
2603
|
+
<< " brute_fallback_done found=" << (brute.found ? "yes" : "no")
|
|
2604
|
+
<< " tested_red=" << brute.tested_red_paths
|
|
2605
|
+
<< " tested_green=" << brute.tested_green_paths;
|
|
2606
|
+
emit_progress(options, message.str());
|
|
1887
2607
|
}
|
|
1888
|
-
if (
|
|
1889
|
-
|
|
2608
|
+
if (brute.found) {
|
|
2609
|
+
++output.heuristic_failover_rounds;
|
|
2610
|
+
search = std::move(brute);
|
|
1890
2611
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
if (!search.found) {
|
|
2615
|
+
{
|
|
2616
|
+
std::ostringstream message;
|
|
2617
|
+
message << "round " << round
|
|
2618
|
+
<< " stop_no_path crossings=" << output.code.size();
|
|
2619
|
+
emit_progress(options, message.str());
|
|
1893
2620
|
}
|
|
2621
|
+
break;
|
|
1894
2622
|
}
|
|
1895
|
-
}
|
|
1896
2623
|
|
|
1897
|
-
|
|
2624
|
+
const std::size_t before_apply_crossings = output.code.size();
|
|
2625
|
+
const MidSimplificationApplyResult applied =
|
|
2626
|
+
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2627
|
+
++output.mid_simplification_rounds;
|
|
2628
|
+
const PDSimplificationResult simplified =
|
|
2629
|
+
simplify_pd_code(applied.code, applied.crossingless_components);
|
|
2630
|
+
output.code = simplified.code;
|
|
2631
|
+
output.crossingless_components = simplified.crossingless_components;
|
|
2632
|
+
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2633
|
+
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2634
|
+
{
|
|
2635
|
+
std::ostringstream message;
|
|
2636
|
+
message << "round " << round
|
|
2637
|
+
<< " applied crossings=" << before_apply_crossings
|
|
2638
|
+
<< " -> " << applied.code.size()
|
|
2639
|
+
<< " -> " << output.code.size()
|
|
2640
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2641
|
+
<< " r1_moves=" << simplified.reidemeister_i_moves
|
|
2642
|
+
<< " nugatory_moves=" << simplified.nugatory_crossing_moves;
|
|
2643
|
+
emit_progress(options, message.str());
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
output.stopped_by_round_limit =
|
|
2648
|
+
reduction_round >= 0 && output.mid_simplification_rounds >= reduction_round;
|
|
2649
|
+
{
|
|
2650
|
+
std::ostringstream message;
|
|
2651
|
+
message << "done final_crossings=" << output.code.size()
|
|
2652
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2653
|
+
<< " mid_rounds=" << output.mid_simplification_rounds
|
|
2654
|
+
<< " heuristic_failover_rounds=" << output.heuristic_failover_rounds
|
|
2655
|
+
<< " stopped_by_round_limit="
|
|
2656
|
+
<< (output.stopped_by_round_limit ? "yes" : "no");
|
|
2657
|
+
emit_progress(options, message.str());
|
|
2658
|
+
}
|
|
2659
|
+
return output;
|
|
1898
2660
|
}
|
|
1899
2661
|
|
|
1900
2662
|
std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint) {
|