cpp-pd-code-simplify-interface 0.1.1__py3-none-any.whl → 0.1.3__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 +36 -1
- cpp_pd_code_simplify_interface/data/src/native_interface.cpp +33 -54
- cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +692 -23
- cpp_pd_code_simplify_interface/main.py +128 -25
- {cpp_pd_code_simplify_interface-0.1.1.dist-info → cpp_pd_code_simplify_interface-0.1.3.dist-info}/METADATA +10 -4
- {cpp_pd_code_simplify_interface-0.1.1.dist-info → cpp_pd_code_simplify_interface-0.1.3.dist-info}/RECORD +8 -8
- {cpp_pd_code_simplify_interface-0.1.1.dist-info → cpp_pd_code_simplify_interface-0.1.3.dist-info}/WHEEL +0 -0
- {cpp_pd_code_simplify_interface-0.1.1.dist-info → cpp_pd_code_simplify_interface-0.1.3.dist-info}/entry_points.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <array>
|
|
4
4
|
#include <cstddef>
|
|
5
|
+
#include <functional>
|
|
5
6
|
#include <iosfwd>
|
|
6
7
|
#include <string>
|
|
7
8
|
#include <vector>
|
|
@@ -48,7 +49,11 @@ struct GreenCrossing {
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
struct SimplifierOptions {
|
|
51
|
-
int max_paths =
|
|
52
|
+
int max_paths = -1;
|
|
53
|
+
bool ban_heuristic = false;
|
|
54
|
+
bool require_applicable = false;
|
|
55
|
+
bool verbose = false;
|
|
56
|
+
std::function<void(const std::string&)> progress;
|
|
52
57
|
};
|
|
53
58
|
|
|
54
59
|
struct LinkComponentSummary {
|
|
@@ -98,6 +103,7 @@ struct PDSimplificationResult {
|
|
|
98
103
|
struct SimplificationResult {
|
|
99
104
|
bool found = false;
|
|
100
105
|
Direction direction = Direction::Left;
|
|
106
|
+
std::string path_search_mode;
|
|
101
107
|
std::vector<Endpoint> red_path;
|
|
102
108
|
std::vector<int> green_path;
|
|
103
109
|
std::vector<GreenCrossing> green_crossings;
|
|
@@ -105,6 +111,24 @@ struct SimplificationResult {
|
|
|
105
111
|
std::size_t tested_green_paths = 0;
|
|
106
112
|
};
|
|
107
113
|
|
|
114
|
+
struct MidSimplificationApplyResult {
|
|
115
|
+
PDCode code;
|
|
116
|
+
std::size_t crossingless_components = 0;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
struct ReductionResult {
|
|
120
|
+
PDCode code;
|
|
121
|
+
std::size_t crossingless_components = 0;
|
|
122
|
+
int mid_simplification_rounds = 0;
|
|
123
|
+
int heuristic_failover_rounds = 0;
|
|
124
|
+
int reidemeister_i_moves = 0;
|
|
125
|
+
int nugatory_crossing_moves = 0;
|
|
126
|
+
std::size_t tested_red_paths = 0;
|
|
127
|
+
std::size_t tested_green_paths = 0;
|
|
128
|
+
std::string last_path_search_mode;
|
|
129
|
+
bool stopped_by_round_limit = false;
|
|
130
|
+
};
|
|
131
|
+
|
|
108
132
|
PDCODE_SIMPLIFY_API PDCode parse_pd_code(const std::string& text);
|
|
109
133
|
PDCODE_SIMPLIFY_API std::string format_pd_code(const PDCode& code);
|
|
110
134
|
PDCODE_SIMPLIFY_API std::string format_endpoint(const Endpoint& endpoint);
|
|
@@ -140,6 +164,17 @@ PDCODE_SIMPLIFY_API SimplificationResult find_simplification(
|
|
|
140
164
|
const PDCode& code,
|
|
141
165
|
const SimplifierOptions& options = SimplifierOptions{});
|
|
142
166
|
|
|
167
|
+
PDCODE_SIMPLIFY_API MidSimplificationApplyResult apply_simplification_witness(
|
|
168
|
+
const PDCode& code,
|
|
169
|
+
const SimplificationResult& result,
|
|
170
|
+
std::size_t known_crossingless_components = 0);
|
|
171
|
+
|
|
172
|
+
PDCODE_SIMPLIFY_API ReductionResult reduce_pd_code(
|
|
173
|
+
const PDCode& code,
|
|
174
|
+
std::size_t known_crossingless_components = 0,
|
|
175
|
+
const SimplifierOptions& options = SimplifierOptions{},
|
|
176
|
+
int reduction_round = -1);
|
|
177
|
+
|
|
143
178
|
PDCODE_SIMPLIFY_API std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint);
|
|
144
179
|
|
|
145
180
|
} // namespace pdcode_simplify
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <cstdlib>
|
|
4
4
|
#include <cstring>
|
|
5
5
|
#include <exception>
|
|
6
|
+
#include <iostream>
|
|
6
7
|
#include <sstream>
|
|
7
8
|
#include <string>
|
|
8
9
|
#include <vector>
|
|
@@ -55,14 +56,14 @@ void append_component_counts(
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
std::string result_to_json(
|
|
58
|
-
const pdcode_simplify::
|
|
59
|
+
const pdcode_simplify::ReductionResult& result,
|
|
59
60
|
const pdcode_simplify::ComponentAnalysis& input_components,
|
|
60
|
-
const pdcode_simplify::ComponentAnalysis
|
|
61
|
-
const pdcode_simplify::
|
|
62
|
-
const pdcode_simplify::ComponentAnalysis& search_components) {
|
|
61
|
+
const pdcode_simplify::ComponentAnalysis& final_components,
|
|
62
|
+
const pdcode_simplify::ComponentAnalysis* after_removal_components) {
|
|
63
63
|
std::ostringstream out;
|
|
64
64
|
out << "{";
|
|
65
|
-
out << "\"simplification_found\":"
|
|
65
|
+
out << "\"simplification_found\":"
|
|
66
|
+
<< (result.mid_simplification_rounds > 0 ? "true" : "false") << ",";
|
|
66
67
|
out << "\"input_components\":{";
|
|
67
68
|
append_component_counts(out, input_components);
|
|
68
69
|
out << "},";
|
|
@@ -71,49 +72,21 @@ std::string result_to_json(
|
|
|
71
72
|
append_component_counts(out, *after_removal_components);
|
|
72
73
|
out << "},";
|
|
73
74
|
}
|
|
74
|
-
out << "\"
|
|
75
|
-
<< "\"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<< "},";
|
|
80
|
-
out << "\"search_components\":{";
|
|
81
|
-
append_component_counts(out, search_components);
|
|
75
|
+
out << "\"final_pd_code\":\"" << json_escape(pdcode_simplify::format_pd_code(result.code))
|
|
76
|
+
<< "\",";
|
|
77
|
+
out << "\"final_crossings\":" << result.code.size() << ",";
|
|
78
|
+
out << "\"final_components\":{";
|
|
79
|
+
append_component_counts(out, final_components);
|
|
82
80
|
out << "},";
|
|
81
|
+
out << "\"mid_simplification_rounds\":" << result.mid_simplification_rounds << ",";
|
|
82
|
+
out << "\"heuristic_failover_rounds\":" << result.heuristic_failover_rounds << ",";
|
|
83
|
+
out << "\"reidemeister_i_moves\":" << result.reidemeister_i_moves << ",";
|
|
84
|
+
out << "\"nugatory_crossing_moves\":" << result.nugatory_crossing_moves << ",";
|
|
83
85
|
out << "\"tested_red_paths\":" << result.tested_red_paths << ",";
|
|
84
|
-
out << "\"tested_green_paths\":" << result.tested_green_paths;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
out << "\"red_path\":[";
|
|
89
|
-
for (std::size_t i = 0; i < result.red_path.size(); ++i) {
|
|
90
|
-
if (i != 0) {
|
|
91
|
-
out << ",";
|
|
92
|
-
}
|
|
93
|
-
out << "{\"crossing\":" << result.red_path[i].crossing
|
|
94
|
-
<< ",\"strand\":" << result.red_path[i].strand << "}";
|
|
95
|
-
}
|
|
96
|
-
out << "],";
|
|
97
|
-
out << "\"green_path\":[";
|
|
98
|
-
for (std::size_t i = 0; i < result.green_path.size(); ++i) {
|
|
99
|
-
if (i != 0) {
|
|
100
|
-
out << ",";
|
|
101
|
-
}
|
|
102
|
-
out << result.green_path[i];
|
|
103
|
-
}
|
|
104
|
-
out << "],";
|
|
105
|
-
out << "\"green_crossings\":[";
|
|
106
|
-
for (std::size_t i = 0; i < result.green_crossings.size(); ++i) {
|
|
107
|
-
if (i != 0) {
|
|
108
|
-
out << ",";
|
|
109
|
-
}
|
|
110
|
-
const auto& crossing = result.green_crossings[i];
|
|
111
|
-
out << "{\"from_face\":" << crossing.from_face
|
|
112
|
-
<< ",\"to_face\":" << crossing.to_face
|
|
113
|
-
<< ",\"strand_level\":\"" << json_escape(crossing.strand_level) << "\"}";
|
|
114
|
-
}
|
|
115
|
-
out << "]";
|
|
116
|
-
}
|
|
86
|
+
out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
|
|
87
|
+
out << "\"last_path_search_mode\":\"" << json_escape(result.last_path_search_mode) << "\",";
|
|
88
|
+
out << "\"stopped_by_round_limit\":"
|
|
89
|
+
<< (result.stopped_by_round_limit ? "true" : "false");
|
|
117
90
|
out << "}";
|
|
118
91
|
return out.str();
|
|
119
92
|
}
|
|
@@ -137,6 +110,9 @@ __declspec(dllexport)
|
|
|
137
110
|
char* pdcode_simplify_run_json(
|
|
138
111
|
const char* pd_text,
|
|
139
112
|
int max_paths,
|
|
113
|
+
int ban_heuristic,
|
|
114
|
+
int reduction_round,
|
|
115
|
+
int verbose,
|
|
140
116
|
unsigned long long known_crossingless_components,
|
|
141
117
|
const int* removed_crossings,
|
|
142
118
|
unsigned long long removed_crossing_count) {
|
|
@@ -148,6 +124,11 @@ char* pdcode_simplify_run_json(
|
|
|
148
124
|
const std::string text(pd_text);
|
|
149
125
|
pdcode_simplify::SimplifierOptions options;
|
|
150
126
|
options.max_paths = max_paths;
|
|
127
|
+
options.ban_heuristic = ban_heuristic != 0;
|
|
128
|
+
options.verbose = verbose != 0;
|
|
129
|
+
options.progress = [](const std::string& message) {
|
|
130
|
+
std::cerr << "[pdcode-simplify] " << message << '\n';
|
|
131
|
+
};
|
|
151
132
|
|
|
152
133
|
const pdcode_simplify::PDCode code = pdcode_simplify::parse_pd_code(text);
|
|
153
134
|
std::size_t crossingless = static_cast<std::size_t>(known_crossingless_components);
|
|
@@ -167,17 +148,15 @@ char* pdcode_simplify_run_json(
|
|
|
167
148
|
code, removed, crossingless);
|
|
168
149
|
}
|
|
169
150
|
|
|
170
|
-
const auto
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const auto result = pdcode_simplify::find_simplification(pd_simplification.code, options);
|
|
151
|
+
const auto result =
|
|
152
|
+
pdcode_simplify::reduce_pd_code(code, crossingless, options, reduction_round);
|
|
153
|
+
const auto final_components =
|
|
154
|
+
pdcode_simplify::analyze_components(result.code, result.crossingless_components);
|
|
175
155
|
return copy_string(result_to_json(
|
|
176
156
|
result,
|
|
177
157
|
input_components,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
search_components));
|
|
158
|
+
final_components,
|
|
159
|
+
has_removal ? &after_removal_components : nullptr));
|
|
181
160
|
} catch (const std::exception& error) {
|
|
182
161
|
return copy_string(std::string("{\"error\":\"") + json_escape(error.what()) + "\"}");
|
|
183
162
|
} catch (...) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <limits>
|
|
8
8
|
#include <map>
|
|
9
9
|
#include <numeric>
|
|
10
|
+
#include <queue>
|
|
10
11
|
#include <random>
|
|
11
12
|
#include <set>
|
|
12
13
|
#include <sstream>
|
|
@@ -18,6 +19,11 @@ namespace pdcode_simplify {
|
|
|
18
19
|
namespace {
|
|
19
20
|
|
|
20
21
|
constexpr int kBlockedWeight = 10000;
|
|
22
|
+
constexpr int kHeuristicBeamWidth = 8;
|
|
23
|
+
constexpr int kHeuristicMinStateBudget = 128;
|
|
24
|
+
constexpr int kHeuristicMaxStateBudget = 4096;
|
|
25
|
+
constexpr int kHeuristicMinPathBudget = 24;
|
|
26
|
+
constexpr int kHeuristicMaxPathBudget = 384;
|
|
21
27
|
|
|
22
28
|
int positive_mod(int value, int modulus) {
|
|
23
29
|
int result = value % modulus;
|
|
@@ -998,18 +1004,30 @@ void reset_weights(DualGraph& graph) {
|
|
|
998
1004
|
}
|
|
999
1005
|
}
|
|
1000
1006
|
|
|
1007
|
+
std::vector<int> heuristic_distances_to_target(
|
|
1008
|
+
const DualGraph& graph,
|
|
1009
|
+
int target,
|
|
1010
|
+
int cutoff);
|
|
1011
|
+
|
|
1001
1012
|
void collect_simple_paths_dfs(
|
|
1002
1013
|
const DualGraph& graph,
|
|
1003
1014
|
int current,
|
|
1004
1015
|
int target,
|
|
1005
1016
|
int cutoff,
|
|
1006
1017
|
int max_paths,
|
|
1018
|
+
int current_weight,
|
|
1019
|
+
const std::vector<int>& distance,
|
|
1007
1020
|
std::vector<char>& visited,
|
|
1008
1021
|
std::vector<int>& current_path,
|
|
1009
1022
|
std::vector<std::vector<int>>& paths) {
|
|
1023
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1010
1024
|
if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
|
|
1011
1025
|
return;
|
|
1012
1026
|
}
|
|
1027
|
+
if (current < 0 || current >= static_cast<int>(distance.size()) ||
|
|
1028
|
+
distance[current] == infinity || current_weight + distance[current] >= cutoff) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1013
1031
|
|
|
1014
1032
|
for (int edge_index : graph.adjacency[current]) {
|
|
1015
1033
|
const GraphEdge& edge = graph.edges[edge_index];
|
|
@@ -1017,32 +1035,37 @@ void collect_simple_paths_dfs(
|
|
|
1017
1035
|
if (visited[next]) {
|
|
1018
1036
|
continue;
|
|
1019
1037
|
}
|
|
1038
|
+
const int next_weight = current_weight + edge.weight;
|
|
1039
|
+
if (next_weight >= cutoff) {
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (next < 0 || next >= static_cast<int>(distance.size()) ||
|
|
1043
|
+
distance[next] == infinity || next_weight + distance[next] >= cutoff) {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1020
1046
|
|
|
1021
1047
|
current_path.push_back(next);
|
|
1022
1048
|
visited[next] = true;
|
|
1023
1049
|
|
|
1024
1050
|
if (next == target) {
|
|
1025
|
-
|
|
1026
|
-
for (std::size_t i = 0; i + 1 < current_path.size(); ++i) {
|
|
1027
|
-
const GraphEdge* path_edge = graph.edge(current_path[i], current_path[i + 1]);
|
|
1028
|
-
if (path_edge == nullptr) {
|
|
1029
|
-
throw std::logic_error("Missing dual edge while weighing a path");
|
|
1030
|
-
}
|
|
1031
|
-
path_weight += path_edge->weight;
|
|
1032
|
-
if (path_weight >= cutoff) {
|
|
1033
|
-
break;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
if (path_weight < cutoff) {
|
|
1037
|
-
paths.push_back(current_path);
|
|
1038
|
-
}
|
|
1051
|
+
paths.push_back(current_path);
|
|
1039
1052
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1040
1053
|
visited[next] = false;
|
|
1041
1054
|
current_path.pop_back();
|
|
1042
1055
|
return;
|
|
1043
1056
|
}
|
|
1044
1057
|
} else {
|
|
1045
|
-
collect_simple_paths_dfs(
|
|
1058
|
+
collect_simple_paths_dfs(
|
|
1059
|
+
graph,
|
|
1060
|
+
next,
|
|
1061
|
+
target,
|
|
1062
|
+
cutoff,
|
|
1063
|
+
max_paths,
|
|
1064
|
+
next_weight,
|
|
1065
|
+
distance,
|
|
1066
|
+
visited,
|
|
1067
|
+
current_path,
|
|
1068
|
+
paths);
|
|
1046
1069
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1047
1070
|
visited[next] = false;
|
|
1048
1071
|
current_path.pop_back();
|
|
@@ -1071,8 +1094,216 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1071
1094
|
|
|
1072
1095
|
std::vector<char> visited(graph.faces.size(), false);
|
|
1073
1096
|
std::vector<int> current_path{source};
|
|
1097
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1074
1098
|
visited[source] = true;
|
|
1075
|
-
collect_simple_paths_dfs(
|
|
1099
|
+
collect_simple_paths_dfs(
|
|
1100
|
+
graph,
|
|
1101
|
+
source,
|
|
1102
|
+
target,
|
|
1103
|
+
cutoff,
|
|
1104
|
+
max_paths,
|
|
1105
|
+
0,
|
|
1106
|
+
distance,
|
|
1107
|
+
visited,
|
|
1108
|
+
current_path,
|
|
1109
|
+
paths);
|
|
1110
|
+
return paths;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
std::vector<int> heuristic_distances_to_target(
|
|
1114
|
+
const DualGraph& graph,
|
|
1115
|
+
int target,
|
|
1116
|
+
int cutoff) {
|
|
1117
|
+
const int face_count = static_cast<int>(graph.faces.size());
|
|
1118
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1119
|
+
std::vector<int> distance(face_count, infinity);
|
|
1120
|
+
std::deque<int> queue;
|
|
1121
|
+
distance[target] = 0;
|
|
1122
|
+
queue.push_back(target);
|
|
1123
|
+
|
|
1124
|
+
while (!queue.empty()) {
|
|
1125
|
+
const int current = queue.front();
|
|
1126
|
+
queue.pop_front();
|
|
1127
|
+
for (int edge_index : graph.adjacency[current]) {
|
|
1128
|
+
const GraphEdge& edge = graph.edges[edge_index];
|
|
1129
|
+
if (edge.weight >= cutoff) {
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
const int next = edge.u == current ? edge.v : edge.u;
|
|
1133
|
+
if (distance[next] != infinity) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
distance[next] = distance[current] + 1;
|
|
1137
|
+
queue.push_back(next);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return distance;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
struct HeuristicState {
|
|
1144
|
+
std::vector<int> path;
|
|
1145
|
+
std::vector<char> visited;
|
|
1146
|
+
int weight = 0;
|
|
1147
|
+
int branch_penalty = 0;
|
|
1148
|
+
int estimated_weight = 0;
|
|
1149
|
+
int estimated_length = 0;
|
|
1150
|
+
int serial = 0;
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
struct HeuristicStateWorse {
|
|
1154
|
+
bool operator()(const HeuristicState& lhs, const HeuristicState& rhs) const {
|
|
1155
|
+
if (lhs.estimated_weight != rhs.estimated_weight) {
|
|
1156
|
+
return lhs.estimated_weight > rhs.estimated_weight;
|
|
1157
|
+
}
|
|
1158
|
+
if (lhs.estimated_length != rhs.estimated_length) {
|
|
1159
|
+
return lhs.estimated_length > rhs.estimated_length;
|
|
1160
|
+
}
|
|
1161
|
+
if (lhs.branch_penalty != rhs.branch_penalty) {
|
|
1162
|
+
return lhs.branch_penalty > rhs.branch_penalty;
|
|
1163
|
+
}
|
|
1164
|
+
if (lhs.weight != rhs.weight) {
|
|
1165
|
+
return lhs.weight > rhs.weight;
|
|
1166
|
+
}
|
|
1167
|
+
if (lhs.path.size() != rhs.path.size()) {
|
|
1168
|
+
return lhs.path.size() > rhs.path.size();
|
|
1169
|
+
}
|
|
1170
|
+
return lhs.serial > rhs.serial;
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
struct HeuristicStep {
|
|
1175
|
+
int next = -1;
|
|
1176
|
+
int edge_index = -1;
|
|
1177
|
+
int edge_weight = 0;
|
|
1178
|
+
int distance = 0;
|
|
1179
|
+
int degree_penalty = 0;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
bool heuristic_step_less(const HeuristicStep& lhs, const HeuristicStep& rhs) {
|
|
1183
|
+
if (lhs.edge_weight != rhs.edge_weight) {
|
|
1184
|
+
return lhs.edge_weight < rhs.edge_weight;
|
|
1185
|
+
}
|
|
1186
|
+
if (lhs.distance != rhs.distance) {
|
|
1187
|
+
return lhs.distance < rhs.distance;
|
|
1188
|
+
}
|
|
1189
|
+
if (lhs.degree_penalty != rhs.degree_penalty) {
|
|
1190
|
+
return lhs.degree_penalty < rhs.degree_penalty;
|
|
1191
|
+
}
|
|
1192
|
+
if (lhs.next != rhs.next) {
|
|
1193
|
+
return lhs.next < rhs.next;
|
|
1194
|
+
}
|
|
1195
|
+
return lhs.edge_index < rhs.edge_index;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
std::vector<std::vector<int>> collect_heuristic_paths(
|
|
1199
|
+
const DualGraph& graph,
|
|
1200
|
+
int source,
|
|
1201
|
+
int target,
|
|
1202
|
+
int cutoff) {
|
|
1203
|
+
std::vector<std::vector<int>> paths;
|
|
1204
|
+
const int face_count = static_cast<int>(graph.faces.size());
|
|
1205
|
+
if (source == target || source < 0 || target < 0 ||
|
|
1206
|
+
source >= face_count || target >= face_count || cutoff <= 0) {
|
|
1207
|
+
return paths;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1211
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1212
|
+
if (distance[source] == infinity || distance[source] >= cutoff) {
|
|
1213
|
+
return paths;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const int state_budget = std::max(
|
|
1217
|
+
kHeuristicMinStateBudget,
|
|
1218
|
+
std::min(kHeuristicMaxStateBudget, face_count * std::max(1, cutoff) * 8));
|
|
1219
|
+
const int path_budget = std::max(
|
|
1220
|
+
kHeuristicMinPathBudget,
|
|
1221
|
+
std::min(kHeuristicMaxPathBudget, face_count * 2 + cutoff * 8));
|
|
1222
|
+
|
|
1223
|
+
std::priority_queue<HeuristicState, std::vector<HeuristicState>, HeuristicStateWorse> queue;
|
|
1224
|
+
int serial = 0;
|
|
1225
|
+
HeuristicState initial;
|
|
1226
|
+
initial.path.push_back(source);
|
|
1227
|
+
initial.visited.assign(face_count, false);
|
|
1228
|
+
initial.visited[source] = true;
|
|
1229
|
+
initial.estimated_weight = distance[source];
|
|
1230
|
+
initial.estimated_length = distance[source];
|
|
1231
|
+
initial.serial = serial++;
|
|
1232
|
+
queue.push(initial);
|
|
1233
|
+
|
|
1234
|
+
std::map<long long, int> popped_by_depth_face;
|
|
1235
|
+
int popped_states = 0;
|
|
1236
|
+
while (!queue.empty() && popped_states < state_budget &&
|
|
1237
|
+
static_cast<int>(paths.size()) < path_budget) {
|
|
1238
|
+
HeuristicState state = queue.top();
|
|
1239
|
+
queue.pop();
|
|
1240
|
+
++popped_states;
|
|
1241
|
+
|
|
1242
|
+
const int current = state.path.back();
|
|
1243
|
+
const int depth = static_cast<int>(state.path.size()) - 1;
|
|
1244
|
+
if (current == target) {
|
|
1245
|
+
if (state.weight < cutoff) {
|
|
1246
|
+
paths.push_back(state.path);
|
|
1247
|
+
}
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
if (depth >= cutoff - 1) {
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
const long long beam_key =
|
|
1255
|
+
static_cast<long long>(depth) * static_cast<long long>(face_count) + current;
|
|
1256
|
+
int& beam_count = popped_by_depth_face[beam_key];
|
|
1257
|
+
if (beam_count >= kHeuristicBeamWidth) {
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
++beam_count;
|
|
1261
|
+
|
|
1262
|
+
std::vector<HeuristicStep> steps;
|
|
1263
|
+
for (int edge_index : graph.adjacency[current]) {
|
|
1264
|
+
const GraphEdge& edge = graph.edges[edge_index];
|
|
1265
|
+
const int next = edge.u == current ? edge.v : edge.u;
|
|
1266
|
+
if (state.visited[next]) {
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
if (distance[next] == infinity) {
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
const int new_weight = state.weight + edge.weight;
|
|
1273
|
+
if (new_weight >= cutoff) {
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
const int new_depth = depth + 1;
|
|
1277
|
+
if (new_depth + distance[next] >= cutoff) {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
HeuristicStep step;
|
|
1281
|
+
step.next = next;
|
|
1282
|
+
step.edge_index = edge_index;
|
|
1283
|
+
step.edge_weight = edge.weight;
|
|
1284
|
+
step.distance = distance[next];
|
|
1285
|
+
step.degree_penalty = std::max(0, static_cast<int>(graph.adjacency[next].size()) - 2);
|
|
1286
|
+
steps.push_back(step);
|
|
1287
|
+
}
|
|
1288
|
+
std::sort(steps.begin(), steps.end(), heuristic_step_less);
|
|
1289
|
+
|
|
1290
|
+
for (const HeuristicStep& step : steps) {
|
|
1291
|
+
const GraphEdge& edge = graph.edges[step.edge_index];
|
|
1292
|
+
HeuristicState next_state;
|
|
1293
|
+
next_state.path = state.path;
|
|
1294
|
+
next_state.path.push_back(step.next);
|
|
1295
|
+
next_state.visited = state.visited;
|
|
1296
|
+
next_state.visited[step.next] = true;
|
|
1297
|
+
next_state.weight = state.weight + edge.weight;
|
|
1298
|
+
next_state.branch_penalty = state.branch_penalty + step.degree_penalty;
|
|
1299
|
+
next_state.estimated_weight = next_state.weight + distance[step.next];
|
|
1300
|
+
next_state.estimated_length =
|
|
1301
|
+
static_cast<int>(next_state.path.size()) - 1 + distance[step.next];
|
|
1302
|
+
next_state.serial = serial++;
|
|
1303
|
+
queue.push(std::move(next_state));
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1076
1307
|
return paths;
|
|
1077
1308
|
}
|
|
1078
1309
|
|
|
@@ -1243,6 +1474,292 @@ bool do_check(
|
|
|
1243
1474
|
return true;
|
|
1244
1475
|
}
|
|
1245
1476
|
|
|
1477
|
+
class DisjointSet {
|
|
1478
|
+
public:
|
|
1479
|
+
int find(int value) {
|
|
1480
|
+
auto inserted = parent_.insert(std::make_pair(value, value));
|
|
1481
|
+
int parent = inserted.first->second;
|
|
1482
|
+
if (parent != value) {
|
|
1483
|
+
parent = find(parent);
|
|
1484
|
+
parent_[value] = parent;
|
|
1485
|
+
}
|
|
1486
|
+
return parent;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
void unite(int first, int second) {
|
|
1490
|
+
int first_root = find(first);
|
|
1491
|
+
int second_root = find(second);
|
|
1492
|
+
if (first_root == second_root) {
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (second_root < first_root) {
|
|
1496
|
+
std::swap(first_root, second_root);
|
|
1497
|
+
}
|
|
1498
|
+
parent_[second_root] = first_root;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
private:
|
|
1502
|
+
std::map<int, int> parent_;
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
std::map<std::pair<int, int>, std::string> green_crossing_levels(
|
|
1506
|
+
const SimplificationResult& result) {
|
|
1507
|
+
std::map<std::pair<int, int>, std::string> levels;
|
|
1508
|
+
for (const GreenCrossing& crossing : result.green_crossings) {
|
|
1509
|
+
levels[std::make_pair(crossing.from_face, crossing.to_face)] = crossing.strand_level;
|
|
1510
|
+
}
|
|
1511
|
+
return levels;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
void clear_witness(SimplificationResult& result) {
|
|
1515
|
+
result.found = false;
|
|
1516
|
+
result.red_path.clear();
|
|
1517
|
+
result.green_path.clear();
|
|
1518
|
+
result.green_crossings.clear();
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
} // namespace
|
|
1522
|
+
|
|
1523
|
+
MidSimplificationApplyResult apply_simplification_witness(
|
|
1524
|
+
const PDCode& code,
|
|
1525
|
+
const SimplificationResult& result,
|
|
1526
|
+
std::size_t known_crossingless_components) {
|
|
1527
|
+
if (!result.found) {
|
|
1528
|
+
throw std::invalid_argument("Cannot apply a missing simplification witness");
|
|
1529
|
+
}
|
|
1530
|
+
if (result.red_path.size() < 2) {
|
|
1531
|
+
throw std::invalid_argument("Simplification witness red path is too short");
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
Diagram diagram(code);
|
|
1535
|
+
DualGraph graph(diagram);
|
|
1536
|
+
std::set<int> removed_crossings;
|
|
1537
|
+
std::map<int, int> red_entry_by_crossing;
|
|
1538
|
+
for (std::size_t i = 0; i + 1 < result.red_path.size(); ++i) {
|
|
1539
|
+
removed_crossings.insert(result.red_path[i].crossing);
|
|
1540
|
+
red_entry_by_crossing[result.red_path[i].crossing] = result.red_path[i].strand;
|
|
1541
|
+
}
|
|
1542
|
+
if (removed_crossings.size() != result.red_path.size() - 1) {
|
|
1543
|
+
throw std::invalid_argument("Simplification witness repeats a removed red crossing");
|
|
1544
|
+
}
|
|
1545
|
+
if (removed_crossings.count(result.red_path.back().crossing) != 0) {
|
|
1546
|
+
throw std::invalid_argument("Simplification witness ends inside the removed red arc");
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const std::map<std::pair<int, int>, std::string> levels = green_crossing_levels(result);
|
|
1550
|
+
DisjointSet dsu;
|
|
1551
|
+
const int endpoint_count = static_cast<int>(code.size() * 4);
|
|
1552
|
+
const int new_crossing_count =
|
|
1553
|
+
result.green_path.empty() ? 0 : static_cast<int>(result.green_path.size()) - 1;
|
|
1554
|
+
const int new_base = endpoint_count;
|
|
1555
|
+
|
|
1556
|
+
auto new_node = [&](int crossing_index, int strand) {
|
|
1557
|
+
return new_base + crossing_index * 4 + strand;
|
|
1558
|
+
};
|
|
1559
|
+
auto is_removed_node = [&](int node) {
|
|
1560
|
+
return node < endpoint_count && removed_crossings.count(node / 4) != 0;
|
|
1561
|
+
};
|
|
1562
|
+
auto is_removed_red_node = [&](int node) {
|
|
1563
|
+
if (!is_removed_node(node)) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
const int crossing = node / 4;
|
|
1567
|
+
const int strand = node % 4;
|
|
1568
|
+
const int red_strand = red_entry_by_crossing.at(crossing);
|
|
1569
|
+
return strand == red_strand || strand == positive_mod(red_strand + 2, 4);
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
std::set<int> crossed_labels;
|
|
1573
|
+
struct CrossedEdge {
|
|
1574
|
+
int interface_from = -1;
|
|
1575
|
+
int interface_to = -1;
|
|
1576
|
+
std::string level;
|
|
1577
|
+
};
|
|
1578
|
+
std::vector<CrossedEdge> crossed_edges;
|
|
1579
|
+
crossed_edges.reserve(new_crossing_count);
|
|
1580
|
+
for (int i = 0; i < new_crossing_count; ++i) {
|
|
1581
|
+
const int from_face = result.green_path[i];
|
|
1582
|
+
const int to_face = result.green_path[i + 1];
|
|
1583
|
+
const GraphEdge* edge = graph.edge(from_face, to_face);
|
|
1584
|
+
if (edge == nullptr) {
|
|
1585
|
+
throw std::invalid_argument("Simplification witness green path crosses a missing dual edge");
|
|
1586
|
+
}
|
|
1587
|
+
const int interface_from = graph.interface_for_face(*edge, from_face);
|
|
1588
|
+
const int interface_to = graph.interface_for_face(*edge, to_face);
|
|
1589
|
+
if (is_removed_red_node(interface_from) || is_removed_red_node(interface_to)) {
|
|
1590
|
+
throw std::invalid_argument("Simplification witness crosses an edge removed with the red arc");
|
|
1591
|
+
}
|
|
1592
|
+
const int label = code[interface_from / 4][interface_from % 4];
|
|
1593
|
+
if (!crossed_labels.insert(label).second) {
|
|
1594
|
+
throw std::invalid_argument("Simplification witness crosses the same PD edge more than once");
|
|
1595
|
+
}
|
|
1596
|
+
const auto level = levels.find(std::make_pair(from_face, to_face));
|
|
1597
|
+
if (level == levels.end()) {
|
|
1598
|
+
throw std::invalid_argument("Simplification witness is missing a green crossing level");
|
|
1599
|
+
}
|
|
1600
|
+
CrossedEdge crossed;
|
|
1601
|
+
crossed.interface_from = interface_from;
|
|
1602
|
+
crossed.interface_to = interface_to;
|
|
1603
|
+
crossed.level = level->second;
|
|
1604
|
+
crossed_edges.push_back(std::move(crossed));
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
std::map<int, std::vector<int>> label_endpoints;
|
|
1608
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1609
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1610
|
+
label_endpoints[code[crossing_index][strand]].push_back(crossing_index * 4 + strand);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
for (const auto& item : label_endpoints) {
|
|
1614
|
+
if (item.second.size() != 2) {
|
|
1615
|
+
std::ostringstream message;
|
|
1616
|
+
message << "PD label " << item.first << " appears " << item.second.size() << " times";
|
|
1617
|
+
throw std::invalid_argument(message.str());
|
|
1618
|
+
}
|
|
1619
|
+
if (crossed_labels.count(item.first) == 0) {
|
|
1620
|
+
dsu.unite(item.second[0], item.second[1]);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
for (const auto& item : red_entry_by_crossing) {
|
|
1625
|
+
const int crossing = item.first;
|
|
1626
|
+
const int strand = item.second;
|
|
1627
|
+
dsu.unite(crossing * 4 + positive_mod(strand + 1, 4),
|
|
1628
|
+
crossing * 4 + positive_mod(strand + 3, 4));
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
int green_anchor = endpoint_key(result.red_path.front());
|
|
1632
|
+
for (int i = 0; i < static_cast<int>(crossed_edges.size()); ++i) {
|
|
1633
|
+
const CrossedEdge& crossed = crossed_edges[i];
|
|
1634
|
+
int existing_from_pos = -1;
|
|
1635
|
+
int existing_to_pos = -1;
|
|
1636
|
+
int green_in_pos = -1;
|
|
1637
|
+
int green_out_pos = -1;
|
|
1638
|
+
if (crossed.level == "over") {
|
|
1639
|
+
existing_from_pos = 0;
|
|
1640
|
+
green_in_pos = 1;
|
|
1641
|
+
existing_to_pos = 2;
|
|
1642
|
+
green_out_pos = 3;
|
|
1643
|
+
} else if (crossed.level == "under") {
|
|
1644
|
+
green_in_pos = 0;
|
|
1645
|
+
existing_to_pos = 1;
|
|
1646
|
+
green_out_pos = 2;
|
|
1647
|
+
existing_from_pos = 3;
|
|
1648
|
+
} else {
|
|
1649
|
+
throw std::invalid_argument("Unknown green crossing strand level: " + crossed.level);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
dsu.unite(crossed.interface_from, new_node(i, existing_from_pos));
|
|
1653
|
+
dsu.unite(crossed.interface_to, new_node(i, existing_to_pos));
|
|
1654
|
+
dsu.unite(green_anchor, new_node(i, green_in_pos));
|
|
1655
|
+
green_anchor = new_node(i, green_out_pos);
|
|
1656
|
+
}
|
|
1657
|
+
dsu.unite(green_anchor, endpoint_key(result.red_path.back()));
|
|
1658
|
+
|
|
1659
|
+
std::vector<int> active_nodes;
|
|
1660
|
+
active_nodes.reserve(endpoint_count + new_crossing_count * 4);
|
|
1661
|
+
for (int node = 0; node < endpoint_count; ++node) {
|
|
1662
|
+
if (!is_removed_node(node)) {
|
|
1663
|
+
active_nodes.push_back(node);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
for (int crossing = 0; crossing < new_crossing_count; ++crossing) {
|
|
1667
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1668
|
+
active_nodes.push_back(new_node(crossing, strand));
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
std::map<int, std::vector<int>> grouped;
|
|
1673
|
+
for (int node : active_nodes) {
|
|
1674
|
+
grouped[dsu.find(node)].push_back(node);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
std::vector<std::vector<int>> groups;
|
|
1678
|
+
for (auto& item : grouped) {
|
|
1679
|
+
std::sort(item.second.begin(), item.second.end());
|
|
1680
|
+
groups.push_back(item.second);
|
|
1681
|
+
}
|
|
1682
|
+
std::sort(groups.begin(), groups.end(), [](const std::vector<int>& lhs, const std::vector<int>& rhs) {
|
|
1683
|
+
return lhs.front() < rhs.front();
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
std::map<int, int> label_by_node;
|
|
1687
|
+
int next_label = 0;
|
|
1688
|
+
for (const std::vector<int>& nodes : groups) {
|
|
1689
|
+
if (nodes.size() != 2) {
|
|
1690
|
+
std::ostringstream message;
|
|
1691
|
+
message << "Applied simplification produced a non-PD edge with "
|
|
1692
|
+
<< nodes.size() << " active endpoints";
|
|
1693
|
+
throw std::runtime_error(message.str());
|
|
1694
|
+
}
|
|
1695
|
+
for (int node : nodes) {
|
|
1696
|
+
label_by_node[node] = next_label;
|
|
1697
|
+
}
|
|
1698
|
+
++next_label;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
PDCode output;
|
|
1702
|
+
output.reserve(code.size() - removed_crossings.size() + static_cast<std::size_t>(new_crossing_count));
|
|
1703
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1704
|
+
if (removed_crossings.count(crossing_index) != 0) {
|
|
1705
|
+
continue;
|
|
1706
|
+
}
|
|
1707
|
+
Crossing crossing{};
|
|
1708
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1709
|
+
crossing[strand] = label_by_node.at(crossing_index * 4 + strand);
|
|
1710
|
+
}
|
|
1711
|
+
output.push_back(crossing);
|
|
1712
|
+
}
|
|
1713
|
+
for (int crossing_index = 0; crossing_index < new_crossing_count; ++crossing_index) {
|
|
1714
|
+
Crossing crossing{};
|
|
1715
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1716
|
+
crossing[strand] = label_by_node.at(new_node(crossing_index, strand));
|
|
1717
|
+
}
|
|
1718
|
+
output.push_back(crossing);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
const std::size_t total_components =
|
|
1722
|
+
analyze_components(code, known_crossingless_components).total_components();
|
|
1723
|
+
output = renumber_full_dfs(output);
|
|
1724
|
+
const std::size_t crossing_components = analyze_components(output).components_with_crossings();
|
|
1725
|
+
|
|
1726
|
+
MidSimplificationApplyResult applied;
|
|
1727
|
+
applied.code = std::move(output);
|
|
1728
|
+
applied.crossingless_components =
|
|
1729
|
+
total_components > crossing_components ? total_components - crossing_components : 0;
|
|
1730
|
+
return applied;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
namespace {
|
|
1734
|
+
|
|
1735
|
+
bool witness_has_applicable_surgery(const PDCode& code, const SimplificationResult& result) {
|
|
1736
|
+
try {
|
|
1737
|
+
(void)apply_simplification_witness(code, result, 0);
|
|
1738
|
+
return true;
|
|
1739
|
+
} catch (const std::exception&) {
|
|
1740
|
+
return false;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
void emit_progress(const SimplifierOptions& options, const std::string& message) {
|
|
1745
|
+
if (!options.verbose) {
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
if (options.progress) {
|
|
1749
|
+
options.progress(message);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
std::string search_mode_for_options(const SimplifierOptions& options) {
|
|
1754
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
1755
|
+
return "heuristic";
|
|
1756
|
+
}
|
|
1757
|
+
if (options.max_paths == -1) {
|
|
1758
|
+
return "bruteforce";
|
|
1759
|
+
}
|
|
1760
|
+
return "bounded";
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1246
1763
|
} // namespace
|
|
1247
1764
|
|
|
1248
1765
|
PDCode parse_pd_code(const std::string& text) {
|
|
@@ -1283,13 +1800,13 @@ PDCode parse_pd_code(const std::string& text) {
|
|
|
1283
1800
|
|
|
1284
1801
|
std::string format_pd_code(const PDCode& code) {
|
|
1285
1802
|
std::ostringstream out;
|
|
1286
|
-
out <<
|
|
1803
|
+
out << "PD[";
|
|
1287
1804
|
for (std::size_t i = 0; i < code.size(); ++i) {
|
|
1288
1805
|
if (i != 0) {
|
|
1289
|
-
out <<
|
|
1806
|
+
out << ',';
|
|
1290
1807
|
}
|
|
1291
|
-
out <<
|
|
1292
|
-
<< code[i][2] <<
|
|
1808
|
+
out << "X[" << code[i][0] << ',' << code[i][1] << ','
|
|
1809
|
+
<< code[i][2] << ',' << code[i][3] << ']';
|
|
1293
1810
|
}
|
|
1294
1811
|
out << ']';
|
|
1295
1812
|
return out.str();
|
|
@@ -1627,6 +2144,13 @@ SimplificationResult find_simplification(
|
|
|
1627
2144
|
const PDCode& code,
|
|
1628
2145
|
const SimplifierOptions& options) {
|
|
1629
2146
|
SimplificationResult result;
|
|
2147
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2148
|
+
result.path_search_mode = "heuristic";
|
|
2149
|
+
} else if (options.max_paths == -1) {
|
|
2150
|
+
result.path_search_mode = "bruteforce";
|
|
2151
|
+
} else {
|
|
2152
|
+
result.path_search_mode = "bounded";
|
|
2153
|
+
}
|
|
1630
2154
|
Diagram diagram(code);
|
|
1631
2155
|
DualGraph graph(diagram);
|
|
1632
2156
|
const auto red_lines = possible_red_lines(diagram);
|
|
@@ -1657,7 +2181,12 @@ SimplificationResult find_simplification(
|
|
|
1657
2181
|
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
1658
2182
|
for (int source : sources) {
|
|
1659
2183
|
for (int destination : destinations) {
|
|
1660
|
-
|
|
2184
|
+
std::vector<std::vector<int>> found_paths;
|
|
2185
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2186
|
+
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2187
|
+
} else {
|
|
2188
|
+
found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
|
|
2189
|
+
}
|
|
1661
2190
|
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
1662
2191
|
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
1663
2192
|
break;
|
|
@@ -1671,10 +2200,16 @@ SimplificationResult find_simplification(
|
|
|
1671
2200
|
continue;
|
|
1672
2201
|
}
|
|
1673
2202
|
if (do_check(diagram, graph, red_path, green_path, Direction::Left, result)) {
|
|
1674
|
-
|
|
2203
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, result)) {
|
|
2204
|
+
return result;
|
|
2205
|
+
}
|
|
2206
|
+
clear_witness(result);
|
|
1675
2207
|
}
|
|
1676
2208
|
if (do_check(diagram, graph, red_path, green_path, Direction::Right, result)) {
|
|
1677
|
-
|
|
2209
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, result)) {
|
|
2210
|
+
return result;
|
|
2211
|
+
}
|
|
2212
|
+
clear_witness(result);
|
|
1678
2213
|
}
|
|
1679
2214
|
}
|
|
1680
2215
|
}
|
|
@@ -1682,6 +2217,140 @@ SimplificationResult find_simplification(
|
|
|
1682
2217
|
return result;
|
|
1683
2218
|
}
|
|
1684
2219
|
|
|
2220
|
+
ReductionResult reduce_pd_code(
|
|
2221
|
+
const PDCode& code,
|
|
2222
|
+
std::size_t known_crossingless_components,
|
|
2223
|
+
const SimplifierOptions& options,
|
|
2224
|
+
int reduction_round) {
|
|
2225
|
+
{
|
|
2226
|
+
std::ostringstream message;
|
|
2227
|
+
message << "start input_crossings=" << code.size()
|
|
2228
|
+
<< " known_crossingless_components=" << known_crossingless_components
|
|
2229
|
+
<< " reduction_round=" << reduction_round
|
|
2230
|
+
<< " max_paths=" << options.max_paths
|
|
2231
|
+
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2232
|
+
emit_progress(options, message.str());
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
|
|
2236
|
+
ReductionResult output;
|
|
2237
|
+
output.code = prepared.code;
|
|
2238
|
+
output.crossingless_components = prepared.crossingless_components;
|
|
2239
|
+
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2240
|
+
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2241
|
+
{
|
|
2242
|
+
std::ostringstream message;
|
|
2243
|
+
message << "pre_simplify input_crossings=" << code.size()
|
|
2244
|
+
<< " output_crossings=" << output.code.size()
|
|
2245
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2246
|
+
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2247
|
+
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2248
|
+
emit_progress(options, message.str());
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
|
|
2252
|
+
SimplifierOptions search_options = options;
|
|
2253
|
+
search_options.require_applicable = true;
|
|
2254
|
+
const int round = output.mid_simplification_rounds + 1;
|
|
2255
|
+
{
|
|
2256
|
+
std::ostringstream message;
|
|
2257
|
+
message << "round " << round
|
|
2258
|
+
<< " search_start crossings=" << output.code.size()
|
|
2259
|
+
<< " mode=" << search_mode_for_options(search_options);
|
|
2260
|
+
emit_progress(options, message.str());
|
|
2261
|
+
}
|
|
2262
|
+
SimplificationResult search = find_simplification(output.code, search_options);
|
|
2263
|
+
output.tested_red_paths += search.tested_red_paths;
|
|
2264
|
+
output.tested_green_paths += search.tested_green_paths;
|
|
2265
|
+
output.last_path_search_mode = search.path_search_mode;
|
|
2266
|
+
{
|
|
2267
|
+
std::ostringstream message;
|
|
2268
|
+
message << "round " << round
|
|
2269
|
+
<< " search_done found=" << (search.found ? "yes" : "no")
|
|
2270
|
+
<< " mode=" << search.path_search_mode
|
|
2271
|
+
<< " tested_red=" << search.tested_red_paths
|
|
2272
|
+
<< " tested_green=" << search.tested_green_paths;
|
|
2273
|
+
emit_progress(options, message.str());
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
if (!search.found && reduction_round < 0 &&
|
|
2277
|
+
options.max_paths == -1 && !options.ban_heuristic) {
|
|
2278
|
+
SimplifierOptions brute_options = options;
|
|
2279
|
+
brute_options.max_paths = -1;
|
|
2280
|
+
brute_options.ban_heuristic = true;
|
|
2281
|
+
brute_options.require_applicable = true;
|
|
2282
|
+
{
|
|
2283
|
+
std::ostringstream message;
|
|
2284
|
+
message << "round " << round
|
|
2285
|
+
<< " brute_fallback_start crossings=" << output.code.size();
|
|
2286
|
+
emit_progress(options, message.str());
|
|
2287
|
+
}
|
|
2288
|
+
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
2289
|
+
output.tested_red_paths += brute.tested_red_paths;
|
|
2290
|
+
output.tested_green_paths += brute.tested_green_paths;
|
|
2291
|
+
output.last_path_search_mode = brute.path_search_mode;
|
|
2292
|
+
{
|
|
2293
|
+
std::ostringstream message;
|
|
2294
|
+
message << "round " << round
|
|
2295
|
+
<< " brute_fallback_done found=" << (brute.found ? "yes" : "no")
|
|
2296
|
+
<< " tested_red=" << brute.tested_red_paths
|
|
2297
|
+
<< " tested_green=" << brute.tested_green_paths;
|
|
2298
|
+
emit_progress(options, message.str());
|
|
2299
|
+
}
|
|
2300
|
+
if (brute.found) {
|
|
2301
|
+
++output.heuristic_failover_rounds;
|
|
2302
|
+
search = std::move(brute);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
if (!search.found) {
|
|
2307
|
+
{
|
|
2308
|
+
std::ostringstream message;
|
|
2309
|
+
message << "round " << round
|
|
2310
|
+
<< " stop_no_path crossings=" << output.code.size();
|
|
2311
|
+
emit_progress(options, message.str());
|
|
2312
|
+
}
|
|
2313
|
+
break;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
const std::size_t before_apply_crossings = output.code.size();
|
|
2317
|
+
const MidSimplificationApplyResult applied =
|
|
2318
|
+
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2319
|
+
++output.mid_simplification_rounds;
|
|
2320
|
+
const PDSimplificationResult simplified =
|
|
2321
|
+
simplify_pd_code(applied.code, applied.crossingless_components);
|
|
2322
|
+
output.code = simplified.code;
|
|
2323
|
+
output.crossingless_components = simplified.crossingless_components;
|
|
2324
|
+
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2325
|
+
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2326
|
+
{
|
|
2327
|
+
std::ostringstream message;
|
|
2328
|
+
message << "round " << round
|
|
2329
|
+
<< " applied crossings=" << before_apply_crossings
|
|
2330
|
+
<< " -> " << applied.code.size()
|
|
2331
|
+
<< " -> " << output.code.size()
|
|
2332
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2333
|
+
<< " r1_moves=" << simplified.reidemeister_i_moves
|
|
2334
|
+
<< " nugatory_moves=" << simplified.nugatory_crossing_moves;
|
|
2335
|
+
emit_progress(options, message.str());
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
output.stopped_by_round_limit =
|
|
2340
|
+
reduction_round >= 0 && output.mid_simplification_rounds >= reduction_round;
|
|
2341
|
+
{
|
|
2342
|
+
std::ostringstream message;
|
|
2343
|
+
message << "done final_crossings=" << output.code.size()
|
|
2344
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2345
|
+
<< " mid_rounds=" << output.mid_simplification_rounds
|
|
2346
|
+
<< " heuristic_failover_rounds=" << output.heuristic_failover_rounds
|
|
2347
|
+
<< " stopped_by_round_limit="
|
|
2348
|
+
<< (output.stopped_by_round_limit ? "yes" : "no");
|
|
2349
|
+
emit_progress(options, message.str());
|
|
2350
|
+
}
|
|
2351
|
+
return output;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
1685
2354
|
std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint) {
|
|
1686
2355
|
out << format_endpoint(endpoint);
|
|
1687
2356
|
return out;
|
|
@@ -87,6 +87,72 @@ def normalize_pd_codes(pd_codes: PdManyInput) -> list[str]:
|
|
|
87
87
|
return [normalize_pd_code(pd_code) for pd_code in pd_codes]
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def _label_for_block(text: str, block_start: int, label_prefix: str, index: int) -> str:
|
|
91
|
+
line_start = text.rfind("\n", 0, block_start)
|
|
92
|
+
line_start = 0 if line_start == -1 else line_start + 1
|
|
93
|
+
before_block = text[line_start:block_start]
|
|
94
|
+
if ":" in before_block:
|
|
95
|
+
line_label = before_block.split(":", 1)[0].strip()
|
|
96
|
+
if line_label:
|
|
97
|
+
return f"{label_prefix}:{line_label}"
|
|
98
|
+
if index == 0:
|
|
99
|
+
return label_prefix
|
|
100
|
+
return f"{label_prefix}#{index + 1}"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _pd_file_jobs(path: str) -> list[tuple[str, str]]:
|
|
104
|
+
text = pathlib.Path(path).read_text(encoding="utf-8")
|
|
105
|
+
jobs: list[tuple[str, str]] = []
|
|
106
|
+
position = 0
|
|
107
|
+
index = 0
|
|
108
|
+
|
|
109
|
+
while True:
|
|
110
|
+
start = text.find("PD[", position)
|
|
111
|
+
if start == -1:
|
|
112
|
+
break
|
|
113
|
+
depth = 0
|
|
114
|
+
end = -1
|
|
115
|
+
for cursor in range(start + 2, len(text)):
|
|
116
|
+
if text[cursor] == "[":
|
|
117
|
+
depth += 1
|
|
118
|
+
elif text[cursor] == "]":
|
|
119
|
+
depth -= 1
|
|
120
|
+
if depth == 0:
|
|
121
|
+
end = cursor
|
|
122
|
+
break
|
|
123
|
+
if end == -1:
|
|
124
|
+
jobs.append((f"{path}#{index + 1}", text[start:].strip()))
|
|
125
|
+
break
|
|
126
|
+
jobs.append((
|
|
127
|
+
_label_for_block(text, start, path, index),
|
|
128
|
+
text[start : end + 1],
|
|
129
|
+
))
|
|
130
|
+
index += 1
|
|
131
|
+
position = end + 1
|
|
132
|
+
|
|
133
|
+
if jobs:
|
|
134
|
+
if len(jobs) == 1:
|
|
135
|
+
jobs[0] = (path, jobs[0][1])
|
|
136
|
+
return jobs
|
|
137
|
+
|
|
138
|
+
for line in text.splitlines():
|
|
139
|
+
cleaned = line.strip()
|
|
140
|
+
if not cleaned or cleaned.startswith("#"):
|
|
141
|
+
continue
|
|
142
|
+
label = path
|
|
143
|
+
payload = cleaned
|
|
144
|
+
if ":" in cleaned:
|
|
145
|
+
line_label, payload = cleaned.split(":", 1)
|
|
146
|
+
line_label = line_label.strip()
|
|
147
|
+
payload = payload.strip()
|
|
148
|
+
if line_label:
|
|
149
|
+
label = f"{path}:{line_label}"
|
|
150
|
+
elif jobs:
|
|
151
|
+
label = f"{path}#{len(jobs) + 1}"
|
|
152
|
+
jobs.append((label, payload))
|
|
153
|
+
return jobs
|
|
154
|
+
|
|
155
|
+
|
|
90
156
|
@contextlib.contextmanager
|
|
91
157
|
def _resource_paths():
|
|
92
158
|
package = "cpp_pd_code_simplify_interface"
|
|
@@ -335,6 +401,9 @@ def _load_library() -> ctypes.CDLL:
|
|
|
335
401
|
library.pdcode_simplify_run_json.argtypes = [
|
|
336
402
|
ctypes.c_char_p,
|
|
337
403
|
ctypes.c_int,
|
|
404
|
+
ctypes.c_int,
|
|
405
|
+
ctypes.c_int,
|
|
406
|
+
ctypes.c_int,
|
|
338
407
|
ctypes.c_ulonglong,
|
|
339
408
|
ctypes.POINTER(ctypes.c_int),
|
|
340
409
|
ctypes.c_ulonglong,
|
|
@@ -350,10 +419,15 @@ def _load_library() -> ctypes.CDLL:
|
|
|
350
419
|
def _run_one(
|
|
351
420
|
pd_text: str,
|
|
352
421
|
*,
|
|
353
|
-
max_paths: int =
|
|
422
|
+
max_paths: int = -1,
|
|
423
|
+
ban_heuristic: bool = False,
|
|
424
|
+
reduction_round: int = -1,
|
|
425
|
+
verbose: bool = False,
|
|
354
426
|
known_crossingless_components: int = 0,
|
|
355
427
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
356
428
|
) -> dict[str, Any]:
|
|
429
|
+
if reduction_round < -1:
|
|
430
|
+
raise ValueError("reduction_round must be -1 or a non-negative integer")
|
|
357
431
|
library = _load_library()
|
|
358
432
|
removed_count = 0 if remove_crossings is None else len(remove_crossings)
|
|
359
433
|
removed_array = None
|
|
@@ -363,6 +437,9 @@ def _run_one(
|
|
|
363
437
|
pointer = library.pdcode_simplify_run_json(
|
|
364
438
|
pd_text.encode("utf-8"),
|
|
365
439
|
int(max_paths),
|
|
440
|
+
1 if ban_heuristic else 0,
|
|
441
|
+
int(reduction_round),
|
|
442
|
+
1 if verbose else 0,
|
|
366
443
|
int(known_crossingless_components),
|
|
367
444
|
removed_array,
|
|
368
445
|
int(removed_count),
|
|
@@ -386,7 +463,10 @@ def _run_one(
|
|
|
386
463
|
def simplify(
|
|
387
464
|
pd_code: PdInput,
|
|
388
465
|
*,
|
|
389
|
-
max_paths: int =
|
|
466
|
+
max_paths: int = -1,
|
|
467
|
+
ban_heuristic: bool = False,
|
|
468
|
+
reduction_round: int = -1,
|
|
469
|
+
verbose: bool = False,
|
|
390
470
|
known_crossingless_components: int = 0,
|
|
391
471
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
392
472
|
) -> dict[str, Any]:
|
|
@@ -395,6 +475,9 @@ def simplify(
|
|
|
395
475
|
return _run_one(
|
|
396
476
|
normalize_pd_code(pd_code),
|
|
397
477
|
max_paths=max_paths,
|
|
478
|
+
ban_heuristic=ban_heuristic,
|
|
479
|
+
reduction_round=reduction_round,
|
|
480
|
+
verbose=verbose,
|
|
398
481
|
known_crossingless_components=known_crossingless_components,
|
|
399
482
|
remove_crossings=remove_crossings,
|
|
400
483
|
)
|
|
@@ -403,7 +486,10 @@ def simplify(
|
|
|
403
486
|
def simplify_many(
|
|
404
487
|
pd_codes: PdManyInput,
|
|
405
488
|
*,
|
|
406
|
-
max_paths: int =
|
|
489
|
+
max_paths: int = -1,
|
|
490
|
+
ban_heuristic: bool = False,
|
|
491
|
+
reduction_round: int = -1,
|
|
492
|
+
verbose: bool = False,
|
|
407
493
|
known_crossingless_components: int = 0,
|
|
408
494
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
409
495
|
) -> list[dict[str, Any]]:
|
|
@@ -413,6 +499,9 @@ def simplify_many(
|
|
|
413
499
|
_run_one(
|
|
414
500
|
pd_text,
|
|
415
501
|
max_paths=max_paths,
|
|
502
|
+
ban_heuristic=ban_heuristic,
|
|
503
|
+
reduction_round=reduction_round,
|
|
504
|
+
verbose=verbose,
|
|
416
505
|
known_crossingless_components=known_crossingless_components,
|
|
417
506
|
remove_crossings=remove_crossings,
|
|
418
507
|
)
|
|
@@ -423,47 +512,61 @@ def simplify_many(
|
|
|
423
512
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
424
513
|
parser = argparse.ArgumentParser(description="Run cpp-pd-code-simplify through the Python interface.")
|
|
425
514
|
parser.add_argument("pd_code", nargs="?", help="PD code as PD[...] text or a Python-style list of crossings.")
|
|
515
|
+
parser.add_argument("--pd-code", "-c", dest="pd_code_option", help="literal PD[...] string")
|
|
426
516
|
parser.add_argument("--pd-file", "-f", help="read one file containing one or more labelled PD-code lines")
|
|
427
|
-
parser.add_argument("--max-paths", type=int, default
|
|
517
|
+
parser.add_argument("--max-paths", type=int, default=-1)
|
|
518
|
+
parser.add_argument("--ban-heuristic", action="store_true")
|
|
519
|
+
parser.add_argument("--reduction-round", type=int, default=-1)
|
|
520
|
+
parser.add_argument("--verbose", action="store_true")
|
|
428
521
|
parser.add_argument("--known-crossingless-components", type=int, default=0)
|
|
429
522
|
parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
|
|
430
523
|
args = parser.parse_args(argv)
|
|
431
|
-
if args.
|
|
432
|
-
parser.error("
|
|
433
|
-
if
|
|
434
|
-
parser.error("a positional PD code or --pd-
|
|
524
|
+
if args.reduction_round < -1:
|
|
525
|
+
parser.error("--reduction-round must be -1 or a non-negative integer")
|
|
526
|
+
if args.pd_code and args.pd_code_option:
|
|
527
|
+
parser.error("pass either a positional PD code or --pd-code, not both")
|
|
528
|
+
pd_code_text = args.pd_code_option or args.pd_code
|
|
529
|
+
if args.pd_file and pd_code_text:
|
|
530
|
+
parser.error("pass either a PD code or --pd-file, not both")
|
|
531
|
+
if not args.pd_file and not pd_code_text:
|
|
532
|
+
parser.error("a PD code or --pd-file is required")
|
|
435
533
|
remove_crossings = None
|
|
436
534
|
if args.remove_crossings:
|
|
437
535
|
remove_crossings = [int(token) for token in re.findall(r"-?\d+", args.remove_crossings)]
|
|
438
536
|
|
|
439
537
|
exit_code = 0
|
|
440
538
|
if args.pd_file:
|
|
441
|
-
|
|
442
|
-
for line in pathlib.Path(args.pd_file).read_text(encoding="utf-8").splitlines():
|
|
443
|
-
cleaned = line.strip()
|
|
444
|
-
if not cleaned or cleaned.startswith("#"):
|
|
445
|
-
continue
|
|
446
|
-
payload = cleaned.split(":", 1)[1].strip() if ":" in cleaned else cleaned
|
|
447
|
-
lines.append(payload)
|
|
539
|
+
jobs = _pd_file_jobs(args.pd_file)
|
|
448
540
|
batch_payload = []
|
|
449
|
-
|
|
541
|
+
show_labels = len(jobs) > 1
|
|
542
|
+
for label, line in jobs:
|
|
450
543
|
try:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
544
|
+
item = simplify(
|
|
545
|
+
line,
|
|
546
|
+
max_paths=args.max_paths,
|
|
547
|
+
ban_heuristic=args.ban_heuristic,
|
|
548
|
+
reduction_round=args.reduction_round,
|
|
549
|
+
verbose=args.verbose,
|
|
550
|
+
known_crossingless_components=args.known_crossingless_components,
|
|
551
|
+
remove_crossings=remove_crossings,
|
|
458
552
|
)
|
|
553
|
+
if show_labels:
|
|
554
|
+
item = {"label": label, **item}
|
|
555
|
+
batch_payload.append(item)
|
|
459
556
|
except Exception as exc:
|
|
460
557
|
exit_code = 2
|
|
461
|
-
|
|
558
|
+
item = {"error": str(exc)}
|
|
559
|
+
if show_labels:
|
|
560
|
+
item = {"label": label, **item}
|
|
561
|
+
batch_payload.append(item)
|
|
462
562
|
payload: Any = batch_payload
|
|
463
563
|
else:
|
|
464
564
|
payload = simplify(
|
|
465
|
-
|
|
565
|
+
pd_code_text or "",
|
|
466
566
|
max_paths=args.max_paths,
|
|
567
|
+
ban_heuristic=args.ban_heuristic,
|
|
568
|
+
reduction_round=args.reduction_round,
|
|
569
|
+
verbose=args.verbose,
|
|
467
570
|
known_crossingless_components=args.known_crossingless_components,
|
|
468
571
|
remove_crossings=remove_crossings,
|
|
469
572
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cpp-pd-code-simplify-interface
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Author: GGN_2015
|
|
@@ -35,7 +35,7 @@ use, the package compiles a cached local dynamic library through
|
|
|
35
35
|
compiler compatible with `g++` must be available at runtime.
|
|
36
36
|
|
|
37
37
|
Calls use the C++ library's default preprocessing pipeline: R1-move removal
|
|
38
|
-
followed by nugatory-crossing removal.
|
|
38
|
+
followed by nugatory-crossing removal, then iterative mid-simplification.
|
|
39
39
|
|
|
40
40
|
## Example
|
|
41
41
|
|
|
@@ -44,9 +44,15 @@ import cpp_pd_code_simplify_interface as simplify
|
|
|
44
44
|
|
|
45
45
|
pd_code = "PD[X[1,5,2,4],X[3,1,4,6],X[5,3,6,2]]"
|
|
46
46
|
result = simplify.simplify(pd_code)
|
|
47
|
-
print(result["
|
|
47
|
+
print(result["final_pd_code"])
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
51
|
+
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
52
|
+
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
53
|
+
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
54
|
+
`verbose=True` to forward C++ progress logs to stderr.
|
|
55
|
+
|
|
50
56
|
Batch use:
|
|
51
57
|
|
|
52
58
|
```python
|
|
@@ -72,7 +78,7 @@ Python process needs a 64-bit compiler target.
|
|
|
72
78
|
Command-line use also supports multi-line PD-code files:
|
|
73
79
|
|
|
74
80
|
```sh
|
|
75
|
-
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths
|
|
81
|
+
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1 --verbose
|
|
76
82
|
```
|
|
77
83
|
|
|
78
84
|
## Build And Publish
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
cpp_pd_code_simplify_interface/__init__.py,sha256=aaNwD--zgQX13xiU1hpuvgXy8vVTFGRk-r5zLm8zIRY,447
|
|
2
2
|
cpp_pd_code_simplify_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
|
|
3
|
-
cpp_pd_code_simplify_interface/main.py,sha256=
|
|
3
|
+
cpp_pd_code_simplify_interface/main.py,sha256=rKI0anvKvOlD4Z81P9IzYD4QyBQOsL_RiTMJ6ICavkQ,20045
|
|
4
4
|
cpp_pd_code_simplify_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
5
|
-
cpp_pd_code_simplify_interface-0.1.
|
|
6
|
-
cpp_pd_code_simplify_interface-0.1.
|
|
7
|
-
cpp_pd_code_simplify_interface-0.1.
|
|
8
|
-
cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=
|
|
9
|
-
cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=
|
|
10
|
-
cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=
|
|
11
|
-
cpp_pd_code_simplify_interface-0.1.
|
|
5
|
+
cpp_pd_code_simplify_interface-0.1.3.dist-info/entry_points.txt,sha256=ThorC4Enxp317TKITsXm9pE0gUp7pt-pP6Dl5mEPUR8,91
|
|
6
|
+
cpp_pd_code_simplify_interface-0.1.3.dist-info/METADATA,sha256=Z-MzZTaFdaI7xDmLWlTv17CYYLVqHtmPo79V3U-71qA,2905
|
|
7
|
+
cpp_pd_code_simplify_interface-0.1.3.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
8
|
+
cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=7j5K9xrycVKXqDCCmeIq2nO9q22MUQSSsmOvfG481Ks,5136
|
|
9
|
+
cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=zVBjjT5RYxBuBtKCCrfzLmyLcsnr6l2z8P5p_rBAj5U,6015
|
|
10
|
+
cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=s3VJNUD_9kAlZpxBDbtObERHEIMZGiKhgmVOIANIjAo,83558
|
|
11
|
+
cpp_pd_code_simplify_interface-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|