cpp-pd-code-simplify-interface 0.1.3__tar.gz → 0.1.4__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.3 → cpp_pd_code_simplify_interface-0.1.4}/PKG-INFO +1 -1
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp +1 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/data/src/native_interface.cpp +2 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp +380 -72
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/main.py +14 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/pyproject.toml +1 -1
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/README.md +0 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/build_backend/cpp_pd_code_simplify_interface_build_backend.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/__init__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/__main__.py +0 -0
- {cpp_pd_code_simplify_interface-0.1.3 → cpp_pd_code_simplify_interface-0.1.4}/cpp_pd_code_simplify_interface/py.typed +0 -0
|
@@ -112,6 +112,7 @@ char* pdcode_simplify_run_json(
|
|
|
112
112
|
int max_paths,
|
|
113
113
|
int ban_heuristic,
|
|
114
114
|
int reduction_round,
|
|
115
|
+
int max_thread,
|
|
115
116
|
int verbose,
|
|
116
117
|
unsigned long long known_crossingless_components,
|
|
117
118
|
const int* removed_crossings,
|
|
@@ -124,6 +125,7 @@ char* pdcode_simplify_run_json(
|
|
|
124
125
|
const std::string text(pd_text);
|
|
125
126
|
pdcode_simplify::SimplifierOptions options;
|
|
126
127
|
options.max_paths = max_paths;
|
|
128
|
+
options.max_threads = max_thread;
|
|
127
129
|
options.ban_heuristic = ban_heuristic != 0;
|
|
128
130
|
options.verbose = verbose != 0;
|
|
129
131
|
options.progress = [](const std::string& message) {
|
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
#include <deque>
|
|
7
7
|
#include <limits>
|
|
8
8
|
#include <map>
|
|
9
|
+
#include <atomic>
|
|
10
|
+
#include <exception>
|
|
11
|
+
#include <mutex>
|
|
9
12
|
#include <numeric>
|
|
10
13
|
#include <queue>
|
|
11
14
|
#include <random>
|
|
12
15
|
#include <set>
|
|
13
16
|
#include <sstream>
|
|
14
17
|
#include <stdexcept>
|
|
18
|
+
#include <thread>
|
|
15
19
|
#include <unordered_map>
|
|
16
20
|
#include <unordered_set>
|
|
17
21
|
|
|
@@ -998,12 +1002,6 @@ std::set<int> normalized_removed_crossings(const PDCode& code, const std::vector
|
|
|
998
1002
|
return removed;
|
|
999
1003
|
}
|
|
1000
1004
|
|
|
1001
|
-
void reset_weights(DualGraph& graph) {
|
|
1002
|
-
for (auto& edge : graph.edges) {
|
|
1003
|
-
edge.weight = 1;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
1005
|
std::vector<int> heuristic_distances_to_target(
|
|
1008
1006
|
const DualGraph& graph,
|
|
1009
1007
|
int target,
|
|
@@ -1504,9 +1502,27 @@ private:
|
|
|
1504
1502
|
|
|
1505
1503
|
std::map<std::pair<int, int>, std::string> green_crossing_levels(
|
|
1506
1504
|
const SimplificationResult& result) {
|
|
1505
|
+
std::set<std::pair<int, int>> path_edges;
|
|
1506
|
+
for (std::size_t i = 0; i + 1 < result.green_path.size(); ++i) {
|
|
1507
|
+
path_edges.insert(std::make_pair(result.green_path[i], result.green_path[i + 1]));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1507
1510
|
std::map<std::pair<int, int>, std::string> levels;
|
|
1508
1511
|
for (const GreenCrossing& crossing : result.green_crossings) {
|
|
1509
|
-
|
|
1512
|
+
const std::pair<int, int> edge_key =
|
|
1513
|
+
std::make_pair(crossing.from_face, crossing.to_face);
|
|
1514
|
+
if (path_edges.count(edge_key) == 0) {
|
|
1515
|
+
throw std::invalid_argument("Simplification witness has a green crossing outside the green path");
|
|
1516
|
+
}
|
|
1517
|
+
const auto inserted = levels.insert(std::make_pair(edge_key, crossing.strand_level));
|
|
1518
|
+
if (!inserted.second && inserted.first->second != crossing.strand_level) {
|
|
1519
|
+
throw std::invalid_argument("Simplification witness has conflicting green crossing levels");
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
for (const auto& edge_key : path_edges) {
|
|
1523
|
+
if (levels.count(edge_key) == 0) {
|
|
1524
|
+
throw std::invalid_argument("Simplification witness is missing a green crossing level");
|
|
1525
|
+
}
|
|
1510
1526
|
}
|
|
1511
1527
|
return levels;
|
|
1512
1528
|
}
|
|
@@ -1518,6 +1534,68 @@ void clear_witness(SimplificationResult& result) {
|
|
|
1518
1534
|
result.green_crossings.clear();
|
|
1519
1535
|
}
|
|
1520
1536
|
|
|
1537
|
+
int crossing_graph_component_count(const PDCode& code) {
|
|
1538
|
+
const int crossing_count = static_cast<int>(code.size());
|
|
1539
|
+
if (crossing_count == 0) {
|
|
1540
|
+
return 0;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
std::vector<int> parent(crossing_count);
|
|
1544
|
+
std::iota(parent.begin(), parent.end(), 0);
|
|
1545
|
+
std::function<int(int)> find = [&](int value) -> int {
|
|
1546
|
+
if (parent[value] != value) {
|
|
1547
|
+
parent[value] = find(parent[value]);
|
|
1548
|
+
}
|
|
1549
|
+
return parent[value];
|
|
1550
|
+
};
|
|
1551
|
+
auto unite = [&](int first, int second) {
|
|
1552
|
+
int first_root = find(first);
|
|
1553
|
+
int second_root = find(second);
|
|
1554
|
+
if (first_root == second_root) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (second_root < first_root) {
|
|
1558
|
+
std::swap(first_root, second_root);
|
|
1559
|
+
}
|
|
1560
|
+
parent[second_root] = first_root;
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1563
|
+
std::map<int, std::vector<int>> label_crossings;
|
|
1564
|
+
for (int crossing_index = 0; crossing_index < crossing_count; ++crossing_index) {
|
|
1565
|
+
for (int strand = 0; strand < 4; ++strand) {
|
|
1566
|
+
label_crossings[code[crossing_index][strand]].push_back(crossing_index);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
for (const auto& item : label_crossings) {
|
|
1570
|
+
if (item.second.size() != 2) {
|
|
1571
|
+
std::ostringstream message;
|
|
1572
|
+
message << "PD label " << item.first << " appears " << item.second.size()
|
|
1573
|
+
<< " times; each label must appear exactly twice";
|
|
1574
|
+
throw std::invalid_argument(message.str());
|
|
1575
|
+
}
|
|
1576
|
+
unite(item.second[0], item.second[1]);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
std::set<int> roots;
|
|
1580
|
+
for (int crossing_index = 0; crossing_index < crossing_count; ++crossing_index) {
|
|
1581
|
+
roots.insert(find(crossing_index));
|
|
1582
|
+
}
|
|
1583
|
+
return static_cast<int>(roots.size());
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
bool is_planar_pd_code(const PDCode& code) {
|
|
1587
|
+
if (code.empty()) {
|
|
1588
|
+
return true;
|
|
1589
|
+
}
|
|
1590
|
+
Diagram diagram(code);
|
|
1591
|
+
DualGraph graph(diagram);
|
|
1592
|
+
const int vertices = static_cast<int>(code.size());
|
|
1593
|
+
const int edges = vertices * 2;
|
|
1594
|
+
const int faces = static_cast<int>(graph.faces.size());
|
|
1595
|
+
const int graph_components = crossing_graph_component_count(code);
|
|
1596
|
+
return vertices - edges + faces == 2 * graph_components;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1521
1599
|
} // namespace
|
|
1522
1600
|
|
|
1523
1601
|
MidSimplificationApplyResult apply_simplification_witness(
|
|
@@ -1637,14 +1715,14 @@ MidSimplificationApplyResult apply_simplification_witness(
|
|
|
1637
1715
|
int green_out_pos = -1;
|
|
1638
1716
|
if (crossed.level == "over") {
|
|
1639
1717
|
existing_from_pos = 0;
|
|
1640
|
-
green_in_pos = 1;
|
|
1641
1718
|
existing_to_pos = 2;
|
|
1642
|
-
|
|
1719
|
+
green_in_pos = 3;
|
|
1720
|
+
green_out_pos = 1;
|
|
1643
1721
|
} else if (crossed.level == "under") {
|
|
1722
|
+
existing_from_pos = 1;
|
|
1644
1723
|
green_in_pos = 0;
|
|
1645
|
-
existing_to_pos = 1;
|
|
1646
1724
|
green_out_pos = 2;
|
|
1647
|
-
|
|
1725
|
+
existing_to_pos = 3;
|
|
1648
1726
|
} else {
|
|
1649
1727
|
throw std::invalid_argument("Unknown green crossing strand level: " + crossed.level);
|
|
1650
1728
|
}
|
|
@@ -1721,6 +1799,9 @@ MidSimplificationApplyResult apply_simplification_witness(
|
|
|
1721
1799
|
const std::size_t total_components =
|
|
1722
1800
|
analyze_components(code, known_crossingless_components).total_components();
|
|
1723
1801
|
output = renumber_full_dfs(output);
|
|
1802
|
+
if (!is_planar_pd_code(output)) {
|
|
1803
|
+
throw std::invalid_argument("Applied simplification produced a non-planar PD code");
|
|
1804
|
+
}
|
|
1724
1805
|
const std::size_t crossing_components = analyze_components(output).components_with_crossings();
|
|
1725
1806
|
|
|
1726
1807
|
MidSimplificationApplyResult applied;
|
|
@@ -2140,78 +2221,302 @@ PDSimplificationResult simplify_pd_code(
|
|
|
2140
2221
|
return result;
|
|
2141
2222
|
}
|
|
2142
2223
|
|
|
2143
|
-
|
|
2224
|
+
namespace {
|
|
2225
|
+
|
|
2226
|
+
int detected_worker_count() {
|
|
2227
|
+
const unsigned int reported = std::thread::hardware_concurrency();
|
|
2228
|
+
if (reported == 0) {
|
|
2229
|
+
return 1;
|
|
2230
|
+
}
|
|
2231
|
+
if (reported <= 2) {
|
|
2232
|
+
return static_cast<int>(reported);
|
|
2233
|
+
}
|
|
2234
|
+
return static_cast<int>(reported - 1);
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
int selected_bruteforce_worker_count(int max_threads, int task_count) {
|
|
2238
|
+
if (task_count <= 1) {
|
|
2239
|
+
return 1;
|
|
2240
|
+
}
|
|
2241
|
+
int requested = max_threads == -1 ? detected_worker_count() : max_threads;
|
|
2242
|
+
if (requested < 1) {
|
|
2243
|
+
requested = 1;
|
|
2244
|
+
}
|
|
2245
|
+
if (max_threads == -1 && task_count < 32) {
|
|
2246
|
+
return 1;
|
|
2247
|
+
}
|
|
2248
|
+
return std::max(1, std::min(requested, task_count));
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
bool should_skip_parallel_red_path(
|
|
2252
|
+
int red_index,
|
|
2253
|
+
const std::atomic<int>* best_found_index) {
|
|
2254
|
+
return best_found_index != nullptr &&
|
|
2255
|
+
red_index > best_found_index->load(std::memory_order_relaxed);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
void record_parallel_found_index(
|
|
2259
|
+
int red_index,
|
|
2260
|
+
std::atomic<int>* best_found_index) {
|
|
2261
|
+
if (best_found_index == nullptr) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
int observed = best_found_index->load(std::memory_order_relaxed);
|
|
2265
|
+
while (red_index < observed &&
|
|
2266
|
+
!best_found_index->compare_exchange_weak(
|
|
2267
|
+
observed,
|
|
2268
|
+
red_index,
|
|
2269
|
+
std::memory_order_relaxed,
|
|
2270
|
+
std::memory_order_relaxed)) {
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
struct RedPathSearchOutcome {
|
|
2275
|
+
bool completed = false;
|
|
2276
|
+
bool skipped = false;
|
|
2277
|
+
bool found = false;
|
|
2278
|
+
std::size_t tested_green_paths = 0;
|
|
2279
|
+
SimplificationResult witness;
|
|
2280
|
+
};
|
|
2281
|
+
|
|
2282
|
+
RedPathSearchOutcome search_single_red_path(
|
|
2144
2283
|
const PDCode& code,
|
|
2145
|
-
const
|
|
2284
|
+
const Diagram& diagram,
|
|
2285
|
+
const DualGraph& base_graph,
|
|
2286
|
+
const std::vector<Endpoint>& red_path,
|
|
2287
|
+
const SimplifierOptions& options,
|
|
2288
|
+
const std::string& path_search_mode,
|
|
2289
|
+
int red_index,
|
|
2290
|
+
const std::atomic<int>* best_found_index) {
|
|
2291
|
+
RedPathSearchOutcome outcome;
|
|
2292
|
+
outcome.witness.path_search_mode = path_search_mode;
|
|
2293
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2294
|
+
outcome.skipped = true;
|
|
2295
|
+
return outcome;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
DualGraph graph = base_graph;
|
|
2299
|
+
const Endpoint start = red_path.front();
|
|
2300
|
+
const Endpoint end = red_path.back();
|
|
2301
|
+
const int start_face = graph.edge_to_face[endpoint_key(start)];
|
|
2302
|
+
const int start_opposite_face = graph.edge_to_face[endpoint_key(diagram.opposite(start))];
|
|
2303
|
+
const int end_face = graph.edge_to_face[endpoint_key(end)];
|
|
2304
|
+
const int end_opposite_face = graph.edge_to_face[endpoint_key(diagram.opposite(end))];
|
|
2305
|
+
const std::array<int, 2> sources{start_face, start_opposite_face};
|
|
2306
|
+
const std::array<int, 2> destinations{end_face, end_opposite_face};
|
|
2307
|
+
|
|
2308
|
+
for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
|
|
2309
|
+
const Endpoint endpoint = red_path[i];
|
|
2310
|
+
const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
|
|
2311
|
+
const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
|
|
2312
|
+
if (GraphEdge* edge = graph.mutable_edge(right_region, left_region)) {
|
|
2313
|
+
edge->weight = kBlockedWeight;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
std::vector<std::vector<int>> paths;
|
|
2318
|
+
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
2319
|
+
for (int source : sources) {
|
|
2320
|
+
for (int destination : destinations) {
|
|
2321
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2322
|
+
outcome.skipped = true;
|
|
2323
|
+
return outcome;
|
|
2324
|
+
}
|
|
2325
|
+
std::vector<std::vector<int>> found_paths;
|
|
2326
|
+
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2327
|
+
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2328
|
+
} else {
|
|
2329
|
+
found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
|
|
2330
|
+
}
|
|
2331
|
+
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
2332
|
+
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
2333
|
+
break;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
for (const auto& green_path : paths) {
|
|
2339
|
+
if (should_skip_parallel_red_path(red_index, best_found_index)) {
|
|
2340
|
+
outcome.skipped = true;
|
|
2341
|
+
return outcome;
|
|
2342
|
+
}
|
|
2343
|
+
++outcome.tested_green_paths;
|
|
2344
|
+
if (green_path.size() >= red_path.size()) {
|
|
2345
|
+
continue;
|
|
2346
|
+
}
|
|
2347
|
+
if (do_check(diagram, graph, red_path, green_path, Direction::Left, outcome.witness)) {
|
|
2348
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, outcome.witness)) {
|
|
2349
|
+
outcome.found = true;
|
|
2350
|
+
outcome.completed = true;
|
|
2351
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2352
|
+
return outcome;
|
|
2353
|
+
}
|
|
2354
|
+
clear_witness(outcome.witness);
|
|
2355
|
+
}
|
|
2356
|
+
if (do_check(diagram, graph, red_path, green_path, Direction::Right, outcome.witness)) {
|
|
2357
|
+
if (!options.require_applicable || witness_has_applicable_surgery(code, outcome.witness)) {
|
|
2358
|
+
outcome.found = true;
|
|
2359
|
+
outcome.completed = true;
|
|
2360
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2361
|
+
return outcome;
|
|
2362
|
+
}
|
|
2363
|
+
clear_witness(outcome.witness);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
outcome.completed = true;
|
|
2368
|
+
outcome.witness.tested_green_paths = outcome.tested_green_paths;
|
|
2369
|
+
return outcome;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
SimplificationResult merge_red_path_outcomes(
|
|
2373
|
+
const std::vector<RedPathSearchOutcome>& outcomes,
|
|
2374
|
+
const std::string& path_search_mode) {
|
|
2146
2375
|
SimplificationResult result;
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2376
|
+
result.path_search_mode = path_search_mode;
|
|
2377
|
+
|
|
2378
|
+
int first_found = -1;
|
|
2379
|
+
for (int i = 0; i < static_cast<int>(outcomes.size()); ++i) {
|
|
2380
|
+
if (outcomes[i].found) {
|
|
2381
|
+
first_found = i;
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2153
2384
|
}
|
|
2154
|
-
Diagram diagram(code);
|
|
2155
|
-
DualGraph graph(diagram);
|
|
2156
|
-
const auto red_lines = possible_red_lines(diagram);
|
|
2157
2385
|
|
|
2158
|
-
|
|
2386
|
+
const int limit = first_found >= 0 ? first_found : static_cast<int>(outcomes.size()) - 1;
|
|
2387
|
+
for (int i = 0; i <= limit; ++i) {
|
|
2388
|
+
if (!outcomes[i].completed && !outcomes[i].found) {
|
|
2389
|
+
std::ostringstream message;
|
|
2390
|
+
message << "Parallel brute-force search did not complete red path " << i;
|
|
2391
|
+
throw std::runtime_error(message.str());
|
|
2392
|
+
}
|
|
2159
2393
|
++result.tested_red_paths;
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
|
|
2172
|
-
const Endpoint endpoint = red_path[i];
|
|
2173
|
-
const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
|
|
2174
|
-
const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
|
|
2175
|
-
if (GraphEdge* edge = graph.mutable_edge(right_region, left_region)) {
|
|
2176
|
-
edge->weight = kBlockedWeight;
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
std::vector<std::vector<int>> paths;
|
|
2181
|
-
const int cutoff = static_cast<int>(red_path.size()) - 1;
|
|
2182
|
-
for (int source : sources) {
|
|
2183
|
-
for (int destination : destinations) {
|
|
2184
|
-
std::vector<std::vector<int>> found_paths;
|
|
2185
|
-
if (options.max_paths == -1 && !options.ban_heuristic) {
|
|
2186
|
-
found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
|
|
2187
|
-
} else {
|
|
2188
|
-
found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
|
|
2189
|
-
}
|
|
2190
|
-
paths.insert(paths.end(), found_paths.begin(), found_paths.end());
|
|
2191
|
-
if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
|
|
2192
|
-
break;
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2394
|
+
result.tested_green_paths += outcomes[i].tested_green_paths;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
if (first_found >= 0) {
|
|
2398
|
+
result = outcomes[first_found].witness;
|
|
2399
|
+
result.path_search_mode = path_search_mode;
|
|
2400
|
+
result.tested_red_paths = static_cast<std::size_t>(first_found + 1);
|
|
2401
|
+
result.tested_green_paths = 0;
|
|
2402
|
+
for (int i = 0; i <= first_found; ++i) {
|
|
2403
|
+
result.tested_green_paths += outcomes[i].tested_green_paths;
|
|
2195
2404
|
}
|
|
2405
|
+
}
|
|
2406
|
+
return result;
|
|
2407
|
+
}
|
|
2196
2408
|
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2409
|
+
SimplificationResult find_simplification_parallel_bruteforce(
|
|
2410
|
+
const PDCode& code,
|
|
2411
|
+
const Diagram& diagram,
|
|
2412
|
+
const DualGraph& base_graph,
|
|
2413
|
+
const std::vector<std::vector<Endpoint>>& red_lines,
|
|
2414
|
+
const SimplifierOptions& options,
|
|
2415
|
+
const std::string& path_search_mode,
|
|
2416
|
+
int worker_count) {
|
|
2417
|
+
std::vector<RedPathSearchOutcome> outcomes(red_lines.size());
|
|
2418
|
+
std::atomic<int> next_index(0);
|
|
2419
|
+
std::atomic<int> best_found_index(static_cast<int>(red_lines.size()));
|
|
2420
|
+
std::atomic<bool> failed(false);
|
|
2421
|
+
std::exception_ptr first_exception;
|
|
2422
|
+
std::mutex exception_mutex;
|
|
2423
|
+
|
|
2424
|
+
auto worker = [&]() {
|
|
2425
|
+
while (!failed.load(std::memory_order_relaxed)) {
|
|
2426
|
+
const int index = next_index.fetch_add(1, std::memory_order_relaxed);
|
|
2427
|
+
if (index >= static_cast<int>(red_lines.size())) {
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
if (should_skip_parallel_red_path(index, &best_found_index)) {
|
|
2431
|
+
outcomes[index].skipped = true;
|
|
2200
2432
|
continue;
|
|
2201
2433
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2434
|
+
try {
|
|
2435
|
+
RedPathSearchOutcome outcome = search_single_red_path(
|
|
2436
|
+
code,
|
|
2437
|
+
diagram,
|
|
2438
|
+
base_graph,
|
|
2439
|
+
red_lines[index],
|
|
2440
|
+
options,
|
|
2441
|
+
path_search_mode,
|
|
2442
|
+
index,
|
|
2443
|
+
&best_found_index);
|
|
2444
|
+
if (outcome.found) {
|
|
2445
|
+
record_parallel_found_index(index, &best_found_index);
|
|
2205
2446
|
}
|
|
2206
|
-
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2447
|
+
outcomes[index] = std::move(outcome);
|
|
2448
|
+
} catch (...) {
|
|
2449
|
+
failed.store(true, std::memory_order_relaxed);
|
|
2450
|
+
std::lock_guard<std::mutex> lock(exception_mutex);
|
|
2451
|
+
if (!first_exception) {
|
|
2452
|
+
first_exception = std::current_exception();
|
|
2211
2453
|
}
|
|
2212
|
-
|
|
2454
|
+
break;
|
|
2213
2455
|
}
|
|
2214
2456
|
}
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
std::vector<std::thread> workers;
|
|
2460
|
+
workers.reserve(static_cast<std::size_t>(worker_count));
|
|
2461
|
+
for (int i = 0; i < worker_count; ++i) {
|
|
2462
|
+
workers.emplace_back(worker);
|
|
2463
|
+
}
|
|
2464
|
+
for (std::thread& thread : workers) {
|
|
2465
|
+
thread.join();
|
|
2466
|
+
}
|
|
2467
|
+
if (first_exception) {
|
|
2468
|
+
std::rethrow_exception(first_exception);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
return merge_red_path_outcomes(outcomes, path_search_mode);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
} // namespace
|
|
2475
|
+
|
|
2476
|
+
SimplificationResult find_simplification(
|
|
2477
|
+
const PDCode& code,
|
|
2478
|
+
const SimplifierOptions& options) {
|
|
2479
|
+
SimplificationResult result;
|
|
2480
|
+
result.path_search_mode = search_mode_for_options(options);
|
|
2481
|
+
Diagram diagram(code);
|
|
2482
|
+
DualGraph base_graph(diagram);
|
|
2483
|
+
const auto red_lines = possible_red_lines(diagram);
|
|
2484
|
+
const bool brute_force_mode = options.max_paths == -1 && options.ban_heuristic;
|
|
2485
|
+
const int worker_count = brute_force_mode
|
|
2486
|
+
? selected_bruteforce_worker_count(
|
|
2487
|
+
options.max_threads,
|
|
2488
|
+
static_cast<int>(red_lines.size()))
|
|
2489
|
+
: 1;
|
|
2490
|
+
if (worker_count > 1) {
|
|
2491
|
+
return find_simplification_parallel_bruteforce(
|
|
2492
|
+
code,
|
|
2493
|
+
diagram,
|
|
2494
|
+
base_graph,
|
|
2495
|
+
red_lines,
|
|
2496
|
+
options,
|
|
2497
|
+
result.path_search_mode,
|
|
2498
|
+
worker_count);
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
for (const auto& red_path : red_lines) {
|
|
2502
|
+
++result.tested_red_paths;
|
|
2503
|
+
const RedPathSearchOutcome outcome = search_single_red_path(
|
|
2504
|
+
code,
|
|
2505
|
+
diagram,
|
|
2506
|
+
base_graph,
|
|
2507
|
+
red_path,
|
|
2508
|
+
options,
|
|
2509
|
+
result.path_search_mode,
|
|
2510
|
+
-1,
|
|
2511
|
+
nullptr);
|
|
2512
|
+
result.tested_green_paths += outcome.tested_green_paths;
|
|
2513
|
+
if (outcome.found) {
|
|
2514
|
+
SimplificationResult found = outcome.witness;
|
|
2515
|
+
found.path_search_mode = result.path_search_mode;
|
|
2516
|
+
found.tested_red_paths = result.tested_red_paths;
|
|
2517
|
+
found.tested_green_paths = result.tested_green_paths;
|
|
2518
|
+
return found;
|
|
2519
|
+
}
|
|
2215
2520
|
}
|
|
2216
2521
|
|
|
2217
2522
|
return result;
|
|
@@ -2228,6 +2533,7 @@ ReductionResult reduce_pd_code(
|
|
|
2228
2533
|
<< " known_crossingless_components=" << known_crossingless_components
|
|
2229
2534
|
<< " reduction_round=" << reduction_round
|
|
2230
2535
|
<< " max_paths=" << options.max_paths
|
|
2536
|
+
<< " max_thread=" << options.max_threads
|
|
2231
2537
|
<< " heuristic=" << (options.ban_heuristic ? "off" : "on");
|
|
2232
2538
|
emit_progress(options, message.str());
|
|
2233
2539
|
}
|
|
@@ -2256,7 +2562,8 @@ ReductionResult reduce_pd_code(
|
|
|
2256
2562
|
std::ostringstream message;
|
|
2257
2563
|
message << "round " << round
|
|
2258
2564
|
<< " search_start crossings=" << output.code.size()
|
|
2259
|
-
<< " mode=" << search_mode_for_options(search_options)
|
|
2565
|
+
<< " mode=" << search_mode_for_options(search_options)
|
|
2566
|
+
<< " max_thread=" << search_options.max_threads;
|
|
2260
2567
|
emit_progress(options, message.str());
|
|
2261
2568
|
}
|
|
2262
2569
|
SimplificationResult search = find_simplification(output.code, search_options);
|
|
@@ -2282,7 +2589,8 @@ ReductionResult reduce_pd_code(
|
|
|
2282
2589
|
{
|
|
2283
2590
|
std::ostringstream message;
|
|
2284
2591
|
message << "round " << round
|
|
2285
|
-
<< " brute_fallback_start crossings=" << output.code.size()
|
|
2592
|
+
<< " brute_fallback_start crossings=" << output.code.size()
|
|
2593
|
+
<< " max_thread=" << brute_options.max_threads;
|
|
2286
2594
|
emit_progress(options, message.str());
|
|
2287
2595
|
}
|
|
2288
2596
|
SimplificationResult brute = find_simplification(output.code, brute_options);
|
|
@@ -404,6 +404,7 @@ def _load_library() -> ctypes.CDLL:
|
|
|
404
404
|
ctypes.c_int,
|
|
405
405
|
ctypes.c_int,
|
|
406
406
|
ctypes.c_int,
|
|
407
|
+
ctypes.c_int,
|
|
407
408
|
ctypes.c_ulonglong,
|
|
408
409
|
ctypes.POINTER(ctypes.c_int),
|
|
409
410
|
ctypes.c_ulonglong,
|
|
@@ -422,12 +423,15 @@ def _run_one(
|
|
|
422
423
|
max_paths: int = -1,
|
|
423
424
|
ban_heuristic: bool = False,
|
|
424
425
|
reduction_round: int = -1,
|
|
426
|
+
max_thread: int = -1,
|
|
425
427
|
verbose: bool = False,
|
|
426
428
|
known_crossingless_components: int = 0,
|
|
427
429
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
428
430
|
) -> dict[str, Any]:
|
|
429
431
|
if reduction_round < -1:
|
|
430
432
|
raise ValueError("reduction_round must be -1 or a non-negative integer")
|
|
433
|
+
if max_thread < -1 or max_thread == 0:
|
|
434
|
+
raise ValueError("max_thread must be -1 or a positive integer")
|
|
431
435
|
library = _load_library()
|
|
432
436
|
removed_count = 0 if remove_crossings is None else len(remove_crossings)
|
|
433
437
|
removed_array = None
|
|
@@ -439,6 +443,7 @@ def _run_one(
|
|
|
439
443
|
int(max_paths),
|
|
440
444
|
1 if ban_heuristic else 0,
|
|
441
445
|
int(reduction_round),
|
|
446
|
+
int(max_thread),
|
|
442
447
|
1 if verbose else 0,
|
|
443
448
|
int(known_crossingless_components),
|
|
444
449
|
removed_array,
|
|
@@ -466,6 +471,7 @@ def simplify(
|
|
|
466
471
|
max_paths: int = -1,
|
|
467
472
|
ban_heuristic: bool = False,
|
|
468
473
|
reduction_round: int = -1,
|
|
474
|
+
max_thread: int = -1,
|
|
469
475
|
verbose: bool = False,
|
|
470
476
|
known_crossingless_components: int = 0,
|
|
471
477
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
@@ -477,6 +483,7 @@ def simplify(
|
|
|
477
483
|
max_paths=max_paths,
|
|
478
484
|
ban_heuristic=ban_heuristic,
|
|
479
485
|
reduction_round=reduction_round,
|
|
486
|
+
max_thread=max_thread,
|
|
480
487
|
verbose=verbose,
|
|
481
488
|
known_crossingless_components=known_crossingless_components,
|
|
482
489
|
remove_crossings=remove_crossings,
|
|
@@ -489,6 +496,7 @@ def simplify_many(
|
|
|
489
496
|
max_paths: int = -1,
|
|
490
497
|
ban_heuristic: bool = False,
|
|
491
498
|
reduction_round: int = -1,
|
|
499
|
+
max_thread: int = -1,
|
|
492
500
|
verbose: bool = False,
|
|
493
501
|
known_crossingless_components: int = 0,
|
|
494
502
|
remove_crossings: Optional[Sequence[int]] = None,
|
|
@@ -501,6 +509,7 @@ def simplify_many(
|
|
|
501
509
|
max_paths=max_paths,
|
|
502
510
|
ban_heuristic=ban_heuristic,
|
|
503
511
|
reduction_round=reduction_round,
|
|
512
|
+
max_thread=max_thread,
|
|
504
513
|
verbose=verbose,
|
|
505
514
|
known_crossingless_components=known_crossingless_components,
|
|
506
515
|
remove_crossings=remove_crossings,
|
|
@@ -517,12 +526,15 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
517
526
|
parser.add_argument("--max-paths", type=int, default=-1)
|
|
518
527
|
parser.add_argument("--ban-heuristic", action="store_true")
|
|
519
528
|
parser.add_argument("--reduction-round", type=int, default=-1)
|
|
529
|
+
parser.add_argument("--max-thread", type=int, default=-1)
|
|
520
530
|
parser.add_argument("--verbose", action="store_true")
|
|
521
531
|
parser.add_argument("--known-crossingless-components", type=int, default=0)
|
|
522
532
|
parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
|
|
523
533
|
args = parser.parse_args(argv)
|
|
524
534
|
if args.reduction_round < -1:
|
|
525
535
|
parser.error("--reduction-round must be -1 or a non-negative integer")
|
|
536
|
+
if args.max_thread < -1 or args.max_thread == 0:
|
|
537
|
+
parser.error("--max-thread must be -1 or a positive integer")
|
|
526
538
|
if args.pd_code and args.pd_code_option:
|
|
527
539
|
parser.error("pass either a positional PD code or --pd-code, not both")
|
|
528
540
|
pd_code_text = args.pd_code_option or args.pd_code
|
|
@@ -546,6 +558,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
546
558
|
max_paths=args.max_paths,
|
|
547
559
|
ban_heuristic=args.ban_heuristic,
|
|
548
560
|
reduction_round=args.reduction_round,
|
|
561
|
+
max_thread=args.max_thread,
|
|
549
562
|
verbose=args.verbose,
|
|
550
563
|
known_crossingless_components=args.known_crossingless_components,
|
|
551
564
|
remove_crossings=remove_crossings,
|
|
@@ -566,6 +579,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
566
579
|
max_paths=args.max_paths,
|
|
567
580
|
ban_heuristic=args.ban_heuristic,
|
|
568
581
|
reduction_round=args.reduction_round,
|
|
582
|
+
max_thread=args.max_thread,
|
|
569
583
|
verbose=args.verbose,
|
|
570
584
|
known_crossingless_components=args.known_crossingless_components,
|
|
571
585
|
remove_crossings=remove_crossings,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|