cpp-pd-code-simplify-interface 0.1.2__tar.gz → 0.1.3__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.2 → cpp_pd_code_simplify_interface-0.1.3}/PKG-INFO +7 -5
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/README.md +6 -4
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +33 -0
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/data/src/native_interface.cpp +30 -54
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +476 -22
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/main.py +33 -5
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/pyproject.toml +1 -1
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/build_backend/cpp_pd_code_simplify_interface_build_backend.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/__init__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/cpp_pd_code_simplify_interface/__main__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.2 → cpp_pd_code_simplify_interface-0.1.3}/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.3
|
|
4
4
|
Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Author: GGN_2015
|
|
@@ -35,7 +35,7 @@ use, the package compiles a cached local dynamic library through
|
|
|
35
35
|
compiler compatible with `g++` must be available at runtime.
|
|
36
36
|
|
|
37
37
|
Calls use the C++ library's default preprocessing pipeline: R1-move removal
|
|
38
|
-
followed by nugatory-crossing removal.
|
|
38
|
+
followed by nugatory-crossing removal, then iterative mid-simplification.
|
|
39
39
|
|
|
40
40
|
## Example
|
|
41
41
|
|
|
@@ -44,12 +44,14 @@ import cpp_pd_code_simplify_interface as simplify
|
|
|
44
44
|
|
|
45
45
|
pd_code = "PD[X[1,5,2,4],X[3,1,4,6],X[5,3,6,2]]"
|
|
46
46
|
result = simplify.simplify(pd_code)
|
|
47
|
-
print(result["
|
|
47
|
+
print(result["final_pd_code"])
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
51
51
|
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
52
|
-
enumeration for a manageable input.
|
|
52
|
+
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
53
|
+
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
54
|
+
`verbose=True` to forward C++ progress logs to stderr.
|
|
53
55
|
|
|
54
56
|
Batch use:
|
|
55
57
|
|
|
@@ -76,7 +78,7 @@ Python process needs a 64-bit compiler target.
|
|
|
76
78
|
Command-line use also supports multi-line PD-code files:
|
|
77
79
|
|
|
78
80
|
```sh
|
|
79
|
-
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
|
|
81
|
+
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1 --verbose
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
## Build And Publish
|
|
@@ -15,7 +15,7 @@ use, the package compiles a cached local dynamic library through
|
|
|
15
15
|
compiler compatible with `g++` must be available at runtime.
|
|
16
16
|
|
|
17
17
|
Calls use the C++ library's default preprocessing pipeline: R1-move removal
|
|
18
|
-
followed by nugatory-crossing removal.
|
|
18
|
+
followed by nugatory-crossing removal, then iterative mid-simplification.
|
|
19
19
|
|
|
20
20
|
## Example
|
|
21
21
|
|
|
@@ -24,12 +24,14 @@ import cpp_pd_code_simplify_interface as simplify
|
|
|
24
24
|
|
|
25
25
|
pd_code = "PD[X[1,5,2,4],X[3,1,4,6],X[5,3,6,2]]"
|
|
26
26
|
result = simplify.simplify(pd_code)
|
|
27
|
-
print(result["
|
|
27
|
+
print(result["final_pd_code"])
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
The default `max_paths=-1` uses deterministic heuristic green-path sampling in
|
|
31
31
|
the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
|
|
32
|
-
enumeration for a manageable input.
|
|
32
|
+
enumeration for a manageable input. Use `reduction_round=K` to cap applied
|
|
33
|
+
mid-simplification rounds; the default `-1` runs until stable. Use
|
|
34
|
+
`verbose=True` to forward C++ progress logs to stderr.
|
|
33
35
|
|
|
34
36
|
Batch use:
|
|
35
37
|
|
|
@@ -56,7 +58,7 @@ Python process needs a 64-bit compiler target.
|
|
|
56
58
|
Command-line use also supports multi-line PD-code files:
|
|
57
59
|
|
|
58
60
|
```sh
|
|
59
|
-
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
|
|
61
|
+
python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1 --verbose
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
## Build And Publish
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <array>
|
|
4
4
|
#include <cstddef>
|
|
5
|
+
#include <functional>
|
|
5
6
|
#include <iosfwd>
|
|
6
7
|
#include <string>
|
|
7
8
|
#include <vector>
|
|
@@ -50,6 +51,9 @@ struct GreenCrossing {
|
|
|
50
51
|
struct SimplifierOptions {
|
|
51
52
|
int max_paths = -1;
|
|
52
53
|
bool ban_heuristic = false;
|
|
54
|
+
bool require_applicable = false;
|
|
55
|
+
bool verbose = false;
|
|
56
|
+
std::function<void(const std::string&)> progress;
|
|
53
57
|
};
|
|
54
58
|
|
|
55
59
|
struct LinkComponentSummary {
|
|
@@ -107,6 +111,24 @@ struct SimplificationResult {
|
|
|
107
111
|
std::size_t tested_green_paths = 0;
|
|
108
112
|
};
|
|
109
113
|
|
|
114
|
+
struct MidSimplificationApplyResult {
|
|
115
|
+
PDCode code;
|
|
116
|
+
std::size_t crossingless_components = 0;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
struct ReductionResult {
|
|
120
|
+
PDCode code;
|
|
121
|
+
std::size_t crossingless_components = 0;
|
|
122
|
+
int mid_simplification_rounds = 0;
|
|
123
|
+
int heuristic_failover_rounds = 0;
|
|
124
|
+
int reidemeister_i_moves = 0;
|
|
125
|
+
int nugatory_crossing_moves = 0;
|
|
126
|
+
std::size_t tested_red_paths = 0;
|
|
127
|
+
std::size_t tested_green_paths = 0;
|
|
128
|
+
std::string last_path_search_mode;
|
|
129
|
+
bool stopped_by_round_limit = false;
|
|
130
|
+
};
|
|
131
|
+
|
|
110
132
|
PDCODE_SIMPLIFY_API PDCode parse_pd_code(const std::string& text);
|
|
111
133
|
PDCODE_SIMPLIFY_API std::string format_pd_code(const PDCode& code);
|
|
112
134
|
PDCODE_SIMPLIFY_API std::string format_endpoint(const Endpoint& endpoint);
|
|
@@ -142,6 +164,17 @@ PDCODE_SIMPLIFY_API SimplificationResult find_simplification(
|
|
|
142
164
|
const PDCode& code,
|
|
143
165
|
const SimplifierOptions& options = SimplifierOptions{});
|
|
144
166
|
|
|
167
|
+
PDCODE_SIMPLIFY_API MidSimplificationApplyResult apply_simplification_witness(
|
|
168
|
+
const PDCode& code,
|
|
169
|
+
const SimplificationResult& result,
|
|
170
|
+
std::size_t known_crossingless_components = 0);
|
|
171
|
+
|
|
172
|
+
PDCODE_SIMPLIFY_API ReductionResult reduce_pd_code(
|
|
173
|
+
const PDCode& code,
|
|
174
|
+
std::size_t known_crossingless_components = 0,
|
|
175
|
+
const SimplifierOptions& options = SimplifierOptions{},
|
|
176
|
+
int reduction_round = -1);
|
|
177
|
+
|
|
145
178
|
PDCODE_SIMPLIFY_API std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint);
|
|
146
179
|
|
|
147
180
|
} // namespace pdcode_simplify
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include <cstdlib>
|
|
4
4
|
#include <cstring>
|
|
5
5
|
#include <exception>
|
|
6
|
+
#include <iostream>
|
|
6
7
|
#include <sstream>
|
|
7
8
|
#include <string>
|
|
8
9
|
#include <vector>
|
|
@@ -55,14 +56,14 @@ void append_component_counts(
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
std::string result_to_json(
|
|
58
|
-
const pdcode_simplify::
|
|
59
|
+
const pdcode_simplify::ReductionResult& result,
|
|
59
60
|
const pdcode_simplify::ComponentAnalysis& input_components,
|
|
60
|
-
const pdcode_simplify::ComponentAnalysis
|
|
61
|
-
const pdcode_simplify::
|
|
62
|
-
const pdcode_simplify::ComponentAnalysis& search_components) {
|
|
61
|
+
const pdcode_simplify::ComponentAnalysis& final_components,
|
|
62
|
+
const pdcode_simplify::ComponentAnalysis* after_removal_components) {
|
|
63
63
|
std::ostringstream out;
|
|
64
64
|
out << "{";
|
|
65
|
-
out << "\"simplification_found\":"
|
|
65
|
+
out << "\"simplification_found\":"
|
|
66
|
+
<< (result.mid_simplification_rounds > 0 ? "true" : "false") << ",";
|
|
66
67
|
out << "\"input_components\":{";
|
|
67
68
|
append_component_counts(out, input_components);
|
|
68
69
|
out << "},";
|
|
@@ -71,50 +72,21 @@ std::string result_to_json(
|
|
|
71
72
|
append_component_counts(out, *after_removal_components);
|
|
72
73
|
out << "},";
|
|
73
74
|
}
|
|
74
|
-
out << "\"
|
|
75
|
-
<< "\"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<< "},";
|
|
80
|
-
out << "\"search_components\":{";
|
|
81
|
-
append_component_counts(out, search_components);
|
|
75
|
+
out << "\"final_pd_code\":\"" << json_escape(pdcode_simplify::format_pd_code(result.code))
|
|
76
|
+
<< "\",";
|
|
77
|
+
out << "\"final_crossings\":" << result.code.size() << ",";
|
|
78
|
+
out << "\"final_components\":{";
|
|
79
|
+
append_component_counts(out, final_components);
|
|
82
80
|
out << "},";
|
|
81
|
+
out << "\"mid_simplification_rounds\":" << result.mid_simplification_rounds << ",";
|
|
82
|
+
out << "\"heuristic_failover_rounds\":" << result.heuristic_failover_rounds << ",";
|
|
83
|
+
out << "\"reidemeister_i_moves\":" << result.reidemeister_i_moves << ",";
|
|
84
|
+
out << "\"nugatory_crossing_moves\":" << result.nugatory_crossing_moves << ",";
|
|
83
85
|
out << "\"tested_red_paths\":" << result.tested_red_paths << ",";
|
|
84
86
|
out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
|
|
85
|
-
out << "\"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
out << "\"direction\":\"" << pdcode_simplify::format_direction(result.direction) << "\",";
|
|
89
|
-
out << "\"red_path\":[";
|
|
90
|
-
for (std::size_t i = 0; i < result.red_path.size(); ++i) {
|
|
91
|
-
if (i != 0) {
|
|
92
|
-
out << ",";
|
|
93
|
-
}
|
|
94
|
-
out << "{\"crossing\":" << result.red_path[i].crossing
|
|
95
|
-
<< ",\"strand\":" << result.red_path[i].strand << "}";
|
|
96
|
-
}
|
|
97
|
-
out << "],";
|
|
98
|
-
out << "\"green_path\":[";
|
|
99
|
-
for (std::size_t i = 0; i < result.green_path.size(); ++i) {
|
|
100
|
-
if (i != 0) {
|
|
101
|
-
out << ",";
|
|
102
|
-
}
|
|
103
|
-
out << result.green_path[i];
|
|
104
|
-
}
|
|
105
|
-
out << "],";
|
|
106
|
-
out << "\"green_crossings\":[";
|
|
107
|
-
for (std::size_t i = 0; i < result.green_crossings.size(); ++i) {
|
|
108
|
-
if (i != 0) {
|
|
109
|
-
out << ",";
|
|
110
|
-
}
|
|
111
|
-
const auto& crossing = result.green_crossings[i];
|
|
112
|
-
out << "{\"from_face\":" << crossing.from_face
|
|
113
|
-
<< ",\"to_face\":" << crossing.to_face
|
|
114
|
-
<< ",\"strand_level\":\"" << json_escape(crossing.strand_level) << "\"}";
|
|
115
|
-
}
|
|
116
|
-
out << "]";
|
|
117
|
-
}
|
|
87
|
+
out << "\"last_path_search_mode\":\"" << json_escape(result.last_path_search_mode) << "\",";
|
|
88
|
+
out << "\"stopped_by_round_limit\":"
|
|
89
|
+
<< (result.stopped_by_round_limit ? "true" : "false");
|
|
118
90
|
out << "}";
|
|
119
91
|
return out.str();
|
|
120
92
|
}
|
|
@@ -139,6 +111,8 @@ char* pdcode_simplify_run_json(
|
|
|
139
111
|
const char* pd_text,
|
|
140
112
|
int max_paths,
|
|
141
113
|
int ban_heuristic,
|
|
114
|
+
int reduction_round,
|
|
115
|
+
int verbose,
|
|
142
116
|
unsigned long long known_crossingless_components,
|
|
143
117
|
const int* removed_crossings,
|
|
144
118
|
unsigned long long removed_crossing_count) {
|
|
@@ -151,6 +125,10 @@ char* pdcode_simplify_run_json(
|
|
|
151
125
|
pdcode_simplify::SimplifierOptions options;
|
|
152
126
|
options.max_paths = max_paths;
|
|
153
127
|
options.ban_heuristic = ban_heuristic != 0;
|
|
128
|
+
options.verbose = verbose != 0;
|
|
129
|
+
options.progress = [](const std::string& message) {
|
|
130
|
+
std::cerr << "[pdcode-simplify] " << message << '\n';
|
|
131
|
+
};
|
|
154
132
|
|
|
155
133
|
const pdcode_simplify::PDCode code = pdcode_simplify::parse_pd_code(text);
|
|
156
134
|
std::size_t crossingless = static_cast<std::size_t>(known_crossingless_components);
|
|
@@ -170,17 +148,15 @@ char* pdcode_simplify_run_json(
|
|
|
170
148
|
code, removed, crossingless);
|
|
171
149
|
}
|
|
172
150
|
|
|
173
|
-
const auto
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const auto result = pdcode_simplify::find_simplification(pd_simplification.code, options);
|
|
151
|
+
const auto result =
|
|
152
|
+
pdcode_simplify::reduce_pd_code(code, crossingless, options, reduction_round);
|
|
153
|
+
const auto final_components =
|
|
154
|
+
pdcode_simplify::analyze_components(result.code, result.crossingless_components);
|
|
178
155
|
return copy_string(result_to_json(
|
|
179
156
|
result,
|
|
180
157
|
input_components,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
search_components));
|
|
158
|
+
final_components,
|
|
159
|
+
has_removal ? &after_removal_components : nullptr));
|
|
184
160
|
} catch (const std::exception& error) {
|
|
185
161
|
return copy_string(std::string("{\"error\":\"") + json_escape(error.what()) + "\"}");
|
|
186
162
|
} catch (...) {
|
|
@@ -1004,18 +1004,30 @@ void reset_weights(DualGraph& graph) {
|
|
|
1004
1004
|
}
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
|
+
std::vector<int> heuristic_distances_to_target(
|
|
1008
|
+
const DualGraph& graph,
|
|
1009
|
+
int target,
|
|
1010
|
+
int cutoff);
|
|
1011
|
+
|
|
1007
1012
|
void collect_simple_paths_dfs(
|
|
1008
1013
|
const DualGraph& graph,
|
|
1009
1014
|
int current,
|
|
1010
1015
|
int target,
|
|
1011
1016
|
int cutoff,
|
|
1012
1017
|
int max_paths,
|
|
1018
|
+
int current_weight,
|
|
1019
|
+
const std::vector<int>& distance,
|
|
1013
1020
|
std::vector<char>& visited,
|
|
1014
1021
|
std::vector<int>& current_path,
|
|
1015
1022
|
std::vector<std::vector<int>>& paths) {
|
|
1023
|
+
const int infinity = std::numeric_limits<int>::max() / 4;
|
|
1016
1024
|
if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
|
|
1017
1025
|
return;
|
|
1018
1026
|
}
|
|
1027
|
+
if (current < 0 || current >= static_cast<int>(distance.size()) ||
|
|
1028
|
+
distance[current] == infinity || current_weight + distance[current] >= cutoff) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1019
1031
|
|
|
1020
1032
|
for (int edge_index : graph.adjacency[current]) {
|
|
1021
1033
|
const GraphEdge& edge = graph.edges[edge_index];
|
|
@@ -1023,32 +1035,37 @@ void collect_simple_paths_dfs(
|
|
|
1023
1035
|
if (visited[next]) {
|
|
1024
1036
|
continue;
|
|
1025
1037
|
}
|
|
1038
|
+
const int next_weight = current_weight + edge.weight;
|
|
1039
|
+
if (next_weight >= cutoff) {
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (next < 0 || next >= static_cast<int>(distance.size()) ||
|
|
1043
|
+
distance[next] == infinity || next_weight + distance[next] >= cutoff) {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1026
1046
|
|
|
1027
1047
|
current_path.push_back(next);
|
|
1028
1048
|
visited[next] = true;
|
|
1029
1049
|
|
|
1030
1050
|
if (next == target) {
|
|
1031
|
-
|
|
1032
|
-
for (std::size_t i = 0; i + 1 < current_path.size(); ++i) {
|
|
1033
|
-
const GraphEdge* path_edge = graph.edge(current_path[i], current_path[i + 1]);
|
|
1034
|
-
if (path_edge == nullptr) {
|
|
1035
|
-
throw std::logic_error("Missing dual edge while weighing a path");
|
|
1036
|
-
}
|
|
1037
|
-
path_weight += path_edge->weight;
|
|
1038
|
-
if (path_weight >= cutoff) {
|
|
1039
|
-
break;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
if (path_weight < cutoff) {
|
|
1043
|
-
paths.push_back(current_path);
|
|
1044
|
-
}
|
|
1051
|
+
paths.push_back(current_path);
|
|
1045
1052
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1046
1053
|
visited[next] = false;
|
|
1047
1054
|
current_path.pop_back();
|
|
1048
1055
|
return;
|
|
1049
1056
|
}
|
|
1050
1057
|
} else {
|
|
1051
|
-
collect_simple_paths_dfs(
|
|
1058
|
+
collect_simple_paths_dfs(
|
|
1059
|
+
graph,
|
|
1060
|
+
next,
|
|
1061
|
+
target,
|
|
1062
|
+
cutoff,
|
|
1063
|
+
max_paths,
|
|
1064
|
+
next_weight,
|
|
1065
|
+
distance,
|
|
1066
|
+
visited,
|
|
1067
|
+
current_path,
|
|
1068
|
+
paths);
|
|
1052
1069
|
if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
|
|
1053
1070
|
visited[next] = false;
|
|
1054
1071
|
current_path.pop_back();
|
|
@@ -1077,8 +1094,19 @@ std::vector<std::vector<int>> collect_simple_paths(
|
|
|
1077
1094
|
|
|
1078
1095
|
std::vector<char> visited(graph.faces.size(), false);
|
|
1079
1096
|
std::vector<int> current_path{source};
|
|
1097
|
+
const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
|
|
1080
1098
|
visited[source] = true;
|
|
1081
|
-
collect_simple_paths_dfs(
|
|
1099
|
+
collect_simple_paths_dfs(
|
|
1100
|
+
graph,
|
|
1101
|
+
source,
|
|
1102
|
+
target,
|
|
1103
|
+
cutoff,
|
|
1104
|
+
max_paths,
|
|
1105
|
+
0,
|
|
1106
|
+
distance,
|
|
1107
|
+
visited,
|
|
1108
|
+
current_path,
|
|
1109
|
+
paths);
|
|
1082
1110
|
return paths;
|
|
1083
1111
|
}
|
|
1084
1112
|
|
|
@@ -1446,6 +1474,292 @@ bool do_check(
|
|
|
1446
1474
|
return true;
|
|
1447
1475
|
}
|
|
1448
1476
|
|
|
1477
|
+
class DisjointSet {
|
|
1478
|
+
public:
|
|
1479
|
+
int find(int value) {
|
|
1480
|
+
auto inserted = parent_.insert(std::make_pair(value, value));
|
|
1481
|
+
int parent = inserted.first->second;
|
|
1482
|
+
if (parent != value) {
|
|
1483
|
+
parent = find(parent);
|
|
1484
|
+
parent_[value] = parent;
|
|
1485
|
+
}
|
|
1486
|
+
return parent;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
void unite(int first, int second) {
|
|
1490
|
+
int first_root = find(first);
|
|
1491
|
+
int second_root = find(second);
|
|
1492
|
+
if (first_root == second_root) {
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (second_root < first_root) {
|
|
1496
|
+
std::swap(first_root, second_root);
|
|
1497
|
+
}
|
|
1498
|
+
parent_[second_root] = first_root;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
private:
|
|
1502
|
+
std::map<int, int> parent_;
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
std::map<std::pair<int, int>, std::string> green_crossing_levels(
|
|
1506
|
+
const SimplificationResult& result) {
|
|
1507
|
+
std::map<std::pair<int, int>, std::string> levels;
|
|
1508
|
+
for (const GreenCrossing& crossing : result.green_crossings) {
|
|
1509
|
+
levels[std::make_pair(crossing.from_face, crossing.to_face)] = crossing.strand_level;
|
|
1510
|
+
}
|
|
1511
|
+
return levels;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
void clear_witness(SimplificationResult& result) {
|
|
1515
|
+
result.found = false;
|
|
1516
|
+
result.red_path.clear();
|
|
1517
|
+
result.green_path.clear();
|
|
1518
|
+
result.green_crossings.clear();
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
} // namespace
|
|
1522
|
+
|
|
1523
|
+
MidSimplificationApplyResult apply_simplification_witness(
|
|
1524
|
+
const PDCode& code,
|
|
1525
|
+
const SimplificationResult& result,
|
|
1526
|
+
std::size_t known_crossingless_components) {
|
|
1527
|
+
if (!result.found) {
|
|
1528
|
+
throw std::invalid_argument("Cannot apply a missing simplification witness");
|
|
1529
|
+
}
|
|
1530
|
+
if (result.red_path.size() < 2) {
|
|
1531
|
+
throw std::invalid_argument("Simplification witness red path is too short");
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
Diagram diagram(code);
|
|
1535
|
+
DualGraph graph(diagram);
|
|
1536
|
+
std::set<int> removed_crossings;
|
|
1537
|
+
std::map<int, int> red_entry_by_crossing;
|
|
1538
|
+
for (std::size_t i = 0; i + 1 < result.red_path.size(); ++i) {
|
|
1539
|
+
removed_crossings.insert(result.red_path[i].crossing);
|
|
1540
|
+
red_entry_by_crossing[result.red_path[i].crossing] = result.red_path[i].strand;
|
|
1541
|
+
}
|
|
1542
|
+
if (removed_crossings.size() != result.red_path.size() - 1) {
|
|
1543
|
+
throw std::invalid_argument("Simplification witness repeats a removed red crossing");
|
|
1544
|
+
}
|
|
1545
|
+
if (removed_crossings.count(result.red_path.back().crossing) != 0) {
|
|
1546
|
+
throw std::invalid_argument("Simplification witness ends inside the removed red arc");
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const std::map<std::pair<int, int>, std::string> levels = green_crossing_levels(result);
|
|
1550
|
+
DisjointSet dsu;
|
|
1551
|
+
const int endpoint_count = static_cast<int>(code.size() * 4);
|
|
1552
|
+
const int new_crossing_count =
|
|
1553
|
+
result.green_path.empty() ? 0 : static_cast<int>(result.green_path.size()) - 1;
|
|
1554
|
+
const int new_base = endpoint_count;
|
|
1555
|
+
|
|
1556
|
+
auto new_node = [&](int crossing_index, int strand) {
|
|
1557
|
+
return new_base + crossing_index * 4 + strand;
|
|
1558
|
+
};
|
|
1559
|
+
auto is_removed_node = [&](int node) {
|
|
1560
|
+
return node < endpoint_count && removed_crossings.count(node / 4) != 0;
|
|
1561
|
+
};
|
|
1562
|
+
auto is_removed_red_node = [&](int node) {
|
|
1563
|
+
if (!is_removed_node(node)) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
const int crossing = node / 4;
|
|
1567
|
+
const int strand = node % 4;
|
|
1568
|
+
const int red_strand = red_entry_by_crossing.at(crossing);
|
|
1569
|
+
return strand == red_strand || strand == positive_mod(red_strand + 2, 4);
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
std::set<int> crossed_labels;
|
|
1573
|
+
struct CrossedEdge {
|
|
1574
|
+
int interface_from = -1;
|
|
1575
|
+
int interface_to = -1;
|
|
1576
|
+
std::string level;
|
|
1577
|
+
};
|
|
1578
|
+
std::vector<CrossedEdge> crossed_edges;
|
|
1579
|
+
crossed_edges.reserve(new_crossing_count);
|
|
1580
|
+
for (int i = 0; i < new_crossing_count; ++i) {
|
|
1581
|
+
const int from_face = result.green_path[i];
|
|
1582
|
+
const int to_face = result.green_path[i + 1];
|
|
1583
|
+
const GraphEdge* edge = graph.edge(from_face, to_face);
|
|
1584
|
+
if (edge == nullptr) {
|
|
1585
|
+
throw std::invalid_argument("Simplification witness green path crosses a missing dual edge");
|
|
1586
|
+
}
|
|
1587
|
+
const int interface_from = graph.interface_for_face(*edge, from_face);
|
|
1588
|
+
const int interface_to = graph.interface_for_face(*edge, to_face);
|
|
1589
|
+
if (is_removed_red_node(interface_from) || is_removed_red_node(interface_to)) {
|
|
1590
|
+
throw std::invalid_argument("Simplification witness crosses an edge removed with the red arc");
|
|
1591
|
+
}
|
|
1592
|
+
const int label = code[interface_from / 4][interface_from % 4];
|
|
1593
|
+
if (!crossed_labels.insert(label).second) {
|
|
1594
|
+
throw std::invalid_argument("Simplification witness crosses the same PD edge more than once");
|
|
1595
|
+
}
|
|
1596
|
+
const auto level = levels.find(std::make_pair(from_face, to_face));
|
|
1597
|
+
if (level == levels.end()) {
|
|
1598
|
+
throw std::invalid_argument("Simplification witness is missing a green crossing level");
|
|
1599
|
+
}
|
|
1600
|
+
CrossedEdge crossed;
|
|
1601
|
+
crossed.interface_from = interface_from;
|
|
1602
|
+
crossed.interface_to = interface_to;
|
|
1603
|
+
crossed.level = level->second;
|
|
1604
|
+
crossed_edges.push_back(std::move(crossed));
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
std::map<int, std::vector<int>> label_endpoints;
|
|
1608
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1609
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1610
|
+
label_endpoints[code[crossing_index][strand]].push_back(crossing_index * 4 + strand);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
for (const auto& item : label_endpoints) {
|
|
1614
|
+
if (item.second.size() != 2) {
|
|
1615
|
+
std::ostringstream message;
|
|
1616
|
+
message << "PD label " << item.first << " appears " << item.second.size() << " times";
|
|
1617
|
+
throw std::invalid_argument(message.str());
|
|
1618
|
+
}
|
|
1619
|
+
if (crossed_labels.count(item.first) == 0) {
|
|
1620
|
+
dsu.unite(item.second[0], item.second[1]);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
for (const auto& item : red_entry_by_crossing) {
|
|
1625
|
+
const int crossing = item.first;
|
|
1626
|
+
const int strand = item.second;
|
|
1627
|
+
dsu.unite(crossing * 4 + positive_mod(strand + 1, 4),
|
|
1628
|
+
crossing * 4 + positive_mod(strand + 3, 4));
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
int green_anchor = endpoint_key(result.red_path.front());
|
|
1632
|
+
for (int i = 0; i < static_cast<int>(crossed_edges.size()); ++i) {
|
|
1633
|
+
const CrossedEdge& crossed = crossed_edges[i];
|
|
1634
|
+
int existing_from_pos = -1;
|
|
1635
|
+
int existing_to_pos = -1;
|
|
1636
|
+
int green_in_pos = -1;
|
|
1637
|
+
int green_out_pos = -1;
|
|
1638
|
+
if (crossed.level == "over") {
|
|
1639
|
+
existing_from_pos = 0;
|
|
1640
|
+
green_in_pos = 1;
|
|
1641
|
+
existing_to_pos = 2;
|
|
1642
|
+
green_out_pos = 3;
|
|
1643
|
+
} else if (crossed.level == "under") {
|
|
1644
|
+
green_in_pos = 0;
|
|
1645
|
+
existing_to_pos = 1;
|
|
1646
|
+
green_out_pos = 2;
|
|
1647
|
+
existing_from_pos = 3;
|
|
1648
|
+
} else {
|
|
1649
|
+
throw std::invalid_argument("Unknown green crossing strand level: " + crossed.level);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
dsu.unite(crossed.interface_from, new_node(i, existing_from_pos));
|
|
1653
|
+
dsu.unite(crossed.interface_to, new_node(i, existing_to_pos));
|
|
1654
|
+
dsu.unite(green_anchor, new_node(i, green_in_pos));
|
|
1655
|
+
green_anchor = new_node(i, green_out_pos);
|
|
1656
|
+
}
|
|
1657
|
+
dsu.unite(green_anchor, endpoint_key(result.red_path.back()));
|
|
1658
|
+
|
|
1659
|
+
std::vector<int> active_nodes;
|
|
1660
|
+
active_nodes.reserve(endpoint_count + new_crossing_count * 4);
|
|
1661
|
+
for (int node = 0; node < endpoint_count; ++node) {
|
|
1662
|
+
if (!is_removed_node(node)) {
|
|
1663
|
+
active_nodes.push_back(node);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
for (int crossing = 0; crossing < new_crossing_count; ++crossing) {
|
|
1667
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1668
|
+
active_nodes.push_back(new_node(crossing, strand));
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
std::map<int, std::vector<int>> grouped;
|
|
1673
|
+
for (int node : active_nodes) {
|
|
1674
|
+
grouped[dsu.find(node)].push_back(node);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
std::vector<std::vector<int>> groups;
|
|
1678
|
+
for (auto& item : grouped) {
|
|
1679
|
+
std::sort(item.second.begin(), item.second.end());
|
|
1680
|
+
groups.push_back(item.second);
|
|
1681
|
+
}
|
|
1682
|
+
std::sort(groups.begin(), groups.end(), [](const std::vector<int>& lhs, const std::vector<int>& rhs) {
|
|
1683
|
+
return lhs.front() < rhs.front();
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
std::map<int, int> label_by_node;
|
|
1687
|
+
int next_label = 0;
|
|
1688
|
+
for (const std::vector<int>& nodes : groups) {
|
|
1689
|
+
if (nodes.size() != 2) {
|
|
1690
|
+
std::ostringstream message;
|
|
1691
|
+
message << "Applied simplification produced a non-PD edge with "
|
|
1692
|
+
<< nodes.size() << " active endpoints";
|
|
1693
|
+
throw std::runtime_error(message.str());
|
|
1694
|
+
}
|
|
1695
|
+
for (int node : nodes) {
|
|
1696
|
+
label_by_node[node] = next_label;
|
|
1697
|
+
}
|
|
1698
|
+
++next_label;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
PDCode output;
|
|
1702
|
+
output.reserve(code.size() - removed_crossings.size() + static_cast<std::size_t>(new_crossing_count));
|
|
1703
|
+
for (int crossing_index = 0; crossing_index < static_cast<int>(code.size()); ++crossing_index) {
|
|
1704
|
+
if (removed_crossings.count(crossing_index) != 0) {
|
|
1705
|
+
continue;
|
|
1706
|
+
}
|
|
1707
|
+
Crossing crossing{};
|
|
1708
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1709
|
+
crossing[strand] = label_by_node.at(crossing_index * 4 + strand);
|
|
1710
|
+
}
|
|
1711
|
+
output.push_back(crossing);
|
|
1712
|
+
}
|
|
1713
|
+
for (int crossing_index = 0; crossing_index < new_crossing_count; ++crossing_index) {
|
|
1714
|
+
Crossing crossing{};
|
|
1715
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1716
|
+
crossing[strand] = label_by_node.at(new_node(crossing_index, strand));
|
|
1717
|
+
}
|
|
1718
|
+
output.push_back(crossing);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
const std::size_t total_components =
|
|
1722
|
+
analyze_components(code, known_crossingless_components).total_components();
|
|
1723
|
+
output = renumber_full_dfs(output);
|
|
1724
|
+
const std::size_t crossing_components = analyze_components(output).components_with_crossings();
|
|
1725
|
+
|
|
1726
|
+
MidSimplificationApplyResult applied;
|
|
1727
|
+
applied.code = std::move(output);
|
|
1728
|
+
applied.crossingless_components =
|
|
1729
|
+
total_components > crossing_components ? total_components - crossing_components : 0;
|
|
1730
|
+
return applied;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
namespace {
|
|
1734
|
+
|
|
1735
|
+
bool witness_has_applicable_surgery(const PDCode& code, const SimplificationResult& result) {
|
|
1736
|
+
try {
|
|
1737
|
+
(void)apply_simplification_witness(code, result, 0);
|
|
1738
|
+
return true;
|
|
1739
|
+
} catch (const std::exception&) {
|
|
1740
|
+
return false;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
void emit_progress(const SimplifierOptions& options, const std::string& message) {
|
|
1745
|
+
if (!options.verbose) {
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
if (options.progress) {
|
|
1749
|
+
options.progress(message);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
std::string search_mode_for_options(const SimplifierOptions& options) {
|
|
1754
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
1755
|
+
return "heuristic";
|
|
1756
|
+
}
|
|
1757
|
+
if (options.max_paths == -1) {
|
|
1758
|
+
return "bruteforce";
|
|
1759
|
+
}
|
|
1760
|
+
return "bounded";
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1449
1763
|
} // namespace
|
|
1450
1764
|
|
|
1451
1765
|
PDCode parse_pd_code(const std::string& text) {
|
|
@@ -1486,13 +1800,13 @@ PDCode parse_pd_code(const std::string& text) {
|
|
|
1486
1800
|
|
|
1487
1801
|
std::string format_pd_code(const PDCode& code) {
|
|
1488
1802
|
std::ostringstream out;
|
|
1489
|
-
out <<
|
|
1803
|
+
out << "PD[";
|
|
1490
1804
|
for (std::size_t i = 0; i < code.size(); ++i) {
|
|
1491
1805
|
if (i != 0) {
|
|
1492
|
-
out <<
|
|
1806
|
+
out << ',';
|
|
1493
1807
|
}
|
|
1494
|
-
out <<
|
|
1495
|
-
<< code[i][2] <<
|
|
1808
|
+
out << "X[" << code[i][0] << ',' << code[i][1] << ','
|
|
1809
|
+
<< code[i][2] << ',' << code[i][3] << ']';
|
|
1496
1810
|
}
|
|
1497
1811
|
out << ']';
|
|
1498
1812
|
return out.str();
|
|
@@ -1886,10 +2200,16 @@ SimplificationResult find_simplification(
|
|
|
1886
2200
|
continue;
|
|
1887
2201
|
}
|
|
1888
2202
|
if (do_check(diagram, graph, red_path, green_path, Direction::Left, result)) {
|
|
1889
|
-
|
|
2203
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, result)) {
|
|
2204
|
+
return result;
|
|
2205
|
+
}
|
|
2206
|
+
clear_witness(result);
|
|
1890
2207
|
}
|
|
1891
2208
|
if (do_check(diagram, graph, red_path, green_path, Direction::Right, result)) {
|
|
1892
|
-
|
|
2209
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, result)) {
|
|
2210
|
+
return result;
|
|
2211
|
+
}
|
|
2212
|
+
clear_witness(result);
|
|
1893
2213
|
}
|
|
1894
2214
|
}
|
|
1895
2215
|
}
|
|
@@ -1897,6 +2217,140 @@ SimplificationResult find_simplification(
|
|
|
1897
2217
|
return result;
|
|
1898
2218
|
}
|
|
1899
2219
|
|
|
2220
|
+
ReductionResult reduce_pd_code(
|
|
2221
|
+
const PDCode& code,
|
|
2222
|
+
std::size_t known_crossingless_components,
|
|
2223
|
+
const SimplifierOptions& options,
|
|
2224
|
+
int reduction_round) {
|
|
2225
|
+
{
|
|
2226
|
+
std::ostringstream message;
|
|
2227
|
+
message << "start input_crossings=" << code.size()
|
|
2228
|
+
<< " known_crossingless_components=" << known_crossingless_components
|
|
2229
|
+
<< " reduction_round=" << reduction_round
|
|
2230
|
+
<< " max_paths=" << options.max_paths
|
|
2231
|
+
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2232
|
+
emit_progress(options, message.str());
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
|
|
2236
|
+
ReductionResult output;
|
|
2237
|
+
output.code = prepared.code;
|
|
2238
|
+
output.crossingless_components = prepared.crossingless_components;
|
|
2239
|
+
output.reidemeister_i_moves = prepared.reidemeister_i_moves;
|
|
2240
|
+
output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
|
|
2241
|
+
{
|
|
2242
|
+
std::ostringstream message;
|
|
2243
|
+
message << "pre_simplify input_crossings=" << code.size()
|
|
2244
|
+
<< " output_crossings=" << output.code.size()
|
|
2245
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2246
|
+
<< " r1_moves=" << prepared.reidemeister_i_moves
|
|
2247
|
+
<< " nugatory_moves=" << prepared.nugatory_crossing_moves;
|
|
2248
|
+
emit_progress(options, message.str());
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
|
|
2252
|
+
SimplifierOptions search_options = options;
|
|
2253
|
+
search_options.require_applicable = true;
|
|
2254
|
+
const int round = output.mid_simplification_rounds + 1;
|
|
2255
|
+
{
|
|
2256
|
+
std::ostringstream message;
|
|
2257
|
+
message << "round " << round
|
|
2258
|
+
<< " search_start crossings=" << output.code.size()
|
|
2259
|
+
<< " mode=" << search_mode_for_options(search_options);
|
|
2260
|
+
emit_progress(options, message.str());
|
|
2261
|
+
}
|
|
2262
|
+
SimplificationResult search = find_simplification(output.code, search_options);
|
|
2263
|
+
output.tested_red_paths += search.tested_red_paths;
|
|
2264
|
+
output.tested_green_paths += search.tested_green_paths;
|
|
2265
|
+
output.last_path_search_mode = search.path_search_mode;
|
|
2266
|
+
{
|
|
2267
|
+
std::ostringstream message;
|
|
2268
|
+
message << "round " << round
|
|
2269
|
+
<< " search_done found=" << (search.found ? "yes" : "no")
|
|
2270
|
+
<< " mode=" << search.path_search_mode
|
|
2271
|
+
<< " tested_red=" << search.tested_red_paths
|
|
2272
|
+
<< " tested_green=" << search.tested_green_paths;
|
|
2273
|
+
emit_progress(options, message.str());
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
if (!search.found && reduction_round < 0 &&
|
|
2277
|
+
options.max_paths == -1 && !options.ban_heuristic) {
|
|
2278
|
+
SimplifierOptions brute_options = options;
|
|
2279
|
+
brute_options.max_paths = -1;
|
|
2280
|
+
brute_options.ban_heuristic = true;
|
|
2281
|
+
brute_options.require_applicable = true;
|
|
2282
|
+
{
|
|
2283
|
+
std::ostringstream message;
|
|
2284
|
+
message << "round " << round
|
|
2285
|
+
<< " brute_fallback_start crossings=" << output.code.size();
|
|
2286
|
+
emit_progress(options, message.str());
|
|
2287
|
+
}
|
|
2288
|
+
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
2289
|
+
output.tested_red_paths += brute.tested_red_paths;
|
|
2290
|
+
output.tested_green_paths += brute.tested_green_paths;
|
|
2291
|
+
output.last_path_search_mode = brute.path_search_mode;
|
|
2292
|
+
{
|
|
2293
|
+
std::ostringstream message;
|
|
2294
|
+
message << "round " << round
|
|
2295
|
+
<< " brute_fallback_done found=" << (brute.found ? "yes" : "no")
|
|
2296
|
+
<< " tested_red=" << brute.tested_red_paths
|
|
2297
|
+
<< " tested_green=" << brute.tested_green_paths;
|
|
2298
|
+
emit_progress(options, message.str());
|
|
2299
|
+
}
|
|
2300
|
+
if (brute.found) {
|
|
2301
|
+
++output.heuristic_failover_rounds;
|
|
2302
|
+
search = std::move(brute);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
if (!search.found) {
|
|
2307
|
+
{
|
|
2308
|
+
std::ostringstream message;
|
|
2309
|
+
message << "round " << round
|
|
2310
|
+
<< " stop_no_path crossings=" << output.code.size();
|
|
2311
|
+
emit_progress(options, message.str());
|
|
2312
|
+
}
|
|
2313
|
+
break;
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
const std::size_t before_apply_crossings = output.code.size();
|
|
2317
|
+
const MidSimplificationApplyResult applied =
|
|
2318
|
+
apply_simplification_witness(output.code, search, output.crossingless_components);
|
|
2319
|
+
++output.mid_simplification_rounds;
|
|
2320
|
+
const PDSimplificationResult simplified =
|
|
2321
|
+
simplify_pd_code(applied.code, applied.crossingless_components);
|
|
2322
|
+
output.code = simplified.code;
|
|
2323
|
+
output.crossingless_components = simplified.crossingless_components;
|
|
2324
|
+
output.reidemeister_i_moves += simplified.reidemeister_i_moves;
|
|
2325
|
+
output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
|
|
2326
|
+
{
|
|
2327
|
+
std::ostringstream message;
|
|
2328
|
+
message << "round " << round
|
|
2329
|
+
<< " applied crossings=" << before_apply_crossings
|
|
2330
|
+
<< " -> " << applied.code.size()
|
|
2331
|
+
<< " -> " << output.code.size()
|
|
2332
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2333
|
+
<< " r1_moves=" << simplified.reidemeister_i_moves
|
|
2334
|
+
<< " nugatory_moves=" << simplified.nugatory_crossing_moves;
|
|
2335
|
+
emit_progress(options, message.str());
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
output.stopped_by_round_limit =
|
|
2340
|
+
reduction_round >= 0 && output.mid_simplification_rounds >= reduction_round;
|
|
2341
|
+
{
|
|
2342
|
+
std::ostringstream message;
|
|
2343
|
+
message << "done final_crossings=" << output.code.size()
|
|
2344
|
+
<< " crossingless_components=" << output.crossingless_components
|
|
2345
|
+
<< " mid_rounds=" << output.mid_simplification_rounds
|
|
2346
|
+
<< " heuristic_failover_rounds=" << output.heuristic_failover_rounds
|
|
2347
|
+
<< " stopped_by_round_limit="
|
|
2348
|
+
<< (output.stopped_by_round_limit ? "yes" : "no");
|
|
2349
|
+
emit_progress(options, message.str());
|
|
2350
|
+
}
|
|
2351
|
+
return output;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
1900
2354
|
std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint) {
|
|
1901
2355
|
out << format_endpoint(endpoint);
|
|
1902
2356
|
return out;
|
|
@@ -402,6 +402,8 @@ def _load_library() -> ctypes.CDLL:
|
|
|
402
402
|
ctypes.c_char_p,
|
|
403
403
|
ctypes.c_int,
|
|
404
404
|
ctypes.c_int,
|
|
405
|
+
ctypes.c_int,
|
|
406
|
+
ctypes.c_int,
|
|
405
407
|
ctypes.c_ulonglong,
|
|
406
408
|
ctypes.POINTER(ctypes.c_int),
|
|
407
409
|
ctypes.c_ulonglong,
|
|
@@ -419,9 +421,13 @@ def _run_one(
|
|
|
419
421
|
*,
|
|
420
422
|
max_paths: int = -1,
|
|
421
423
|
ban_heuristic: bool = False,
|
|
424
|
+
reduction_round: int = -1,
|
|
425
|
+
verbose: bool = False,
|
|
422
426
|
known_crossingless_components: int = 0,
|
|
423
427
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
424
428
|
) -> dict[str, Any]:
|
|
429
|
+
if reduction_round < -1:
|
|
430
|
+
raise ValueError("reduction_round must be -1 or a non-negative integer")
|
|
425
431
|
library = _load_library()
|
|
426
432
|
removed_count = 0 if remove_crossings is None else len(remove_crossings)
|
|
427
433
|
removed_array = None
|
|
@@ -432,6 +438,8 @@ def _run_one(
|
|
|
432
438
|
pd_text.encode("utf-8"),
|
|
433
439
|
int(max_paths),
|
|
434
440
|
1 if ban_heuristic else 0,
|
|
441
|
+
int(reduction_round),
|
|
442
|
+
1 if verbose else 0,
|
|
435
443
|
int(known_crossingless_components),
|
|
436
444
|
removed_array,
|
|
437
445
|
int(removed_count),
|
|
@@ -457,6 +465,8 @@ def simplify(
|
|
|
457
465
|
*,
|
|
458
466
|
max_paths: int = -1,
|
|
459
467
|
ban_heuristic: bool = False,
|
|
468
|
+
reduction_round: int = -1,
|
|
469
|
+
verbose: bool = False,
|
|
460
470
|
known_crossingless_components: int = 0,
|
|
461
471
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
462
472
|
) -> dict[str, Any]:
|
|
@@ -466,6 +476,8 @@ def simplify(
|
|
|
466
476
|
normalize_pd_code(pd_code),
|
|
467
477
|
max_paths=max_paths,
|
|
468
478
|
ban_heuristic=ban_heuristic,
|
|
479
|
+
reduction_round=reduction_round,
|
|
480
|
+
verbose=verbose,
|
|
469
481
|
known_crossingless_components=known_crossingless_components,
|
|
470
482
|
remove_crossings=remove_crossings,
|
|
471
483
|
)
|
|
@@ -476,6 +488,8 @@ def simplify_many(
|
|
|
476
488
|
*,
|
|
477
489
|
max_paths: int = -1,
|
|
478
490
|
ban_heuristic: bool = False,
|
|
491
|
+
reduction_round: int = -1,
|
|
492
|
+
verbose: bool = False,
|
|
479
493
|
known_crossingless_components: int = 0,
|
|
480
494
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
481
495
|
) -> list[dict[str, Any]]:
|
|
@@ -486,6 +500,8 @@ def simplify_many(
|
|
|
486
500
|
pd_text,
|
|
487
501
|
max_paths=max_paths,
|
|
488
502
|
ban_heuristic=ban_heuristic,
|
|
503
|
+
reduction_round=reduction_round,
|
|
504
|
+
verbose=verbose,
|
|
489
505
|
known_crossingless_components=known_crossingless_components,
|
|
490
506
|
remove_crossings=remove_crossings,
|
|
491
507
|
)
|
|
@@ -496,16 +512,24 @@ def simplify_many(
|
|
|
496
512
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
497
513
|
parser = argparse.ArgumentParser(description="Run cpp-pd-code-simplify through the Python interface.")
|
|
498
514
|
parser.add_argument("pd_code", nargs="?", help="PD code as PD[...] text or a Python-style list of crossings.")
|
|
515
|
+
parser.add_argument("--pd-code", "-c", dest="pd_code_option", help="literal PD[...] string")
|
|
499
516
|
parser.add_argument("--pd-file", "-f", help="read one file containing one or more labelled PD-code lines")
|
|
500
517
|
parser.add_argument("--max-paths", type=int, default=-1)
|
|
501
518
|
parser.add_argument("--ban-heuristic", action="store_true")
|
|
519
|
+
parser.add_argument("--reduction-round", type=int, default=-1)
|
|
520
|
+
parser.add_argument("--verbose", action="store_true")
|
|
502
521
|
parser.add_argument("--known-crossingless-components", type=int, default=0)
|
|
503
522
|
parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
|
|
504
523
|
args = parser.parse_args(argv)
|
|
505
|
-
if args.
|
|
506
|
-
parser.error("
|
|
507
|
-
if
|
|
508
|
-
parser.error("a positional PD code or --pd-
|
|
524
|
+
if args.reduction_round < -1:
|
|
525
|
+
parser.error("--reduction-round must be -1 or a non-negative integer")
|
|
526
|
+
if args.pd_code and args.pd_code_option:
|
|
527
|
+
parser.error("pass either a positional PD code or --pd-code, not both")
|
|
528
|
+
pd_code_text = args.pd_code_option or args.pd_code
|
|
529
|
+
if args.pd_file and pd_code_text:
|
|
530
|
+
parser.error("pass either a PD code or --pd-file, not both")
|
|
531
|
+
if not args.pd_file and not pd_code_text:
|
|
532
|
+
parser.error("a PD code or --pd-file is required")
|
|
509
533
|
remove_crossings = None
|
|
510
534
|
if args.remove_crossings:
|
|
511
535
|
remove_crossings = [int(token) for token in re.findall(r"-?\d+", args.remove_crossings)]
|
|
@@ -521,6 +545,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
521
545
|
line,
|
|
522
546
|
max_paths=args.max_paths,
|
|
523
547
|
ban_heuristic=args.ban_heuristic,
|
|
548
|
+
reduction_round=args.reduction_round,
|
|
549
|
+
verbose=args.verbose,
|
|
524
550
|
known_crossingless_components=args.known_crossingless_components,
|
|
525
551
|
remove_crossings=remove_crossings,
|
|
526
552
|
)
|
|
@@ -536,9 +562,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
536
562
|
payload: Any = batch_payload
|
|
537
563
|
else:
|
|
538
564
|
payload = simplify(
|
|
539
|
-
|
|
565
|
+
pd_code_text or "",
|
|
540
566
|
max_paths=args.max_paths,
|
|
541
567
|
ban_heuristic=args.ban_heuristic,
|
|
568
|
+
reduction_round=args.reduction_round,
|
|
569
|
+
verbose=args.verbose,
|
|
542
570
|
known_crossingless_components=args.known_crossingless_components,
|
|
543
571
|
remove_crossings=remove_crossings,
|
|
544
572
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|