cpp-pd-code-simplify-interface 0.1.11__tar.gz → 0.1.13__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.11 → cpp_pd_code_simplify_interface-0.1.13}/PKG-INFO +9 -3
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/README.md +8 -2
- cpp_pd_code_simplify_interface-0.1.13/cpp_pd_code_simplify_interface/_worker.py +47 -0
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +6 -0
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/data/src/native_interface.cpp +4 -1
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +219 -112
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/main.py +130 -11
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/pyproject.toml +1 -1
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/build_backend/cpp_pd_code_simplify_interface_build_backend.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/__init__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/cpp_pd_code_simplify_interface/__main__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.11 → cpp_pd_code_simplify_interface-0.1.13}/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.13
|
|
4
4
|
Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Author: GGN_2015
|
|
@@ -69,8 +69,14 @@ The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
|
69
69
|
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
70
70
|
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
71
71
|
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
72
|
-
`
|
|
73
|
-
|
|
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.
|
|
74
80
|
|
|
75
81
|
Batch use:
|
|
76
82
|
|
|
@@ -49,8 +49,14 @@ The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
|
49
49
|
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
50
50
|
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
51
51
|
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
52
|
-
`
|
|
53
|
-
|
|
52
|
+
`timeout=K` to cap a call at `K` seconds; the default `-1` has no timeout. Use
|
|
53
|
+
`verbose=True` to forward timestamped C++ progress logs to stderr. If a call
|
|
54
|
+
times out, the returned dictionary still contains the best PD code found so far
|
|
55
|
+
and sets `timed_out` to `True`. Verbose log lines use local wall-clock time in
|
|
56
|
+
`YYYY-MM-DD HH:MM:SS` format. When `max_thread=-1` reaches a brute-force search
|
|
57
|
+
phase, verbose logs also include `actual_threads`, the worker count selected by
|
|
58
|
+
the C++ backend for that phase. The backend call runs in a helper process, so
|
|
59
|
+
`Ctrl+C` can terminate active C++ work and its worker threads cleanly.
|
|
54
60
|
|
|
55
61
|
Batch use:
|
|
56
62
|
|
|
@@ -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>
|
|
@@ -987,7 +988,10 @@ std::set<int> normalized_removed_crossings(const PDCode& code, const std::vector
|
|
|
987
988
|
std::vector<int> heuristic_distances_to_target(
|
|
988
989
|
const DualGraph& graph,
|
|
989
990
|
int target,
|
|
990
|
-
int cutoff
|
|
991
|
+
int cutoff,
|
|
992
|
+
const SimplifierOptions& options);
|
|
993
|
+
|
|
994
|
+
void check_timeout(const SimplifierOptions& options);
|
|
991
995
|
|
|
992
996
|
void collect_simple_paths_dfs(
|
|
993
997
|
const DualGraph& graph,
|
|
@@ -999,7 +1003,9 @@ void collect_simple_paths_dfs(
|
|
|
999
1003
|
const std::vector<int>& distance,
|
|
1000
1004
|
std::vector<char>& visited,
|
|
1001
1005
|
std::vector<int>& current_path,
|
|
1002
|
-
std::vector<std::vector<int>>& paths
|
|
1006
|
+
std::vector<std::vector<int>>& paths,
|
|
1007
|
+
const SimplifierOptions& options) {
|
|
1008
|
+
check_timeout(options);
|
|
1003
1009
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1004
1010
|
if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
|
|
1005
1011
|
return;
|
|
@@ -1045,7 +1051,8 @@ void collect_simple_paths_dfs(
|
|
|
1045
1051
|
distance,
|
|
1046
1052
|
visited,
|
|
1047
1053
|
current_path,
|
|
1048
|
-
paths
|
|
1054
|
+
paths,
|
|
1055
|
+
options);
|
|
1049
1056
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1050
1057
|
visited[next] = false;
|
|
1051
1058
|
current_path.pop_back();
|
|
@@ -1063,18 +1070,24 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1063
1070
|
int source,
|
|
1064
1071
|
int target,
|
|
1065
1072
|
int cutoff,
|
|
1066
|
-
int max_paths
|
|
1073
|
+
int max_paths,
|
|
1074
|
+
const SimplifierOptions& options) {
|
|
1075
|
+
check_timeout(options);
|
|
1067
1076
|
std::vector<std::vector<int>> paths;
|
|
1068
|
-
if (source
|
|
1077
|
+
if (source < 0 || target < 0 ||
|
|
1069
1078
|
source >= static_cast<int>(graph.faces.size()) ||
|
|
1070
1079
|
target >= static_cast<int>(graph.faces.size()) ||
|
|
1071
1080
|
cutoff <= 0) {
|
|
1072
1081
|
return paths;
|
|
1073
1082
|
}
|
|
1083
|
+
if (source == target) {
|
|
1084
|
+
paths.push_back(std::vector<int>{source});
|
|
1085
|
+
return paths;
|
|
1086
|
+
}
|
|
1074
1087
|
|
|
1075
1088
|
std::vector<char> visited(graph.faces.size(), false);
|
|
1076
1089
|
std::vector<int> current_path{source};
|
|
1077
|
-
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1090
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
|
|
1078
1091
|
visited[source] = true;
|
|
1079
1092
|
collect_simple_paths_dfs(
|
|
1080
1093
|
graph,
|
|
@@ -1086,14 +1099,16 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1086
1099
|
distance,
|
|
1087
1100
|
visited,
|
|
1088
1101
|
current_path,
|
|
1089
|
-
paths
|
|
1102
|
+
paths,
|
|
1103
|
+
options);
|
|
1090
1104
|
return paths;
|
|
1091
1105
|
}
|
|
1092
1106
|
|
|
1093
1107
|
std::vector<int> heuristic_distances_to_target(
|
|
1094
1108
|
const DualGraph& graph,
|
|
1095
1109
|
int target,
|
|
1096
|
-
int cutoff
|
|
1110
|
+
int cutoff,
|
|
1111
|
+
const SimplifierOptions& options) {
|
|
1097
1112
|
const int face_count = static_cast<int>(graph.faces.size());
|
|
1098
1113
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1099
1114
|
std::vector<int> distance(face_count, infinity);
|
|
@@ -1102,6 +1117,7 @@ std::vector<int> heuristic_distances_to_target(
|
|
|
1102
1117
|
queue.push_back(target);
|
|
1103
1118
|
|
|
1104
1119
|
while (!queue.empty()) {
|
|
1120
|
+
check_timeout(options);
|
|
1105
1121
|
const int current = queue.front();
|
|
1106
1122
|
queue.pop_front();
|
|
1107
1123
|
for (int edge_index : graph.adjacency[current]) {
|
|
@@ -1179,15 +1195,21 @@ std::vector<std::vector<int>> collect_heuristic_paths(
|
|
|
1179
1195
|
const DualGraph& graph,
|
|
1180
1196
|
int source,
|
|
1181
1197
|
int target,
|
|
1182
|
-
int cutoff
|
|
1198
|
+
int cutoff,
|
|
1199
|
+
const SimplifierOptions& options) {
|
|
1200
|
+
check_timeout(options);
|
|
1183
1201
|
std::vector<std::vector<int>> paths;
|
|
1184
1202
|
const int face_count = static_cast<int>(graph.faces.size());
|
|
1185
|
-
if (source
|
|
1203
|
+
if (source < 0 || target < 0 ||
|
|
1186
1204
|
source >= face_count || target >= face_count || cutoff <= 0) {
|
|
1187
1205
|
return paths;
|
|
1188
1206
|
}
|
|
1207
|
+
if (source == target) {
|
|
1208
|
+
paths.push_back(std::vector<int>{source});
|
|
1209
|
+
return paths;
|
|
1210
|
+
}
|
|
1189
1211
|
|
|
1190
|
-
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1212
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
|
|
1191
1213
|
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1192
1214
|
if (distance[source] == infinity || distance[source] >= cutoff) {
|
|
1193
1215
|
return paths;
|
|
@@ -1215,6 +1237,7 @@ std::vector<std::vector<int>> collect_heuristic_paths(
|
|
|
1215
1237
|
int popped_states = 0;
|
|
1216
1238
|
while (!queue.empty() && popped_states < state_budget &&
|
|
1217
1239
|
static_cast<int>(paths.size()) < path_budget) {
|
|
1240
|
+
check_timeout(options);
|
|
1218
1241
|
HeuristicState state = queue.top();
|
|
1219
1242
|
queue.pop();
|
|
1220
1243
|
++popped_states;
|
|
@@ -1823,6 +1846,41 @@ std::string search_mode_for_options(const SimplifierOptions& options) {
|
|
|
1823
1846
|
return "bounded";
|
|
1824
1847
|
}
|
|
1825
1848
|
|
|
1849
|
+
void validate_timeout_options(const SimplifierOptions& options) {
|
|
1850
|
+
if (options.timeout_seconds < -1 || options.timeout_seconds == 0) {
|
|
1851
|
+
throw std::invalid_argument("timeout must be -1 or a positive integer");
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
class TimeoutError : public std::runtime_error {
|
|
1856
|
+
public:
|
|
1857
|
+
explicit TimeoutError(const std::string& message) : std::runtime_error(message) {}
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1860
|
+
SimplifierOptions with_timeout_deadline(SimplifierOptions options) {
|
|
1861
|
+
validate_timeout_options(options);
|
|
1862
|
+
if (options.timeout_seconds > 0 && !options.has_timeout_deadline) {
|
|
1863
|
+
options.has_timeout_deadline = true;
|
|
1864
|
+
options.timeout_deadline =
|
|
1865
|
+
std::chrono::steady_clock::now() + std::chrono::seconds(options.timeout_seconds);
|
|
1866
|
+
}
|
|
1867
|
+
return options;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
void check_timeout(const SimplifierOptions& options) {
|
|
1871
|
+
if (options.should_cancel && options.should_cancel()) {
|
|
1872
|
+
throw std::runtime_error("interrupted by Ctrl+C");
|
|
1873
|
+
}
|
|
1874
|
+
if (!options.has_timeout_deadline) {
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
if (std::chrono::steady_clock::now() >= options.timeout_deadline) {
|
|
1878
|
+
std::ostringstream message;
|
|
1879
|
+
message << "timeout after " << options.timeout_seconds << " seconds";
|
|
1880
|
+
throw TimeoutError(message.str());
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1826
1884
|
} // namespace
|
|
1827
1885
|
|
|
1828
1886
|
PDCode parse_pd_code(const std::string& text) {
|
|
@@ -2213,6 +2271,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2213
2271
|
const std::string& path_search_mode,
|
|
2214
2272
|
int red_index,
|
|
2215
2273
|
const std::atomic<int>* best_found_index) {
|
|
2274
|
+
check_timeout(options);
|
|
2216
2275
|
RedPathSearchOutcome outcome;
|
|
2217
2276
|
outcome.witness.path_search_mode = path_search_mode;
|
|
2218
2277
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
@@ -2231,6 +2290,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2231
2290
|
const std::array<int, 2> destinations{end_face, end_opposite_face};
|
|
2232
2291
|
|
|
2233
2292
|
for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
|
|
2293
|
+
check_timeout(options);
|
|
2234
2294
|
const Endpoint endpoint = red_path[i];
|
|
2235
2295
|
const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
|
|
2236
2296
|
const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
|
|
@@ -2243,15 +2303,17 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2243
2303
|
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
2244
2304
|
for (int source : sources) {
|
|
2245
2305
|
for (int destination : destinations) {
|
|
2306
|
+
check_timeout(options);
|
|
2246
2307
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2247
2308
|
outcome.skipped = true;
|
|
2248
2309
|
return outcome;
|
|
2249
2310
|
}
|
|
2250
2311
|
std::vector<std::vector<int>> found_paths;
|
|
2251
2312
|
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2252
|
-
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2313
|
+
found_paths = collect_heuristic_paths(graph, source, destination, cutoff, options);
|
|
2253
2314
|
} else {
|
|
2254
|
-
found_paths = collect_simple_paths(
|
|
2315
|
+
found_paths = collect_simple_paths(
|
|
2316
|
+
graph, source, destination, cutoff, options.max_paths, options);
|
|
2255
2317
|
}
|
|
2256
2318
|
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
2257
2319
|
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
@@ -2261,6 +2323,7 @@ RedPathSearchOutcome search_single_red_path(
|
|
|
2261
2323
|
}
|
|
2262
2324
|
|
|
2263
2325
|
for (const auto& green_path : paths) {
|
|
2326
|
+
check_timeout(options);
|
|
2264
2327
|
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2265
2328
|
outcome.skipped = true;
|
|
2266
2329
|
return outcome;
|
|
@@ -2401,36 +2464,49 @@ SimplificationResult find_simplification_parallel_bruteforce(
|
|
|
2401
2464
|
SimplificationResult find_simplification(
|
|
2402
2465
|
const PDCode& code,
|
|
2403
2466
|
const SimplifierOptions& options) {
|
|
2467
|
+
const SimplifierOptions run_options = with_timeout_deadline(options);
|
|
2468
|
+
check_timeout(run_options);
|
|
2404
2469
|
SimplificationResult result;
|
|
2405
|
-
result.path_search_mode = search_mode_for_options(
|
|
2470
|
+
result.path_search_mode = search_mode_for_options(run_options);
|
|
2406
2471
|
Diagram diagram(code);
|
|
2472
|
+
check_timeout(run_options);
|
|
2407
2473
|
DualGraph base_graph(diagram);
|
|
2474
|
+
check_timeout(run_options);
|
|
2408
2475
|
const auto red_lines = possible_red_lines(diagram);
|
|
2409
|
-
|
|
2476
|
+
check_timeout(run_options);
|
|
2477
|
+
const bool brute_force_mode = run_options.max_paths == -1 && run_options.ban_heuristic;
|
|
2410
2478
|
const int worker_count = brute_force_mode
|
|
2411
2479
|
? selected_bruteforce_worker_count(
|
|
2412
|
-
|
|
2480
|
+
run_options.max_threads,
|
|
2413
2481
|
static_cast<int>(red_lines.size()))
|
|
2414
2482
|
: 1;
|
|
2483
|
+
if (brute_force_mode && run_options.max_threads == -1) {
|
|
2484
|
+
std::ostringstream message;
|
|
2485
|
+
message << "bruteforce_threads max_thread=-1"
|
|
2486
|
+
<< " actual_threads=" << worker_count
|
|
2487
|
+
<< " red_paths=" << red_lines.size();
|
|
2488
|
+
emit_progress(run_options, message.str());
|
|
2489
|
+
}
|
|
2415
2490
|
if (worker_count > 1) {
|
|
2416
2491
|
return find_simplification_parallel_bruteforce(
|
|
2417
2492
|
code,
|
|
2418
2493
|
diagram,
|
|
2419
2494
|
base_graph,
|
|
2420
2495
|
red_lines,
|
|
2421
|
-
|
|
2496
|
+
run_options,
|
|
2422
2497
|
result.path_search_mode,
|
|
2423
2498
|
worker_count);
|
|
2424
2499
|
}
|
|
2425
2500
|
|
|
2426
2501
|
for (const auto& red_path : red_lines) {
|
|
2502
|
+
check_timeout(run_options);
|
|
2427
2503
|
++result.tested_red_paths;
|
|
2428
2504
|
const RedPathSearchOutcome outcome = search_single_red_path(
|
|
2429
2505
|
code,
|
|
2430
2506
|
diagram,
|
|
2431
2507
|
base_graph,
|
|
2432
2508
|
red_path,
|
|
2433
|
-
|
|
2509
|
+
run_options,
|
|
2434
2510
|
result.path_search_mode,
|
|
2435
2511
|
-1,
|
|
2436
2512
|
nullptr);
|
|
@@ -2452,125 +2528,155 @@ ReductionResult reduce_pd_code(
|
|
|
2452
2528
|
std::size_t known_crossingless_components,
|
|
2453
2529
|
const SimplifierOptions& options,
|
|
2454
2530
|
int reduction_round) {
|
|
2455
|
-
|
|
2456
|
-
std::ostringstream message;
|
|
2457
|
-
message << "start input_crossings=" << code.size()
|
|
2458
|
-
<< " known_crossingless_components=" << known_crossingless_components
|
|
2459
|
-
<< " reduction_round=" << reduction_round
|
|
2460
|
-
<< " max_paths=" << options.max_paths
|
|
2461
|
-
<< " max_thread=" << options.max_threads
|
|
2462
|
-
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2463
|
-
emit_progress(options, message.str());
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
|
|
2531
|
+
const SimplifierOptions run_options = with_timeout_deadline(options);
|
|
2467
2532
|
ReductionResult output;
|
|
2468
|
-
output.code =
|
|
2469
|
-
output.crossingless_components =
|
|
2470
|
-
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2471
|
-
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2472
|
-
{
|
|
2473
|
-
std::ostringstream message;
|
|
2474
|
-
message << "pre_simplify input_crossings=" << code.size()
|
|
2475
|
-
<< " output_crossings=" << output.code.size()
|
|
2476
|
-
<< " crossingless_components=" << output.crossingless_components
|
|
2477
|
-
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2478
|
-
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2479
|
-
emit_progress(options, message.str());
|
|
2480
|
-
}
|
|
2533
|
+
output.code = code;
|
|
2534
|
+
output.crossingless_components = known_crossingless_components;
|
|
2481
2535
|
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
search_options.require_applicable = true;
|
|
2485
|
-
const int round = output.mid_simplification_rounds + 1;
|
|
2536
|
+
try {
|
|
2537
|
+
check_timeout(run_options);
|
|
2486
2538
|
{
|
|
2487
2539
|
std::ostringstream message;
|
|
2488
|
-
message << "
|
|
2489
|
-
<< "
|
|
2490
|
-
<< "
|
|
2491
|
-
<< "
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2540
|
+
message << "start input_crossings=" << code.size()
|
|
2541
|
+
<< " known_crossingless_components=" << known_crossingless_components
|
|
2542
|
+
<< " reduction_round=" << reduction_round
|
|
2543
|
+
<< " max_paths=" << run_options.max_paths
|
|
2544
|
+
<< " max_thread=" << run_options.max_threads
|
|
2545
|
+
<< " timeout=" << run_options.timeout_seconds
|
|
2546
|
+
<< " heuristic=" << (run_options.ban_heuristic ? "off" : "on");
|
|
2547
|
+
emit_progress(run_options, message.str());
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
const PDSimplificationResult prepared =
|
|
2551
|
+
simplify_pd_code(code, known_crossingless_components);
|
|
2552
|
+
output.code = prepared.code;
|
|
2553
|
+
output.crossingless_components = prepared.crossingless_components;
|
|
2554
|
+
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2555
|
+
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2556
|
+
check_timeout(run_options);
|
|
2498
2557
|
{
|
|
2499
2558
|
std::ostringstream message;
|
|
2500
|
-
message << "
|
|
2501
|
-
<< "
|
|
2502
|
-
<< "
|
|
2503
|
-
<< "
|
|
2504
|
-
<< "
|
|
2505
|
-
emit_progress(
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
SimplifierOptions
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2559
|
+
message << "pre_simplify input_crossings=" << code.size()
|
|
2560
|
+
<< " output_crossings=" << output.code.size()
|
|
2561
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2562
|
+
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2563
|
+
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2564
|
+
emit_progress(run_options, message.str());
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
|
|
2568
|
+
check_timeout(run_options);
|
|
2569
|
+
SimplifierOptions search_options = run_options;
|
|
2570
|
+
search_options.require_applicable = true;
|
|
2571
|
+
output.last_path_search_mode = search_mode_for_options(search_options);
|
|
2572
|
+
const int round = output.mid_simplification_rounds + 1;
|
|
2514
2573
|
{
|
|
2515
2574
|
std::ostringstream message;
|
|
2516
2575
|
message << "round " << round
|
|
2517
|
-
<< "
|
|
2518
|
-
<< "
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
output.
|
|
2523
|
-
output.
|
|
2524
|
-
output.
|
|
2576
|
+
<< " search_start crossings=" << output.code.size()
|
|
2577
|
+
<< " mode=" << output.last_path_search_mode
|
|
2578
|
+
<< " max_thread=" << search_options.max_threads;
|
|
2579
|
+
emit_progress(run_options, message.str());
|
|
2580
|
+
}
|
|
2581
|
+
SimplificationResult search = find_simplification(output.code, search_options);
|
|
2582
|
+
output.tested_red_paths += search.tested_red_paths;
|
|
2583
|
+
output.tested_green_paths += search.tested_green_paths;
|
|
2584
|
+
output.last_path_search_mode = search.path_search_mode;
|
|
2525
2585
|
{
|
|
2526
2586
|
std::ostringstream message;
|
|
2527
2587
|
message << "round " << round
|
|
2528
|
-
<< "
|
|
2529
|
-
<< "
|
|
2530
|
-
<< "
|
|
2531
|
-
|
|
2588
|
+
<< " search_done found=" << (search.found ? "yes" : "no")
|
|
2589
|
+
<< " mode=" << search.path_search_mode
|
|
2590
|
+
<< " tested_red=" << search.tested_red_paths
|
|
2591
|
+
<< " tested_green=" << search.tested_green_paths;
|
|
2592
|
+
emit_progress(run_options, message.str());
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
if (!search.found && reduction_round < 0 &&
|
|
2596
|
+
run_options.max_paths == -1 && !run_options.ban_heuristic) {
|
|
2597
|
+
SimplifierOptions brute_options = run_options;
|
|
2598
|
+
brute_options.max_paths = -1;
|
|
2599
|
+
brute_options.ban_heuristic = true;
|
|
2600
|
+
brute_options.require_applicable = true;
|
|
2601
|
+
output.last_path_search_mode = search_mode_for_options(brute_options);
|
|
2602
|
+
{
|
|
2603
|
+
std::ostringstream message;
|
|
2604
|
+
message << "round " << round
|
|
2605
|
+
<< " brute_fallback_start crossings=" << output.code.size()
|
|
2606
|
+
<< " max_thread=" << brute_options.max_threads;
|
|
2607
|
+
emit_progress(run_options, message.str());
|
|
2608
|
+
}
|
|
2609
|
+
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
2610
|
+
output.tested_red_paths += brute.tested_red_paths;
|
|
2611
|
+
output.tested_green_paths += brute.tested_green_paths;
|
|
2612
|
+
output.last_path_search_mode = brute.path_search_mode;
|
|
2613
|
+
{
|
|
2614
|
+
std::ostringstream message;
|
|
2615
|
+
message << "round " << round
|
|
2616
|
+
<< " brute_fallback_done found=" << (brute.found ? "yes" : "no")
|
|
2617
|
+
<< " tested_red=" << brute.tested_red_paths
|
|
2618
|
+
<< " tested_green=" << brute.tested_green_paths;
|
|
2619
|
+
emit_progress(run_options, message.str());
|
|
2620
|
+
}
|
|
2621
|
+
if (brute.found) {
|
|
2622
|
+
++output.heuristic_failover_rounds;
|
|
2623
|
+
search = std::move(brute);
|
|
2624
|
+
}
|
|
2532
2625
|
}
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2626
|
+
|
|
2627
|
+
if (!search.found) {
|
|
2628
|
+
{
|
|
2629
|
+
std::ostringstream message;
|
|
2630
|
+
message << "round " << round
|
|
2631
|
+
<< " stop_no_path crossings=" << output.code.size();
|
|
2632
|
+
emit_progress(run_options, message.str());
|
|
2633
|
+
}
|
|
2634
|
+
break;
|
|
2536
2635
|
}
|
|
2537
|
-
}
|
|
2538
2636
|
|
|
2539
|
-
|
|
2637
|
+
const std::size_t before_apply_crossings = output.code.size();
|
|
2638
|
+
check_timeout(run_options);
|
|
2639
|
+
const MidSimplificationApplyResult applied =
|
|
2640
|
+
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2641
|
+
++output.mid_simplification_rounds;
|
|
2642
|
+
output.code = applied.code;
|
|
2643
|
+
output.crossingless_components = applied.crossingless_components;
|
|
2644
|
+
check_timeout(run_options);
|
|
2645
|
+
const PDSimplificationResult simplified =
|
|
2646
|
+
simplify_pd_code(output.code, output.crossingless_components);
|
|
2647
|
+
output.code = simplified.code;
|
|
2648
|
+
output.crossingless_components = simplified.crossingless_components;
|
|
2649
|
+
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2650
|
+
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2651
|
+
check_timeout(run_options);
|
|
2540
2652
|
{
|
|
2541
2653
|
std::ostringstream message;
|
|
2542
2654
|
message << "round " << round
|
|
2543
|
-
<< "
|
|
2544
|
-
|
|
2655
|
+
<< " applied crossings=" << before_apply_crossings
|
|
2656
|
+
<< " -> " << applied.code.size()
|
|
2657
|
+
<< " -> " << output.code.size()
|
|
2658
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2659
|
+
<< " r1_moves=" << simplified.reidemeister_i_moves
|
|
2660
|
+
<< " nugatory_moves=" << simplified.nugatory_crossing_moves;
|
|
2661
|
+
emit_progress(run_options, message.str());
|
|
2545
2662
|
}
|
|
2546
|
-
break;
|
|
2547
2663
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
const MidSimplificationApplyResult applied =
|
|
2551
|
-
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2552
|
-
++output.mid_simplification_rounds;
|
|
2553
|
-
const PDSimplificationResult simplified =
|
|
2554
|
-
simplify_pd_code(applied.code, applied.crossingless_components);
|
|
2555
|
-
output.code = simplified.code;
|
|
2556
|
-
output.crossingless_components = simplified.crossingless_components;
|
|
2557
|
-
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2558
|
-
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2664
|
+
} catch (const TimeoutError& error) {
|
|
2665
|
+
output.timed_out = true;
|
|
2559
2666
|
{
|
|
2560
2667
|
std::ostringstream message;
|
|
2561
|
-
message <<
|
|
2562
|
-
<< "
|
|
2563
|
-
<< " -> " << applied.code.size()
|
|
2564
|
-
<< " -> " << output.code.size()
|
|
2668
|
+
message << error.what()
|
|
2669
|
+
<< "; returning_current_best crossings=" << output.code.size()
|
|
2565
2670
|
<< " crossingless_components=" << output.crossingless_components
|
|
2566
|
-
<< "
|
|
2567
|
-
|
|
2568
|
-
emit_progress(options, message.str());
|
|
2671
|
+
<< " mid_rounds=" << output.mid_simplification_rounds;
|
|
2672
|
+
emit_progress(run_options, message.str());
|
|
2569
2673
|
}
|
|
2570
2674
|
}
|
|
2571
2675
|
|
|
2572
2676
|
output.stopped_by_round_limit =
|
|
2573
|
-
|
|
2677
|
+
!output.timed_out &&
|
|
2678
|
+
reduction_round >= 0 &&
|
|
2679
|
+
output.mid_simplification_rounds >= reduction_round;
|
|
2574
2680
|
{
|
|
2575
2681
|
std::ostringstream message;
|
|
2576
2682
|
message << "done final_crossings=" << output.code.size()
|
|
@@ -2578,8 +2684,9 @@ ReductionResult reduce_pd_code(
|
|
|
2578
2684
|
<< " mid_rounds=" << output.mid_simplification_rounds
|
|
2579
2685
|
<< " heuristic_failover_rounds=" << output.heuristic_failover_rounds
|
|
2580
2686
|
<< " stopped_by_round_limit="
|
|
2581
|
-
<< (output.stopped_by_round_limit ? "yes" : "no")
|
|
2582
|
-
|
|
2687
|
+
<< (output.stopped_by_round_limit ? "yes" : "no")
|
|
2688
|
+
<< " timed_out=" << (output.timed_out ? "yes" : "no");
|
|
2689
|
+
emit_progress(run_options, message.str());
|
|
2583
2690
|
}
|
|
2584
2691
|
return output;
|
|
2585
2692
|
}
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|