cpp-pd-code-simplify-interface 0.1.0__tar.gz → 0.1.2__tar.gz
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-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/PKG-INFO +6 -2
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/README.md +5 -1
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +3 -1
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/data/src/native_interface.cpp +4 -1
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +216 -1
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/main.py +98 -21
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/pyproject.toml +1 -1
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/build_backend/cpp_pd_code_simplify_interface_build_backend.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/__init__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/__main__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.0 → cpp_pd_code_simplify_interface-0.1.2}/cpp_pd_code_simplify_interface/py.typed +0 -0
|
@@ -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.2
|
|
4
4
|
Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Author: GGN_2015
|
|
@@ -47,6 +47,10 @@ result = simplify.simplify(pd_code)
|
|
|
47
47
|
print(result["simplification_found"])
|
|
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.
|
|
53
|
+
|
|
50
54
|
Batch use:
|
|
51
55
|
|
|
52
56
|
```python
|
|
@@ -72,7 +76,7 @@ Python process needs a 64-bit compiler target.
|
|
|
72
76
|
Command-line use also supports multi-line PD-code files:
|
|
73
77
|
|
|
74
78
|
```sh
|
|
75
|
-
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths
|
|
79
|
+
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
## Build And Publish
|
|
@@ -27,6 +27,10 @@ result = simplify.simplify(pd_code)
|
|
|
27
27
|
print(result["simplification_found"])
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
31
|
+
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
32
|
+
enumeration for a manageable input.
|
|
33
|
+
|
|
30
34
|
Batch use:
|
|
31
35
|
|
|
32
36
|
```python
|
|
@@ -52,7 +56,7 @@ Python process needs a 64-bit compiler target.
|
|
|
52
56
|
Command-line use also supports multi-line PD-code files:
|
|
53
57
|
|
|
54
58
|
```sh
|
|
55
|
-
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths
|
|
59
|
+
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
|
|
56
60
|
```
|
|
57
61
|
|
|
58
62
|
## Build And Publish
|
|
@@ -48,7 +48,8 @@ struct GreenCrossing {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
struct SimplifierOptions {
|
|
51
|
-
int max_paths =
|
|
51
|
+
int max_paths = -1;
|
|
52
|
+
bool ban_heuristic = false;
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
struct LinkComponentSummary {
|
|
@@ -98,6 +99,7 @@ struct PDSimplificationResult {
|
|
|
98
99
|
struct SimplificationResult {
|
|
99
100
|
bool found = false;
|
|
100
101
|
Direction direction = Direction::Left;
|
|
102
|
+
std::string path_search_mode;
|
|
101
103
|
std::vector<Endpoint> red_path;
|
|
102
104
|
std::vector<int> green_path;
|
|
103
105
|
std::vector<GreenCrossing> green_crossings;
|
|
@@ -81,7 +81,8 @@ std::string result_to_json(
|
|
|
81
81
|
append_component_counts(out, search_components);
|
|
82
82
|
out << "},";
|
|
83
83
|
out << "\"tested_red_paths\":" << result.tested_red_paths << ",";
|
|
84
|
-
out << "\"tested_green_paths\":" << result.tested_green_paths;
|
|
84
|
+
out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
|
|
85
|
+
out << "\"path_search_mode\":\"" << json_escape(result.path_search_mode) << "\"";
|
|
85
86
|
if (result.found) {
|
|
86
87
|
out << ",";
|
|
87
88
|
out << "\"direction\":\"" << pdcode_simplify::format_direction(result.direction) << "\",";
|
|
@@ -137,6 +138,7 @@ __declspec(dllexport)
|
|
|
137
138
|
char* pdcode_simplify_run_json(
|
|
138
139
|
const char* pd_text,
|
|
139
140
|
int max_paths,
|
|
141
|
+
int ban_heuristic,
|
|
140
142
|
unsigned long long known_crossingless_components,
|
|
141
143
|
const int* removed_crossings,
|
|
142
144
|
unsigned long long removed_crossing_count) {
|
|
@@ -148,6 +150,7 @@ char* pdcode_simplify_run_json(
|
|
|
148
150
|
const std::string text(pd_text);
|
|
149
151
|
pdcode_simplify::SimplifierOptions options;
|
|
150
152
|
options.max_paths = max_paths;
|
|
153
|
+
options.ban_heuristic = ban_heuristic != 0;
|
|
151
154
|
|
|
152
155
|
const pdcode_simplify::PDCode code = pdcode_simplify::parse_pd_code(text);
|
|
153
156
|
std::size_t crossingless = static_cast<std::size_t>(known_crossingless_components);
|
|
@@ -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;
|
|
@@ -1076,6 +1082,203 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1076
1082
|
return paths;
|
|
1077
1083
|
}
|
|
1078
1084
|
|
|
1085
|
+
std::vector<int> heuristic_distances_to_target(
|
|
1086
|
+
const DualGraph& graph,
|
|
1087
|
+
int target,
|
|
1088
|
+
int cutoff) {
|
|
1089
|
+
const int face_count = static_cast<int>(graph.faces.size());
|
|
1090
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1091
|
+
std::vector<int> distance(face_count, infinity);
|
|
1092
|
+
std::deque<int> queue;
|
|
1093
|
+
distance[target] = 0;
|
|
1094
|
+
queue.push_back(target);
|
|
1095
|
+
|
|
1096
|
+
while (!queue.empty()) {
|
|
1097
|
+
const int current = queue.front();
|
|
1098
|
+
queue.pop_front();
|
|
1099
|
+
for (int edge_index : graph.adjacency[current]) {
|
|
1100
|
+
const GraphEdge& edge = graph.edges[edge_index];
|
|
1101
|
+
if (edge.weight >= cutoff) {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
const int next = edge.u == current ? edge.v : edge.u;
|
|
1105
|
+
if (distance[next] != infinity) {
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
distance[next] = distance[current] + 1;
|
|
1109
|
+
queue.push_back(next);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return distance;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
struct HeuristicState {
|
|
1116
|
+
std::vector<int> path;
|
|
1117
|
+
std::vector<char> visited;
|
|
1118
|
+
int weight = 0;
|
|
1119
|
+
int branch_penalty = 0;
|
|
1120
|
+
int estimated_weight = 0;
|
|
1121
|
+
int estimated_length = 0;
|
|
1122
|
+
int serial = 0;
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
struct HeuristicStateWorse {
|
|
1126
|
+
bool operator()(const HeuristicState& lhs, const HeuristicState& rhs) const {
|
|
1127
|
+
if (lhs.estimated_weight != rhs.estimated_weight) {
|
|
1128
|
+
return lhs.estimated_weight > rhs.estimated_weight;
|
|
1129
|
+
}
|
|
1130
|
+
if (lhs.estimated_length != rhs.estimated_length) {
|
|
1131
|
+
return lhs.estimated_length > rhs.estimated_length;
|
|
1132
|
+
}
|
|
1133
|
+
if (lhs.branch_penalty != rhs.branch_penalty) {
|
|
1134
|
+
return lhs.branch_penalty > rhs.branch_penalty;
|
|
1135
|
+
}
|
|
1136
|
+
if (lhs.weight != rhs.weight) {
|
|
1137
|
+
return lhs.weight > rhs.weight;
|
|
1138
|
+
}
|
|
1139
|
+
if (lhs.path.size() != rhs.path.size()) {
|
|
1140
|
+
return lhs.path.size() > rhs.path.size();
|
|
1141
|
+
}
|
|
1142
|
+
return lhs.serial > rhs.serial;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
struct HeuristicStep {
|
|
1147
|
+
int next = -1;
|
|
1148
|
+
int edge_index = -1;
|
|
1149
|
+
int edge_weight = 0;
|
|
1150
|
+
int distance = 0;
|
|
1151
|
+
int degree_penalty = 0;
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
bool heuristic_step_less(const HeuristicStep& lhs, const HeuristicStep& rhs) {
|
|
1155
|
+
if (lhs.edge_weight != rhs.edge_weight) {
|
|
1156
|
+
return lhs.edge_weight < rhs.edge_weight;
|
|
1157
|
+
}
|
|
1158
|
+
if (lhs.distance != rhs.distance) {
|
|
1159
|
+
return lhs.distance < rhs.distance;
|
|
1160
|
+
}
|
|
1161
|
+
if (lhs.degree_penalty != rhs.degree_penalty) {
|
|
1162
|
+
return lhs.degree_penalty < rhs.degree_penalty;
|
|
1163
|
+
}
|
|
1164
|
+
if (lhs.next != rhs.next) {
|
|
1165
|
+
return lhs.next < rhs.next;
|
|
1166
|
+
}
|
|
1167
|
+
return lhs.edge_index < rhs.edge_index;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
std::vector<std::vector<int>> collect_heuristic_paths(
|
|
1171
|
+
const DualGraph& graph,
|
|
1172
|
+
int source,
|
|
1173
|
+
int target,
|
|
1174
|
+
int cutoff) {
|
|
1175
|
+
std::vector<std::vector<int>> paths;
|
|
1176
|
+
const int face_count = static_cast<int>(graph.faces.size());
|
|
1177
|
+
if (source == target || source < 0 || target < 0 ||
|
|
1178
|
+
source >= face_count || target >= face_count || cutoff <= 0) {
|
|
1179
|
+
return paths;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1183
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1184
|
+
if (distance[source] == infinity || distance[source] >= cutoff) {
|
|
1185
|
+
return paths;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const int state_budget = std::max(
|
|
1189
|
+
kHeuristicMinStateBudget,
|
|
1190
|
+
std::min(kHeuristicMaxStateBudget, face_count * std::max(1, cutoff) * 8));
|
|
1191
|
+
const int path_budget = std::max(
|
|
1192
|
+
kHeuristicMinPathBudget,
|
|
1193
|
+
std::min(kHeuristicMaxPathBudget, face_count * 2 + cutoff * 8));
|
|
1194
|
+
|
|
1195
|
+
std::priority_queue<HeuristicState, std::vector<HeuristicState>, HeuristicStateWorse> queue;
|
|
1196
|
+
int serial = 0;
|
|
1197
|
+
HeuristicState initial;
|
|
1198
|
+
initial.path.push_back(source);
|
|
1199
|
+
initial.visited.assign(face_count, false);
|
|
1200
|
+
initial.visited[source] = true;
|
|
1201
|
+
initial.estimated_weight = distance[source];
|
|
1202
|
+
initial.estimated_length = distance[source];
|
|
1203
|
+
initial.serial = serial++;
|
|
1204
|
+
queue.push(initial);
|
|
1205
|
+
|
|
1206
|
+
std::map<long long, int> popped_by_depth_face;
|
|
1207
|
+
int popped_states = 0;
|
|
1208
|
+
while (!queue.empty() && popped_states < state_budget &&
|
|
1209
|
+
static_cast<int>(paths.size()) < path_budget) {
|
|
1210
|
+
HeuristicState state = queue.top();
|
|
1211
|
+
queue.pop();
|
|
1212
|
+
++popped_states;
|
|
1213
|
+
|
|
1214
|
+
const int current = state.path.back();
|
|
1215
|
+
const int depth = static_cast<int>(state.path.size()) - 1;
|
|
1216
|
+
if (current == target) {
|
|
1217
|
+
if (state.weight < cutoff) {
|
|
1218
|
+
paths.push_back(state.path);
|
|
1219
|
+
}
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1222
|
+
if (depth >= cutoff - 1) {
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const long long beam_key =
|
|
1227
|
+
static_cast<long long>(depth) * static_cast<long long>(face_count) + current;
|
|
1228
|
+
int& beam_count = popped_by_depth_face[beam_key];
|
|
1229
|
+
if (beam_count >= kHeuristicBeamWidth) {
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
++beam_count;
|
|
1233
|
+
|
|
1234
|
+
std::vector<HeuristicStep> steps;
|
|
1235
|
+
for (int edge_index : graph.adjacency[current]) {
|
|
1236
|
+
const GraphEdge& edge = graph.edges[edge_index];
|
|
1237
|
+
const int next = edge.u == current ? edge.v : edge.u;
|
|
1238
|
+
if (state.visited[next]) {
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
if (distance[next] == infinity) {
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
const int new_weight = state.weight + edge.weight;
|
|
1245
|
+
if (new_weight >= cutoff) {
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
const int new_depth = depth + 1;
|
|
1249
|
+
if (new_depth + distance[next] >= cutoff) {
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
HeuristicStep step;
|
|
1253
|
+
step.next = next;
|
|
1254
|
+
step.edge_index = edge_index;
|
|
1255
|
+
step.edge_weight = edge.weight;
|
|
1256
|
+
step.distance = distance[next];
|
|
1257
|
+
step.degree_penalty = std::max(0, static_cast<int>(graph.adjacency[next].size()) - 2);
|
|
1258
|
+
steps.push_back(step);
|
|
1259
|
+
}
|
|
1260
|
+
std::sort(steps.begin(), steps.end(), heuristic_step_less);
|
|
1261
|
+
|
|
1262
|
+
for (const HeuristicStep& step : steps) {
|
|
1263
|
+
const GraphEdge& edge = graph.edges[step.edge_index];
|
|
1264
|
+
HeuristicState next_state;
|
|
1265
|
+
next_state.path = state.path;
|
|
1266
|
+
next_state.path.push_back(step.next);
|
|
1267
|
+
next_state.visited = state.visited;
|
|
1268
|
+
next_state.visited[step.next] = true;
|
|
1269
|
+
next_state.weight = state.weight + edge.weight;
|
|
1270
|
+
next_state.branch_penalty = state.branch_penalty + step.degree_penalty;
|
|
1271
|
+
next_state.estimated_weight = next_state.weight + distance[step.next];
|
|
1272
|
+
next_state.estimated_length =
|
|
1273
|
+
static_cast<int>(next_state.path.size()) - 1 + distance[step.next];
|
|
1274
|
+
next_state.serial = serial++;
|
|
1275
|
+
queue.push(std::move(next_state));
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
return paths;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1079
1282
|
bool contains_endpoint_key(const std::vector<int>& endpoints, int key) {
|
|
1080
1283
|
return std::find(endpoints.begin(), endpoints.end(), key) != endpoints.end();
|
|
1081
1284
|
}
|
|
@@ -1627,6 +1830,13 @@ SimplificationResult find_simplification(
|
|
|
1627
1830
|
const PDCode& code,
|
|
1628
1831
|
const SimplifierOptions& options) {
|
|
1629
1832
|
SimplificationResult result;
|
|
1833
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
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
|
+
}
|
|
1630
1840
|
Diagram diagram(code);
|
|
1631
1841
|
DualGraph graph(diagram);
|
|
1632
1842
|
const auto red_lines = possible_red_lines(diagram);
|
|
@@ -1657,7 +1867,12 @@ SimplificationResult find_simplification(
|
|
|
1657
1867
|
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
1658
1868
|
for (int source : sources) {
|
|
1659
1869
|
for (int destination : destinations) {
|
|
1660
|
-
|
|
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
|
+
}
|
|
1661
1876
|
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
1662
1877
|
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
1663
1878
|
break;
|
|
@@ -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,7 @@ 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,
|
|
338
405
|
ctypes.c_ulonglong,
|
|
339
406
|
ctypes.POINTER(ctypes.c_int),
|
|
340
407
|
ctypes.c_ulonglong,
|
|
@@ -350,7 +417,8 @@ def _load_library() -> ctypes.CDLL:
|
|
|
350
417
|
def _run_one(
|
|
351
418
|
pd_text: str,
|
|
352
419
|
*,
|
|
353
|
-
max_paths: int =
|
|
420
|
+
max_paths: int = -1,
|
|
421
|
+
ban_heuristic: bool = False,
|
|
354
422
|
known_crossingless_components: int = 0,
|
|
355
423
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
356
424
|
) -> dict[str, Any]:
|
|
@@ -363,6 +431,7 @@ def _run_one(
|
|
|
363
431
|
pointer = library.pdcode_simplify_run_json(
|
|
364
432
|
pd_text.encode("utf-8"),
|
|
365
433
|
int(max_paths),
|
|
434
|
+
1 if ban_heuristic else 0,
|
|
366
435
|
int(known_crossingless_components),
|
|
367
436
|
removed_array,
|
|
368
437
|
int(removed_count),
|
|
@@ -386,7 +455,8 @@ def _run_one(
|
|
|
386
455
|
def simplify(
|
|
387
456
|
pd_code: PdInput,
|
|
388
457
|
*,
|
|
389
|
-
max_paths: int =
|
|
458
|
+
max_paths: int = -1,
|
|
459
|
+
ban_heuristic: bool = False,
|
|
390
460
|
known_crossingless_components: int = 0,
|
|
391
461
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
392
462
|
) -> dict[str, Any]:
|
|
@@ -395,6 +465,7 @@ def simplify(
|
|
|
395
465
|
return _run_one(
|
|
396
466
|
normalize_pd_code(pd_code),
|
|
397
467
|
max_paths=max_paths,
|
|
468
|
+
ban_heuristic=ban_heuristic,
|
|
398
469
|
known_crossingless_components=known_crossingless_components,
|
|
399
470
|
remove_crossings=remove_crossings,
|
|
400
471
|
)
|
|
@@ -403,7 +474,8 @@ def simplify(
|
|
|
403
474
|
def simplify_many(
|
|
404
475
|
pd_codes: PdManyInput,
|
|
405
476
|
*,
|
|
406
|
-
max_paths: int =
|
|
477
|
+
max_paths: int = -1,
|
|
478
|
+
ban_heuristic: bool = False,
|
|
407
479
|
known_crossingless_components: int = 0,
|
|
408
480
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
409
481
|
) -> list[dict[str, Any]]:
|
|
@@ -413,6 +485,7 @@ def simplify_many(
|
|
|
413
485
|
_run_one(
|
|
414
486
|
pd_text,
|
|
415
487
|
max_paths=max_paths,
|
|
488
|
+
ban_heuristic=ban_heuristic,
|
|
416
489
|
known_crossingless_components=known_crossingless_components,
|
|
417
490
|
remove_crossings=remove_crossings,
|
|
418
491
|
)
|
|
@@ -424,7 +497,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
424
497
|
parser = argparse.ArgumentParser(description="Run cpp-pd-code-simplify through the Python interface.")
|
|
425
498
|
parser.add_argument("pd_code", nargs="?", help="PD code as PD[...] text or a Python-style list of crossings.")
|
|
426
499
|
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
|
|
500
|
+
parser.add_argument("--max-paths", type=int, default=-1)
|
|
501
|
+
parser.add_argument("--ban-heuristic", action="store_true")
|
|
428
502
|
parser.add_argument("--known-crossingless-components", type=int, default=0)
|
|
429
503
|
parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
|
|
430
504
|
args = parser.parse_args(argv)
|
|
@@ -436,38 +510,41 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
436
510
|
if args.remove_crossings:
|
|
437
511
|
remove_crossings = [int(token) for token in re.findall(r"-?\d+", args.remove_crossings)]
|
|
438
512
|
|
|
513
|
+
exit_code = 0
|
|
439
514
|
if args.pd_file:
|
|
440
|
-
|
|
441
|
-
for line in pathlib.Path(args.pd_file).read_text(encoding="utf-8").splitlines():
|
|
442
|
-
cleaned = line.strip()
|
|
443
|
-
if not cleaned or cleaned.startswith("#"):
|
|
444
|
-
continue
|
|
445
|
-
payload = cleaned.split(":", 1)[1].strip() if ":" in cleaned else cleaned
|
|
446
|
-
lines.append(payload)
|
|
515
|
+
jobs = _pd_file_jobs(args.pd_file)
|
|
447
516
|
batch_payload = []
|
|
448
|
-
|
|
517
|
+
show_labels = len(jobs) > 1
|
|
518
|
+
for label, line in jobs:
|
|
449
519
|
try:
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
)
|
|
520
|
+
item = simplify(
|
|
521
|
+
line,
|
|
522
|
+
max_paths=args.max_paths,
|
|
523
|
+
ban_heuristic=args.ban_heuristic,
|
|
524
|
+
known_crossingless_components=args.known_crossingless_components,
|
|
525
|
+
remove_crossings=remove_crossings,
|
|
457
526
|
)
|
|
527
|
+
if show_labels:
|
|
528
|
+
item = {"label": label, **item}
|
|
529
|
+
batch_payload.append(item)
|
|
458
530
|
except Exception as exc:
|
|
459
|
-
|
|
531
|
+
exit_code = 2
|
|
532
|
+
item = {"error": str(exc)}
|
|
533
|
+
if show_labels:
|
|
534
|
+
item = {"label": label, **item}
|
|
535
|
+
batch_payload.append(item)
|
|
460
536
|
payload: Any = batch_payload
|
|
461
537
|
else:
|
|
462
538
|
payload = simplify(
|
|
463
539
|
args.pd_code or "",
|
|
464
540
|
max_paths=args.max_paths,
|
|
541
|
+
ban_heuristic=args.ban_heuristic,
|
|
465
542
|
known_crossingless_components=args.known_crossingless_components,
|
|
466
543
|
remove_crossings=remove_crossings,
|
|
467
544
|
)
|
|
468
545
|
|
|
469
546
|
print(json.dumps(payload, indent=2))
|
|
470
|
-
return
|
|
547
|
+
return exit_code
|
|
471
548
|
|
|
472
549
|
|
|
473
550
|
if __name__ == "__main__":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|