cpp-pd-code-simplify-interface 0.1.10__py3-none-any.whl → 0.1.12__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/_worker.py +47 -0
- cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +6 -0
- cpp_pd_code_simplify_interface/data/src/native_interface.cpp +4 -1
- cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +221 -119
- cpp_pd_code_simplify_interface/main.py +130 -11
- {cpp_pd_code_simplify_interface-0.1.10.dist-info → cpp_pd_code_simplify_interface-0.1.12.dist-info}/METADATA +13 -6
- cpp_pd_code_simplify_interface-0.1.12.dist-info/RECORD +12 -0
- cpp_pd_code_simplify_interface-0.1.10.dist-info/RECORD +0 -11
- {cpp_pd_code_simplify_interface-0.1.10.dist-info → cpp_pd_code_simplify_interface-0.1.12.dist-info}/WHEEL +0 -0
- {cpp_pd_code_simplify_interface-0.1.10.dist-info → cpp_pd_code_simplify_interface-0.1.12.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .main import _run_one_direct
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> int:
|
|
11
|
+
try:
|
|
12
|
+
request: dict[str, Any] = json.loads(sys.stdin.read())
|
|
13
|
+
result = _run_one_direct(
|
|
14
|
+
str(request.get("pd_text", "")),
|
|
15
|
+
max_paths=int(request.get("max_paths", -1)),
|
|
16
|
+
ban_heuristic=bool(request.get("ban_heuristic", False)),
|
|
17
|
+
reduction_round=int(request.get("reduction_round", -1)),
|
|
18
|
+
max_thread=int(request.get("max_thread", -1)),
|
|
19
|
+
timeout=int(request.get("timeout", -1)),
|
|
20
|
+
verbose=bool(request.get("verbose", False)),
|
|
21
|
+
known_crossingless_components=int(
|
|
22
|
+
request.get("known_crossingless_components", 0)
|
|
23
|
+
),
|
|
24
|
+
remove_crossings=request.get("remove_crossings") or [],
|
|
25
|
+
)
|
|
26
|
+
print(json.dumps({"ok": True, "result": result}, separators=(",", ":")))
|
|
27
|
+
return 0
|
|
28
|
+
except KeyboardInterrupt:
|
|
29
|
+
print(
|
|
30
|
+
json.dumps(
|
|
31
|
+
{"ok": False, "error": "interrupted by Ctrl+C"},
|
|
32
|
+
separators=(",", ":"),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
return 130
|
|
36
|
+
except BaseException as exc: # noqa: BLE001 - return errors through JSON.
|
|
37
|
+
print(
|
|
38
|
+
json.dumps(
|
|
39
|
+
{"ok": False, "error": str(exc)},
|
|
40
|
+
separators=(",", ":"),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
raise SystemExit(main())
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <array>
|
|
4
|
+
#include <chrono>
|
|
4
5
|
#include <cstddef>
|
|
5
6
|
#include <functional>
|
|
6
7
|
#include <iosfwd>
|
|
@@ -51,10 +52,14 @@ struct GreenCrossing {
|
|
|
51
52
|
struct SimplifierOptions {
|
|
52
53
|
int max_paths = -1;
|
|
53
54
|
int max_threads = -1;
|
|
55
|
+
int timeout_seconds = -1;
|
|
56
|
+
bool has_timeout_deadline = false;
|
|
57
|
+
std::chrono::steady_clock::time_point timeout_deadline{};
|
|
54
58
|
bool ban_heuristic = false;
|
|
55
59
|
bool require_applicable = false;
|
|
56
60
|
bool verbose = false;
|
|
57
61
|
std::function<void(const std::string&)> progress;
|
|
62
|
+
std::function<bool()> should_cancel;
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
struct LinkComponentSummary {
|
|
@@ -121,6 +126,7 @@ struct ReductionResult {
|
|
|
121
126
|
std::size_t tested_green_paths = 0;
|
|
122
127
|
std::string last_path_search_mode;
|
|
123
128
|
bool stopped_by_round_limit = false;
|
|
129
|
+
bool timed_out = false;
|
|
124
130
|
};
|
|
125
131
|
|
|
126
132
|
PDCODE_SIMPLIFY_API PDCode parse_pd_code(const std::string& text);
|
|
@@ -106,7 +106,8 @@ std::string result_to_json(
|
|
|
106
106
|
out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
|
|
107
107
|
out << "\"last_path_search_mode\":\"" << json_escape(result.last_path_search_mode) << "\",";
|
|
108
108
|
out << "\"stopped_by_round_limit\":"
|
|
109
|
-
<< (result.stopped_by_round_limit ? "true" : "false");
|
|
109
|
+
<< (result.stopped_by_round_limit ? "true" : "false") << ",";
|
|
110
|
+
out << "\"timed_out\":" << (result.timed_out ? "true" : "false");
|
|
110
111
|
out << "}";
|
|
111
112
|
return out.str();
|
|
112
113
|
}
|
|
@@ -133,6 +134,7 @@ char* pdcode_simplify_run_json(
|
|
|
133
134
|
int ban_heuristic,
|
|
134
135
|
int reduction_round,
|
|
135
136
|
int max_thread,
|
|
137
|
+
int timeout_seconds,
|
|
136
138
|
int verbose,
|
|
137
139
|
unsigned long long known_crossingless_components,
|
|
138
140
|
const int* removed_crossings,
|
|
@@ -146,6 +148,7 @@ char* pdcode_simplify_run_json(
|
|
|
146
148
|
pdcode_simplify::SimplifierOptions options;
|
|
147
149
|
options.max_paths = max_paths;
|
|
148
150
|
options.max_threads = max_thread;
|
|
151
|
+
options.timeout_seconds = timeout_seconds;
|
|
149
152
|
options.ban_heuristic = ban_heuristic != 0;
|
|
150
153
|
options.verbose = verbose != 0;
|
|
151
154
|
options.progress = [](const std::string& message) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "pdcode_simplify/pdcode_simplify.hpp"
|
|
2
2
|
|
|
3
3
|
#include <algorithm>
|
|
4
|
+
#include <chrono>
|
|
4
5
|
#include <cctype>
|
|
5
6
|
#include <cstdlib>
|
|
6
7
|
#include <deque>
|
|
@@ -510,8 +511,10 @@ std::vector<std::vector<int>> raw_faces_from_pd_code(const PDCode& code) {
|
|
|
510
511
|
struct Diagram {
|
|
511
512
|
PDCode code;
|
|
512
513
|
std::vector<CrossingState> crossings;
|
|
514
|
+
std::vector<int> rotations;
|
|
513
515
|
|
|
514
|
-
explicit Diagram(PDCode input)
|
|
516
|
+
explicit Diagram(PDCode input)
|
|
517
|
+
: code(std::move(input)), crossings(code.size()), rotations(code.size(), 0) {
|
|
515
518
|
build_adjacency();
|
|
516
519
|
auto starts = component_starts_from_pd();
|
|
517
520
|
orient_crossings(starts);
|
|
@@ -533,6 +536,10 @@ struct Diagram {
|
|
|
533
536
|
return Endpoint{endpoint.crossing, positive_mod(endpoint.strand + offset, 4)};
|
|
534
537
|
}
|
|
535
538
|
|
|
539
|
+
int label_at(int crossing, int strand) const {
|
|
540
|
+
return code.at(crossing).at(positive_mod(strand + rotations.at(crossing), 4));
|
|
541
|
+
}
|
|
542
|
+
|
|
536
543
|
std::vector<Endpoint> crossing_entries() const {
|
|
537
544
|
std::vector<Endpoint> entries;
|
|
538
545
|
entries.reserve(crossings.size() * 2);
|
|
@@ -715,6 +722,7 @@ private:
|
|
|
715
722
|
|
|
716
723
|
void rotate_crossing_180(int crossing) {
|
|
717
724
|
auto old_adjacent = crossings[crossing].adjacent;
|
|
725
|
+
rotations[crossing] = positive_mod(rotations[crossing] + 2, 4);
|
|
718
726
|
bool old_directions[4][4]{};
|
|
719
727
|
for (int a = 0; a < 4; ++a) {
|
|
720
728
|
for (int b = 0; b < 4; ++b) {
|
|
@@ -980,7 +988,10 @@ std::set<int> normalized_removed_crossings(const PDCode& code, const std::vector
|
|
|
980
988
|
std::vector<int> heuristic_distances_to_target(
|
|
981
989
|
const DualGraph& graph,
|
|
982
990
|
int target,
|
|
983
|
-
int cutoff
|
|
991
|
+
int cutoff,
|
|
992
|
+
const SimplifierOptions& options);
|
|
993
|
+
|
|
994
|
+
void check_timeout(const SimplifierOptions& options);
|
|
984
995
|
|
|
985
996
|
void collect_simple_paths_dfs(
|
|
986
997
|
const DualGraph& graph,
|
|
@@ -992,7 +1003,9 @@ void collect_simple_paths_dfs(
|
|
|
992
1003
|
const std::vector<int>& distance,
|
|
993
1004
|
std::vector<char>& visited,
|
|
994
1005
|
std::vector<int>& current_path,
|
|
995
|
-
std::vector<std::vector<int>>& paths
|
|
1006
|
+
std::vector<std::vector<int>>& paths,
|
|
1007
|
+
const SimplifierOptions& options) {
|
|
1008
|
+
check_timeout(options);
|
|
996
1009
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
997
1010
|
if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
|
|
998
1011
|
return;
|
|
@@ -1038,7 +1051,8 @@ void collect_simple_paths_dfs(
|
|
|
1038
1051
|
distance,
|
|
1039
1052
|
visited,
|
|
1040
1053
|
current_path,
|
|
1041
|
-
paths
|
|
1054
|
+
paths,
|
|
1055
|
+
options);
|
|
1042
1056
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1043
1057
|
visited[next] = false;
|
|
1044
1058
|
current_path.pop_back();
|
|
@@ -1056,7 +1070,9 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1056
1070
|
int source,
|
|
1057
1071
|
int target,
|
|
1058
1072
|
int cutoff,
|
|
1059
|
-
int max_paths
|
|
1073
|
+
int max_paths,
|
|
1074
|
+
const SimplifierOptions& options) {
|
|
1075
|
+
check_timeout(options);
|
|
1060
1076
|
std::vector<std::vector<int>> paths;
|
|
1061
1077
|
if (source == target || source < 0 || target < 0 ||
|
|
1062
1078
|
source >= static_cast<int>(graph.faces.size()) ||
|
|
@@ -1067,7 +1083,7 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1067
1083
|
|
|
1068
1084
|
std::vector<char> visited(graph.faces.size(), false);
|
|
1069
1085
|
std::vector<int> current_path{source};
|
|
1070
|
-
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1086
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
|
|
1071
1087
|
visited[source] = true;
|
|
1072
1088
|
collect_simple_paths_dfs(
|
|
1073
1089
|
graph,
|
|
@@ -1079,14 +1095,16 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1079
1095
|
distance,
|
|
1080
1096
|
visited,
|
|
1081
1097
|
current_path,
|
|
1082
|
-
paths
|
|
1098
|
+
paths,
|
|
1099
|
+
options);
|
|
1083
1100
|
return paths;
|
|
1084
1101
|
}
|
|
1085
1102
|
|
|
1086
1103
|
std::vector<int> heuristic_distances_to_target(
|
|
1087
1104
|
const DualGraph& graph,
|
|
1088
1105
|
int target,
|
|
1089
|
-
int cutoff
|
|
1106
|
+
int cutoff,
|
|
1107
|
+
const SimplifierOptions& options) {
|
|
1090
1108
|
const int face_count = static_cast<int>(graph.faces.size());
|
|
1091
1109
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1092
1110
|
std::vector<int> distance(face_count, infinity);
|
|
@@ -1095,6 +1113,7 @@ std::vector<int> heuristic_distances_to_target(
|
|
|
1095
1113
|
queue.push_back(target);
|
|
1096
1114
|
|
|
1097
1115
|
while (!queue.empty()) {
|
|
1116
|
+
check_timeout(options);
|
|
1098
1117
|
const int current = queue.front();
|
|
1099
1118
|
queue.pop_front();
|
|
1100
1119
|
for (int edge_index : graph.adjacency[current]) {
|
|
@@ -1172,7 +1191,9 @@ std::vector<std::vector<int>> collect_heuristic_paths(
|
|
|
1172
1191
|
const DualGraph& graph,
|
|
1173
1192
|
int source,
|
|
1174
1193
|
int target,
|
|
1175
|
-
int cutoff
|
|
1194
|
+
int cutoff,
|
|
1195
|
+
const SimplifierOptions& options) {
|
|
1196
|
+
check_timeout(options);
|
|
1176
1197
|
std::vector<std::vector<int>> paths;
|
|
1177
1198
|
const int face_count = static_cast<int>(graph.faces.size());
|
|
1178
1199
|
if (source == target || source < 0 || target < 0 ||
|
|
@@ -1180,7 +1201,7 @@ std::vector<std::vector<int>> collect_heuristic_paths(
|
|
|
1180
1201
|
return paths;
|
|
1181
1202
|
}
|
|
1182
1203
|
|
|
1183
|
-
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1204
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
|
|
1184
1205
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1185
1206
|
if (distance[source] == infinity || distance[source] >= cutoff) {
|
|
1186
1207
|
return paths;
|
|
@@ -1208,6 +1229,7 @@ std::vector<std::vector<int>> collect_heuristic_paths(
|
|
|
1208
1229
|
int popped_states = 0;
|
|
1209
1230
|
while (!queue.empty() && popped_states < state_budget &&
|
|
1210
1231
|
static_cast<int>(paths.size()) < path_budget) {
|
|
1232
|
+
check_timeout(options);
|
|
1211
1233
|
HeuristicState state = queue.top();
|
|
1212
1234
|
queue.pop();
|
|
1213
1235
|
++popped_states;
|
|
@@ -1816,6 +1838,41 @@ std::string search_mode_for_options(const SimplifierOptions& options) {
|
|
|
1816
1838
|
return "bounded";
|
|
1817
1839
|
}
|
|
1818
1840
|
|
|
1841
|
+
void validate_timeout_options(const SimplifierOptions& options) {
|
|
1842
|
+
if (options.timeout_seconds < -1 || options.timeout_seconds == 0) {
|
|
1843
|
+
throw std::invalid_argument("timeout must be -1 or a positive integer");
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
class TimeoutError : public std::runtime_error {
|
|
1848
|
+
public:
|
|
1849
|
+
explicit TimeoutError(const std::string& message) : std::runtime_error(message) {}
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
SimplifierOptions with_timeout_deadline(SimplifierOptions options) {
|
|
1853
|
+
validate_timeout_options(options);
|
|
1854
|
+
if (options.timeout_seconds > 0 && !options.has_timeout_deadline) {
|
|
1855
|
+
options.has_timeout_deadline = true;
|
|
1856
|
+
options.timeout_deadline =
|
|
1857
|
+
std::chrono::steady_clock::now() + std::chrono::seconds(options.timeout_seconds);
|
|
1858
|
+
}
|
|
1859
|
+
return options;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
void check_timeout(const SimplifierOptions& options) {
|
|
1863
|
+
if (options.should_cancel && options.should_cancel()) {
|
|
1864
|
+
throw std::runtime_error("interrupted by Ctrl+C");
|
|
1865
|
+
}
|
|
1866
|
+
if (!options.has_timeout_deadline) {
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (std::chrono::steady_clock::now() >= options.timeout_deadline) {
|
|
1870
|
+
std::ostringstream message;
|
|
1871
|
+
message << "timeout after " << options.timeout_seconds << " seconds";
|
|
1872
|
+
throw TimeoutError(message.str());
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1819
1876
|
} // namespace
|
|
1820
1877
|
|
|
1821
1878
|
PDCode parse_pd_code(const std::string& text) {
|
|
@@ -1878,14 +1935,9 @@ std::string format_final_pd_code(const PDCode& code) {
|
|
|
1878
1935
|
std::set<int> labels;
|
|
1879
1936
|
std::map<int, int> next_label;
|
|
1880
1937
|
|
|
1881
|
-
auto label_at = [&](int crossing, int strand) {
|
|
1882
|
-
const Endpoint other = diagram.crossings[crossing].adjacent[strand];
|
|
1883
|
-
return diagram.code[other.crossing][other.strand];
|
|
1884
|
-
};
|
|
1885
|
-
|
|
1886
1938
|
for (int crossing = 0; crossing < static_cast<int>(code.size()); ++crossing) {
|
|
1887
1939
|
for (int strand = 0; strand < 4; ++strand) {
|
|
1888
|
-
const int label = label_at(crossing, strand);
|
|
1940
|
+
const int label = diagram.label_at(crossing, strand);
|
|
1889
1941
|
oriented[crossing][strand] = label;
|
|
1890
1942
|
labels.insert(label);
|
|
1891
1943
|
}
|
|
@@ -1898,8 +1950,8 @@ std::string format_final_pd_code(const PDCode& code) {
|
|
|
1898
1950
|
if (!diagram.crossings[crossing].directions[tail][head]) {
|
|
1899
1951
|
continue;
|
|
1900
1952
|
}
|
|
1901
|
-
const int in_label = label_at(crossing, tail);
|
|
1902
|
-
const int out_label = label_at(crossing, head);
|
|
1953
|
+
const int in_label = diagram.label_at(crossing, tail);
|
|
1954
|
+
const int out_label = diagram.label_at(crossing, head);
|
|
1903
1955
|
const auto inserted = next_label.insert(std::make_pair(in_label, out_label));
|
|
1904
1956
|
if (!inserted.second && inserted.first->second != out_label) {
|
|
1905
1957
|
throw std::invalid_argument("Final PD component orientation is inconsistent");
|
|
@@ -1934,6 +1986,7 @@ std::string format_final_pd_code(const PDCode& code) {
|
|
|
1934
1986
|
label = relabel.at(label);
|
|
1935
1987
|
}
|
|
1936
1988
|
}
|
|
1989
|
+
std::sort(oriented.begin(), oriented.end());
|
|
1937
1990
|
return format_pd_code(oriented);
|
|
1938
1991
|
}
|
|
1939
1992
|
|
|
@@ -2210,6 +2263,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2210
2263
|
const std::string& path_search_mode,
|
|
2211
2264
|
int red_index,
|
|
2212
2265
|
const std::atomic<int>* best_found_index) {
|
|
2266
|
+
check_timeout(options);
|
|
2213
2267
|
RedPathSearchOutcome outcome;
|
|
2214
2268
|
outcome.witness.path_search_mode = path_search_mode;
|
|
2215
2269
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
@@ -2228,6 +2282,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2228
2282
|
const std::array<int, 2> destinations{end_face, end_opposite_face};
|
|
2229
2283
|
|
|
2230
2284
|
for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
|
|
2285
|
+
check_timeout(options);
|
|
2231
2286
|
const Endpoint endpoint = red_path[i];
|
|
2232
2287
|
const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
|
|
2233
2288
|
const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
|
|
@@ -2240,15 +2295,17 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2240
2295
|
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
2241
2296
|
for (int source : sources) {
|
|
2242
2297
|
for (int destination : destinations) {
|
|
2298
|
+
check_timeout(options);
|
|
2243
2299
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2244
2300
|
outcome.skipped = true;
|
|
2245
2301
|
return outcome;
|
|
2246
2302
|
}
|
|
2247
2303
|
std::vector<std::vector<int>> found_paths;
|
|
2248
2304
|
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2249
|
-
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2305
|
+
found_paths = collect_heuristic_paths(graph, source, destination, cutoff, options);
|
|
2250
2306
|
} else {
|
|
2251
|
-
found_paths = collect_simple_paths(
|
|
2307
|
+
found_paths = collect_simple_paths(
|
|
2308
|
+
graph, source, destination, cutoff, options.max_paths, options);
|
|
2252
2309
|
}
|
|
2253
2310
|
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
2254
2311
|
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
@@ -2258,6 +2315,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2258
2315
|
}
|
|
2259
2316
|
|
|
2260
2317
|
for (const auto& green_path : paths) {
|
|
2318
|
+
check_timeout(options);
|
|
2261
2319
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2262
2320
|
outcome.skipped = true;
|
|
2263
2321
|
return outcome;
|
|
@@ -2398,36 +2456,49 @@ SimplificationResult find_simplification_parallel_bruteforce(
|
|
|
2398
2456
|
SimplificationResult find_simplification(
|
|
2399
2457
|
const PDCode& code,
|
|
2400
2458
|
const SimplifierOptions& options) {
|
|
2459
|
+
const SimplifierOptions run_options = with_timeout_deadline(options);
|
|
2460
|
+
check_timeout(run_options);
|
|
2401
2461
|
SimplificationResult result;
|
|
2402
|
-
result.path_search_mode = search_mode_for_options(
|
|
2462
|
+
result.path_search_mode = search_mode_for_options(run_options);
|
|
2403
2463
|
Diagram diagram(code);
|
|
2464
|
+
check_timeout(run_options);
|
|
2404
2465
|
DualGraph base_graph(diagram);
|
|
2466
|
+
check_timeout(run_options);
|
|
2405
2467
|
const auto red_lines = possible_red_lines(diagram);
|
|
2406
|
-
|
|
2468
|
+
check_timeout(run_options);
|
|
2469
|
+
const bool brute_force_mode = run_options.max_paths == -1 && run_options.ban_heuristic;
|
|
2407
2470
|
const int worker_count = brute_force_mode
|
|
2408
2471
|
? selected_bruteforce_worker_count(
|
|
2409
|
-
|
|
2472
|
+
run_options.max_threads,
|
|
2410
2473
|
static_cast<int>(red_lines.size()))
|
|
2411
2474
|
: 1;
|
|
2475
|
+
if (brute_force_mode && run_options.max_threads == -1) {
|
|
2476
|
+
std::ostringstream message;
|
|
2477
|
+
message << "bruteforce_threads max_thread=-1"
|
|
2478
|
+
<< " actual_threads=" << worker_count
|
|
2479
|
+
<< " red_paths=" << red_lines.size();
|
|
2480
|
+
emit_progress(run_options, message.str());
|
|
2481
|
+
}
|
|
2412
2482
|
if (worker_count > 1) {
|
|
2413
2483
|
return find_simplification_parallel_bruteforce(
|
|
2414
2484
|
code,
|
|
2415
2485
|
diagram,
|
|
2416
2486
|
base_graph,
|
|
2417
2487
|
red_lines,
|
|
2418
|
-
|
|
2488
|
+
run_options,
|
|
2419
2489
|
result.path_search_mode,
|
|
2420
2490
|
worker_count);
|
|
2421
2491
|
}
|
|
2422
2492
|
|
|
2423
2493
|
for (const auto& red_path : red_lines) {
|
|
2494
|
+
check_timeout(run_options);
|
|
2424
2495
|
++result.tested_red_paths;
|
|
2425
2496
|
const RedPathSearchOutcome outcome = search_single_red_path(
|
|
2426
2497
|
code,
|
|
2427
2498
|
diagram,
|
|
2428
2499
|
base_graph,
|
|
2429
2500
|
red_path,
|
|
2430
|
-
|
|
2501
|
+
run_options,
|
|
2431
2502
|
result.path_search_mode,
|
|
2432
2503
|
-1,
|
|
2433
2504
|
nullptr);
|
|
@@ -2449,125 +2520,155 @@ ReductionResult reduce_pd_code(
|
|
|
2449
2520
|
std::size_t known_crossingless_components,
|
|
2450
2521
|
const SimplifierOptions& options,
|
|
2451
2522
|
int reduction_round) {
|
|
2452
|
-
|
|
2453
|
-
std::ostringstream message;
|
|
2454
|
-
message << "start input_crossings=" << code.size()
|
|
2455
|
-
<< " known_crossingless_components=" << known_crossingless_components
|
|
2456
|
-
<< " reduction_round=" << reduction_round
|
|
2457
|
-
<< " max_paths=" << options.max_paths
|
|
2458
|
-
<< " max_thread=" << options.max_threads
|
|
2459
|
-
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2460
|
-
emit_progress(options, message.str());
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
|
|
2523
|
+
const SimplifierOptions run_options = with_timeout_deadline(options);
|
|
2464
2524
|
ReductionResult output;
|
|
2465
|
-
output.code =
|
|
2466
|
-
output.crossingless_components =
|
|
2467
|
-
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2468
|
-
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2469
|
-
{
|
|
2470
|
-
std::ostringstream message;
|
|
2471
|
-
message << "pre_simplify input_crossings=" << code.size()
|
|
2472
|
-
<< " output_crossings=" << output.code.size()
|
|
2473
|
-
<< " crossingless_components=" << output.crossingless_components
|
|
2474
|
-
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2475
|
-
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2476
|
-
emit_progress(options, message.str());
|
|
2477
|
-
}
|
|
2525
|
+
output.code = code;
|
|
2526
|
+
output.crossingless_components = known_crossingless_components;
|
|
2478
2527
|
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
search_options.require_applicable = true;
|
|
2482
|
-
const int round = output.mid_simplification_rounds + 1;
|
|
2528
|
+
try {
|
|
2529
|
+
check_timeout(run_options);
|
|
2483
2530
|
{
|
|
2484
2531
|
std::ostringstream message;
|
|
2485
|
-
message << "
|
|
2486
|
-
<< "
|
|
2487
|
-
<< "
|
|
2488
|
-
<< "
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2532
|
+
message << "start input_crossings=" << code.size()
|
|
2533
|
+
<< " known_crossingless_components=" << known_crossingless_components
|
|
2534
|
+
<< " reduction_round=" << reduction_round
|
|
2535
|
+
<< " max_paths=" << run_options.max_paths
|
|
2536
|
+
<< " max_thread=" << run_options.max_threads
|
|
2537
|
+
<< " timeout=" << run_options.timeout_seconds
|
|
2538
|
+
<< " heuristic=" << (run_options.ban_heuristic ? "off" : "on");
|
|
2539
|
+
emit_progress(run_options, message.str());
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
const PDSimplificationResult prepared =
|
|
2543
|
+
simplify_pd_code(code, known_crossingless_components);
|
|
2544
|
+
output.code = prepared.code;
|
|
2545
|
+
output.crossingless_components = prepared.crossingless_components;
|
|
2546
|
+
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2547
|
+
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2548
|
+
check_timeout(run_options);
|
|
2495
2549
|
{
|
|
2496
2550
|
std::ostringstream message;
|
|
2497
|
-
message << "
|
|
2498
|
-
<< "
|
|
2499
|
-
<< "
|
|
2500
|
-
<< "
|
|
2501
|
-
<< "
|
|
2502
|
-
emit_progress(
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
SimplifierOptions
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2551
|
+
message << "pre_simplify input_crossings=" << code.size()
|
|
2552
|
+
<< " output_crossings=" << output.code.size()
|
|
2553
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2554
|
+
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2555
|
+
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2556
|
+
emit_progress(run_options, message.str());
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
|
|
2560
|
+
check_timeout(run_options);
|
|
2561
|
+
SimplifierOptions search_options = run_options;
|
|
2562
|
+
search_options.require_applicable = true;
|
|
2563
|
+
output.last_path_search_mode = search_mode_for_options(search_options);
|
|
2564
|
+
const int round = output.mid_simplification_rounds + 1;
|
|
2511
2565
|
{
|
|
2512
2566
|
std::ostringstream message;
|
|
2513
2567
|
message << "round " << round
|
|
2514
|
-
<< "
|
|
2515
|
-
<< "
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
output.
|
|
2520
|
-
output.
|
|
2521
|
-
output.
|
|
2568
|
+
<< " search_start crossings=" << output.code.size()
|
|
2569
|
+
<< " mode=" << output.last_path_search_mode
|
|
2570
|
+
<< " max_thread=" << search_options.max_threads;
|
|
2571
|
+
emit_progress(run_options, message.str());
|
|
2572
|
+
}
|
|
2573
|
+
SimplificationResult search = find_simplification(output.code, search_options);
|
|
2574
|
+
output.tested_red_paths += search.tested_red_paths;
|
|
2575
|
+
output.tested_green_paths += search.tested_green_paths;
|
|
2576
|
+
output.last_path_search_mode = search.path_search_mode;
|
|
2522
2577
|
{
|
|
2523
2578
|
std::ostringstream message;
|
|
2524
2579
|
message << "round " << round
|
|
2525
|
-
<< "
|
|
2526
|
-
<< "
|
|
2527
|
-
<< "
|
|
2528
|
-
|
|
2580
|
+
<< " search_done found=" << (search.found ? "yes" : "no")
|
|
2581
|
+
<< " mode=" << search.path_search_mode
|
|
2582
|
+
<< " tested_red=" << search.tested_red_paths
|
|
2583
|
+
<< " tested_green=" << search.tested_green_paths;
|
|
2584
|
+
emit_progress(run_options, message.str());
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
if (!search.found && reduction_round < 0 &&
|
|
2588
|
+
run_options.max_paths == -1 && !run_options.ban_heuristic) {
|
|
2589
|
+
SimplifierOptions brute_options = run_options;
|
|
2590
|
+
brute_options.max_paths = -1;
|
|
2591
|
+
brute_options.ban_heuristic = true;
|
|
2592
|
+
brute_options.require_applicable = true;
|
|
2593
|
+
output.last_path_search_mode = search_mode_for_options(brute_options);
|
|
2594
|
+
{
|
|
2595
|
+
std::ostringstream message;
|
|
2596
|
+
message << "round " << round
|
|
2597
|
+
<< " brute_fallback_start crossings=" << output.code.size()
|
|
2598
|
+
<< " max_thread=" << brute_options.max_threads;
|
|
2599
|
+
emit_progress(run_options, message.str());
|
|
2600
|
+
}
|
|
2601
|
+
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
2602
|
+
output.tested_red_paths += brute.tested_red_paths;
|
|
2603
|
+
output.tested_green_paths += brute.tested_green_paths;
|
|
2604
|
+
output.last_path_search_mode = brute.path_search_mode;
|
|
2605
|
+
{
|
|
2606
|
+
std::ostringstream message;
|
|
2607
|
+
message << "round " << round
|
|
2608
|
+
<< " brute_fallback_done found=" << (brute.found ? "yes" : "no")
|
|
2609
|
+
<< " tested_red=" << brute.tested_red_paths
|
|
2610
|
+
<< " tested_green=" << brute.tested_green_paths;
|
|
2611
|
+
emit_progress(run_options, message.str());
|
|
2612
|
+
}
|
|
2613
|
+
if (brute.found) {
|
|
2614
|
+
++output.heuristic_failover_rounds;
|
|
2615
|
+
search = std::move(brute);
|
|
2616
|
+
}
|
|
2529
2617
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2618
|
+
|
|
2619
|
+
if (!search.found) {
|
|
2620
|
+
{
|
|
2621
|
+
std::ostringstream message;
|
|
2622
|
+
message << "round " << round
|
|
2623
|
+
<< " stop_no_path crossings=" << output.code.size();
|
|
2624
|
+
emit_progress(run_options, message.str());
|
|
2625
|
+
}
|
|
2626
|
+
break;
|
|
2533
2627
|
}
|
|
2534
|
-
}
|
|
2535
2628
|
|
|
2536
|
-
|
|
2629
|
+
const std::size_t before_apply_crossings = output.code.size();
|
|
2630
|
+
check_timeout(run_options);
|
|
2631
|
+
const MidSimplificationApplyResult applied =
|
|
2632
|
+
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2633
|
+
++output.mid_simplification_rounds;
|
|
2634
|
+
output.code = applied.code;
|
|
2635
|
+
output.crossingless_components = applied.crossingless_components;
|
|
2636
|
+
check_timeout(run_options);
|
|
2637
|
+
const PDSimplificationResult simplified =
|
|
2638
|
+
simplify_pd_code(output.code, output.crossingless_components);
|
|
2639
|
+
output.code = simplified.code;
|
|
2640
|
+
output.crossingless_components = simplified.crossingless_components;
|
|
2641
|
+
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2642
|
+
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2643
|
+
check_timeout(run_options);
|
|
2537
2644
|
{
|
|
2538
2645
|
std::ostringstream message;
|
|
2539
2646
|
message << "round " << round
|
|
2540
|
-
<< "
|
|
2541
|
-
|
|
2647
|
+
<< " applied crossings=" << before_apply_crossings
|
|
2648
|
+
<< " -> " << applied.code.size()
|
|
2649
|
+
<< " -> " << output.code.size()
|
|
2650
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2651
|
+
<< " r1_moves=" << simplified.reidemeister_i_moves
|
|
2652
|
+
<< " nugatory_moves=" << simplified.nugatory_crossing_moves;
|
|
2653
|
+
emit_progress(run_options, message.str());
|
|
2542
2654
|
}
|
|
2543
|
-
break;
|
|
2544
2655
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
const MidSimplificationApplyResult applied =
|
|
2548
|
-
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2549
|
-
++output.mid_simplification_rounds;
|
|
2550
|
-
const PDSimplificationResult simplified =
|
|
2551
|
-
simplify_pd_code(applied.code, applied.crossingless_components);
|
|
2552
|
-
output.code = simplified.code;
|
|
2553
|
-
output.crossingless_components = simplified.crossingless_components;
|
|
2554
|
-
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2555
|
-
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2656
|
+
} catch (const TimeoutError& error) {
|
|
2657
|
+
output.timed_out = true;
|
|
2556
2658
|
{
|
|
2557
2659
|
std::ostringstream message;
|
|
2558
|
-
message <<
|
|
2559
|
-
<< "
|
|
2560
|
-
<< " -> " << applied.code.size()
|
|
2561
|
-
<< " -> " << output.code.size()
|
|
2660
|
+
message << error.what()
|
|
2661
|
+
<< "; returning_current_best crossings=" << output.code.size()
|
|
2562
2662
|
<< " crossingless_components=" << output.crossingless_components
|
|
2563
|
-
<< "
|
|
2564
|
-
|
|
2565
|
-
emit_progress(options, message.str());
|
|
2663
|
+
<< " mid_rounds=" << output.mid_simplification_rounds;
|
|
2664
|
+
emit_progress(run_options, message.str());
|
|
2566
2665
|
}
|
|
2567
2666
|
}
|
|
2568
2667
|
|
|
2569
2668
|
output.stopped_by_round_limit =
|
|
2570
|
-
|
|
2669
|
+
!output.timed_out &&
|
|
2670
|
+
reduction_round >= 0 &&
|
|
2671
|
+
output.mid_simplification_rounds >= reduction_round;
|
|
2571
2672
|
{
|
|
2572
2673
|
std::ostringstream message;
|
|
2573
2674
|
message << "done final_crossings=" << output.code.size()
|
|
@@ -2575,8 +2676,9 @@ ReductionResult reduce_pd_code(
|
|
|
2575
2676
|
<< " mid_rounds=" << output.mid_simplification_rounds
|
|
2576
2677
|
<< " heuristic_failover_rounds=" << output.heuristic_failover_rounds
|
|
2577
2678
|
<< " stopped_by_round_limit="
|
|
2578
|
-
<< (output.stopped_by_round_limit ? "yes" : "no")
|
|
2579
|
-
|
|
2679
|
+
<< (output.stopped_by_round_limit ? "yes" : "no")
|
|
2680
|
+
<< " timed_out=" << (output.timed_out ? "yes" : "no");
|
|
2681
|
+
emit_progress(run_options, message.str());
|
|
2580
2682
|
}
|
|
2581
2683
|
return output;
|
|
2582
2684
|
}
|
|
@@ -762,6 +762,7 @@ def _load_library() -> ctypes.CDLL:
|
|
|
762
762
|
ctypes.c_int,
|
|
763
763
|
ctypes.c_int,
|
|
764
764
|
ctypes.c_int,
|
|
765
|
+
ctypes.c_int,
|
|
765
766
|
ctypes.c_ulonglong,
|
|
766
767
|
ctypes.POINTER(ctypes.c_int),
|
|
767
768
|
ctypes.c_ulonglong,
|
|
@@ -774,13 +775,14 @@ def _load_library() -> ctypes.CDLL:
|
|
|
774
775
|
return library
|
|
775
776
|
|
|
776
777
|
|
|
777
|
-
def
|
|
778
|
+
def _run_one_direct(
|
|
778
779
|
pd_text: str,
|
|
779
780
|
*,
|
|
780
781
|
max_paths: int = -1,
|
|
781
782
|
ban_heuristic: bool = False,
|
|
782
783
|
reduction_round: int = -1,
|
|
783
784
|
max_thread: int = -1,
|
|
785
|
+
timeout: int = -1,
|
|
784
786
|
verbose: bool = False,
|
|
785
787
|
known_crossingless_components: int = 0,
|
|
786
788
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
@@ -789,6 +791,8 @@ def _run_one(
|
|
|
789
791
|
raise ValueError("reduction_round must be -1 or a non-negative integer")
|
|
790
792
|
if max_thread < -1 or max_thread == 0:
|
|
791
793
|
raise ValueError("max_thread must be -1 or a positive integer")
|
|
794
|
+
if timeout < -1 or timeout == 0:
|
|
795
|
+
raise ValueError("timeout must be -1 or a positive integer")
|
|
792
796
|
library = _load_library()
|
|
793
797
|
removed_count = 0 if remove_crossings is None else len(remove_crossings)
|
|
794
798
|
removed_array = None
|
|
@@ -801,6 +805,7 @@ def _run_one(
|
|
|
801
805
|
1 if ban_heuristic else 0,
|
|
802
806
|
int(reduction_round),
|
|
803
807
|
int(max_thread),
|
|
808
|
+
int(timeout),
|
|
804
809
|
1 if verbose else 0,
|
|
805
810
|
int(known_crossingless_components),
|
|
806
811
|
removed_array,
|
|
@@ -822,6 +827,93 @@ def _run_one(
|
|
|
822
827
|
return result
|
|
823
828
|
|
|
824
829
|
|
|
830
|
+
def _terminate_process(proc: subprocess.Popen[str]) -> None:
|
|
831
|
+
if proc.poll() is not None:
|
|
832
|
+
return
|
|
833
|
+
proc.terminate()
|
|
834
|
+
try:
|
|
835
|
+
proc.wait(timeout=2.0)
|
|
836
|
+
except subprocess.TimeoutExpired:
|
|
837
|
+
proc.kill()
|
|
838
|
+
proc.wait()
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def _run_one(
|
|
842
|
+
pd_text: str,
|
|
843
|
+
*,
|
|
844
|
+
max_paths: int = -1,
|
|
845
|
+
ban_heuristic: bool = False,
|
|
846
|
+
reduction_round: int = -1,
|
|
847
|
+
max_thread: int = -1,
|
|
848
|
+
timeout: int = -1,
|
|
849
|
+
verbose: bool = False,
|
|
850
|
+
known_crossingless_components: int = 0,
|
|
851
|
+
remove_crossings: Optional[Sequence[int]] = None,
|
|
852
|
+
) -> dict[str, Any]:
|
|
853
|
+
if reduction_round < -1:
|
|
854
|
+
raise ValueError("reduction_round must be -1 or a non-negative integer")
|
|
855
|
+
if max_thread < -1 or max_thread == 0:
|
|
856
|
+
raise ValueError("max_thread must be -1 or a positive integer")
|
|
857
|
+
if timeout < -1 or timeout == 0:
|
|
858
|
+
raise ValueError("timeout must be -1 or a positive integer")
|
|
859
|
+
|
|
860
|
+
request = {
|
|
861
|
+
"pd_text": pd_text,
|
|
862
|
+
"max_paths": int(max_paths),
|
|
863
|
+
"ban_heuristic": bool(ban_heuristic),
|
|
864
|
+
"reduction_round": int(reduction_round),
|
|
865
|
+
"max_thread": int(max_thread),
|
|
866
|
+
"timeout": int(timeout),
|
|
867
|
+
"verbose": bool(verbose),
|
|
868
|
+
"known_crossingless_components": int(known_crossingless_components),
|
|
869
|
+
"remove_crossings": [int(value) for value in remove_crossings or []],
|
|
870
|
+
}
|
|
871
|
+
env = os.environ.copy()
|
|
872
|
+
project_root = str(pathlib.Path(__file__).resolve().parents[1])
|
|
873
|
+
env["PYTHONPATH"] = project_root + os.pathsep + env.get("PYTHONPATH", "")
|
|
874
|
+
proc = subprocess.Popen(
|
|
875
|
+
[
|
|
876
|
+
sys.executable,
|
|
877
|
+
"-m",
|
|
878
|
+
"cpp_pd_code_simplify_interface._worker",
|
|
879
|
+
],
|
|
880
|
+
cwd=str(pathlib.Path.cwd()),
|
|
881
|
+
text=True,
|
|
882
|
+
stdin=subprocess.PIPE,
|
|
883
|
+
stdout=subprocess.PIPE,
|
|
884
|
+
stderr=None if verbose else subprocess.PIPE,
|
|
885
|
+
env=env,
|
|
886
|
+
)
|
|
887
|
+
try:
|
|
888
|
+
stdout, stderr = proc.communicate(json.dumps(request))
|
|
889
|
+
except KeyboardInterrupt:
|
|
890
|
+
_terminate_process(proc)
|
|
891
|
+
raise
|
|
892
|
+
|
|
893
|
+
if proc.returncode != 0:
|
|
894
|
+
detail = (stderr or "").strip()
|
|
895
|
+
raise PdCodeSimplifyInterfaceError(
|
|
896
|
+
f"C++ interface worker failed with exit code {proc.returncode}"
|
|
897
|
+
+ (f": {detail}" if detail else "")
|
|
898
|
+
)
|
|
899
|
+
try:
|
|
900
|
+
envelope = json.loads(stdout)
|
|
901
|
+
except json.JSONDecodeError as exc:
|
|
902
|
+
raise PdCodeSimplifyInterfaceError(
|
|
903
|
+
f"invalid interface worker JSON output: {stdout!r}"
|
|
904
|
+
) from exc
|
|
905
|
+
if not isinstance(envelope, dict):
|
|
906
|
+
raise PdCodeSimplifyInterfaceError(
|
|
907
|
+
f"invalid interface worker response type: {type(envelope)!r}"
|
|
908
|
+
)
|
|
909
|
+
if not envelope.get("ok", False):
|
|
910
|
+
raise PdCodeSimplifyInterfaceError(str(envelope.get("error", "unknown worker error")))
|
|
911
|
+
result = envelope.get("result")
|
|
912
|
+
if not isinstance(result, dict):
|
|
913
|
+
raise PdCodeSimplifyInterfaceError("interface worker did not return a JSON object result")
|
|
914
|
+
return result
|
|
915
|
+
|
|
916
|
+
|
|
825
917
|
def simplify(
|
|
826
918
|
pd_code: PdInput,
|
|
827
919
|
*,
|
|
@@ -829,6 +921,7 @@ def simplify(
|
|
|
829
921
|
ban_heuristic: bool = False,
|
|
830
922
|
reduction_round: int = -1,
|
|
831
923
|
max_thread: int = -1,
|
|
924
|
+
timeout: int = -1,
|
|
832
925
|
verbose: bool = False,
|
|
833
926
|
known_crossingless_components: int = 0,
|
|
834
927
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
@@ -841,6 +934,7 @@ def simplify(
|
|
|
841
934
|
ban_heuristic=ban_heuristic,
|
|
842
935
|
reduction_round=reduction_round,
|
|
843
936
|
max_thread=max_thread,
|
|
937
|
+
timeout=timeout,
|
|
844
938
|
verbose=verbose,
|
|
845
939
|
known_crossingless_components=known_crossingless_components,
|
|
846
940
|
remove_crossings=remove_crossings,
|
|
@@ -854,6 +948,7 @@ def simplify_many(
|
|
|
854
948
|
ban_heuristic: bool = False,
|
|
855
949
|
reduction_round: int = -1,
|
|
856
950
|
max_thread: int = -1,
|
|
951
|
+
timeout: int = -1,
|
|
857
952
|
verbose: bool = False,
|
|
858
953
|
known_crossingless_components: int = 0,
|
|
859
954
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
@@ -867,6 +962,7 @@ def simplify_many(
|
|
|
867
962
|
ban_heuristic=ban_heuristic,
|
|
868
963
|
reduction_round=reduction_round,
|
|
869
964
|
max_thread=max_thread,
|
|
965
|
+
timeout=timeout,
|
|
870
966
|
verbose=verbose,
|
|
871
967
|
known_crossingless_components=known_crossingless_components,
|
|
872
968
|
remove_crossings=remove_crossings,
|
|
@@ -884,6 +980,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
884
980
|
parser.add_argument("--ban-heuristic", action="store_true")
|
|
885
981
|
parser.add_argument("--reduction-round", type=int, default=-1)
|
|
886
982
|
parser.add_argument("--max-thread", type=int, default=-1)
|
|
983
|
+
parser.add_argument("--timeout", type=int, default=-1)
|
|
887
984
|
parser.add_argument("--verbose", action="store_true")
|
|
888
985
|
parser.add_argument("--known-crossingless-components", type=int, default=0)
|
|
889
986
|
parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
|
|
@@ -892,6 +989,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
892
989
|
parser.error("--reduction-round must be -1 or a non-negative integer")
|
|
893
990
|
if args.max_thread < -1 or args.max_thread == 0:
|
|
894
991
|
parser.error("--max-thread must be -1 or a positive integer")
|
|
992
|
+
if args.timeout < -1 or args.timeout == 0:
|
|
993
|
+
parser.error("--timeout must be -1 or a positive integer")
|
|
895
994
|
if args.pd_code and args.pd_code_option:
|
|
896
995
|
parser.error("pass either a positional PD code or --pd-code, not both")
|
|
897
996
|
pd_code_text = args.pd_code_option or args.pd_code
|
|
@@ -916,6 +1015,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
916
1015
|
ban_heuristic=args.ban_heuristic,
|
|
917
1016
|
reduction_round=args.reduction_round,
|
|
918
1017
|
max_thread=args.max_thread,
|
|
1018
|
+
timeout=args.timeout,
|
|
919
1019
|
verbose=args.verbose,
|
|
920
1020
|
known_crossingless_components=args.known_crossingless_components,
|
|
921
1021
|
remove_crossings=remove_crossings,
|
|
@@ -923,6 +1023,15 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
923
1023
|
if show_labels:
|
|
924
1024
|
item = {"label": label, **item}
|
|
925
1025
|
batch_payload.append(item)
|
|
1026
|
+
if item.get("timed_out"):
|
|
1027
|
+
exit_code = 2
|
|
1028
|
+
except KeyboardInterrupt:
|
|
1029
|
+
exit_code = 130
|
|
1030
|
+
item = {"error": "interrupted by Ctrl+C"}
|
|
1031
|
+
if show_labels:
|
|
1032
|
+
item = {"label": label, **item}
|
|
1033
|
+
batch_payload.append(item)
|
|
1034
|
+
break
|
|
926
1035
|
except Exception as exc:
|
|
927
1036
|
exit_code = 2
|
|
928
1037
|
item = {"error": str(exc)}
|
|
@@ -931,16 +1040,26 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
931
1040
|
batch_payload.append(item)
|
|
932
1041
|
payload: Any = batch_payload
|
|
933
1042
|
else:
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1043
|
+
try:
|
|
1044
|
+
payload = simplify(
|
|
1045
|
+
pd_code_text or "",
|
|
1046
|
+
max_paths=args.max_paths,
|
|
1047
|
+
ban_heuristic=args.ban_heuristic,
|
|
1048
|
+
reduction_round=args.reduction_round,
|
|
1049
|
+
max_thread=args.max_thread,
|
|
1050
|
+
timeout=args.timeout,
|
|
1051
|
+
verbose=args.verbose,
|
|
1052
|
+
known_crossingless_components=args.known_crossingless_components,
|
|
1053
|
+
remove_crossings=remove_crossings,
|
|
1054
|
+
)
|
|
1055
|
+
if isinstance(payload, dict) and payload.get("timed_out"):
|
|
1056
|
+
exit_code = 2
|
|
1057
|
+
except KeyboardInterrupt:
|
|
1058
|
+
exit_code = 130
|
|
1059
|
+
payload = {"error": "interrupted by Ctrl+C"}
|
|
1060
|
+
except Exception as exc:
|
|
1061
|
+
exit_code = 2
|
|
1062
|
+
payload = {"error": str(exc)}
|
|
944
1063
|
|
|
945
1064
|
print(json.dumps(payload, indent=2))
|
|
946
1065
|
return exit_code
|
|
@@ -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.12
|
|
4
4
|
Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Author: GGN_2015
|
|
@@ -60,16 +60,23 @@ print(result["final_pd_code"])
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Returned `final_pd_code` strings are normalized for display: each crossing is
|
|
63
|
-
written from the under-incoming edge
|
|
64
|
-
components from `1
|
|
65
|
-
backend keeps its internal
|
|
63
|
+
written from the under-incoming edge, labels are renumbered along oriented
|
|
64
|
+
components from `1`, and crossing rows are sorted lexicographically. This is
|
|
65
|
+
applied only at the final JSON boundary; the C++ backend keeps its internal
|
|
66
|
+
numbering unchanged while simplifying.
|
|
66
67
|
|
|
67
68
|
The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
68
69
|
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
69
70
|
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
70
71
|
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
71
|
-
`
|
|
72
|
-
|
|
72
|
+
`timeout=K` to cap a call at `K` seconds; the default `-1` has no timeout. Use
|
|
73
|
+
`verbose=True` to forward timestamped C++ progress logs to stderr. If a call
|
|
74
|
+
times out, the returned dictionary still contains the best PD code found so far
|
|
75
|
+
and sets `timed_out` to `True`. Verbose log lines use local wall-clock time in
|
|
76
|
+
`YYYY-MM-DD HH:MM:SS` format. When `max_thread=-1` reaches a brute-force search
|
|
77
|
+
phase, verbose logs also include `actual_threads`, the worker count selected by
|
|
78
|
+
the C++ backend for that phase. The backend call runs in a helper process, so
|
|
79
|
+
`Ctrl+C` can terminate active C++ work and its worker threads cleanly.
|
|
73
80
|
|
|
74
81
|
Batch use:
|
|
75
82
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
cpp_pd_code_simplify_interface/__init__.py,sha256=aaNwD--zgQX13xiU1hpuvgXy8vVTFGRk-r5zLm8zIRY,447
|
|
2
|
+
cpp_pd_code_simplify_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
|
|
3
|
+
cpp_pd_code_simplify_interface/_worker.py,sha256=pgvL3zlAZkUVM033YKAT1geXxcI8TxTUXLp83sgrZgw,1477
|
|
4
|
+
cpp_pd_code_simplify_interface/main.py,sha256=QkhR_2Cq9Ep_8hT5qpFKML7bhq99SuQSKO0jePJHp3U,37007
|
|
5
|
+
cpp_pd_code_simplify_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
+
cpp_pd_code_simplify_interface-0.1.12.dist-info/entry_points.txt,sha256=ThorC4Enxp317TKITsXm9pE0gUp7pt-pP6Dl5mEPUR8,91
|
|
7
|
+
cpp_pd_code_simplify_interface-0.1.12.dist-info/METADATA,sha256=LECLw_rBE_IhkBnc3fiaJjlqoIwd5TzW9tC4yb0zJe8,4553
|
|
8
|
+
cpp_pd_code_simplify_interface-0.1.12.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
9
|
+
cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=ZWZVWuSXN9HgCYd88pfURgLqs-3_0xGDi_ZEPZrYUyY,5135
|
|
10
|
+
cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=-X9ztIyCl5y27Yhj2vCyC4bHCeDpFlcKm6ACPMDoxHs,6708
|
|
11
|
+
cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=8XTpwAl5kSGsyZpHUIdWZC2NzGrhIMxp9LDcCqFSM8I,95614
|
|
12
|
+
cpp_pd_code_simplify_interface-0.1.12.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
cpp_pd_code_simplify_interface/__init__.py,sha256=aaNwD--zgQX13xiU1hpuvgXy8vVTFGRk-r5zLm8zIRY,447
|
|
2
|
-
cpp_pd_code_simplify_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
|
|
3
|
-
cpp_pd_code_simplify_interface/main.py,sha256=4t-6xvjQjsuEWP_oiwedGtgrLYu3316uATrl9t4Q8zs,32778
|
|
4
|
-
cpp_pd_code_simplify_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
5
|
-
cpp_pd_code_simplify_interface-0.1.10.dist-info/entry_points.txt,sha256=ThorC4Enxp317TKITsXm9pE0gUp7pt-pP6Dl5mEPUR8,91
|
|
6
|
-
cpp_pd_code_simplify_interface-0.1.10.dist-info/METADATA,sha256=mFNvOlxRrKm62IouC_r5wWyt7KuJ7XqcAdVq39goeDA,4028
|
|
7
|
-
cpp_pd_code_simplify_interface-0.1.10.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
8
|
-
cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=0WflWW2MB2EAK8GbcKKr9SbnoByMV3hFxt4m4mAerzQ,4917
|
|
9
|
-
cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=16HbKJBPA_EGdbyom4sSoQ7lisDBwaKrl7NsphmlA-k,6555
|
|
10
|
-
cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=EUW5kLkhVxurDhdKZ4dhEXr1_7cUZL42ejXqr6vUo2E,91148
|
|
11
|
-
cpp_pd_code_simplify_interface-0.1.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|