cpp-pd-code-simplify-interface 0.1.2__tar.gz → 0.1.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpp-pd-code-simplify-interface
3
- Version: 0.1.2
3
+ Version: 0.1.9
4
4
  Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
5
5
  License-Expression: MIT
6
6
  Author: GGN_2015
@@ -34,8 +34,20 @@ use, the package compiles a cached local dynamic library through
34
34
  `cpp-simple-interface`; later calls reuse that library through `ctypes`. A C++14
35
35
  compiler compatible with `g++` must be available at runtime.
36
36
 
37
+ Runtime dependencies are handled per platform. On Windows, the interface uses
38
+ `objdump -p` or `dumpbin /DEPENDENTS` when available, then caches MinGW runtime
39
+ DLLs such as `libstdc++-6.dll`, `libgcc_s_*.dll`, and `libwinpthread-1.dll`
40
+ next to the generated DLL. On Linux it adds `$ORIGIN` rpath and can inspect
41
+ `ldd`; on macOS it adds `@loader_path` rpath and can inspect `otool -L`. Load
42
+ failures are wrapped with platform-specific dependency hints.
43
+
44
+ The core C++ source and header are not stored as permanent generated copies in
45
+ this subproject. The custom Poetry build backend syncs them from the repository
46
+ root during `poetry build`, embeds them in the wheel and sdist, then removes the
47
+ temporary copies from the working tree.
48
+
37
49
  Calls use the C++ library's default preprocessing pipeline: R1-move removal
38
- followed by nugatory-crossing removal.
50
+ followed by nugatory-crossing removal, then iterative mid-simplification.
39
51
 
40
52
  ## Example
41
53
 
@@ -44,12 +56,19 @@ import cpp_pd_code_simplify_interface as simplify
44
56
 
45
57
  pd_code = "PD[X[1,5,2,4],X[3,1,4,6],X[5,3,6,2]]"
46
58
  result = simplify.simplify(pd_code)
47
- print(result["simplification_found"])
59
+ print(result["final_pd_code"])
48
60
  ```
49
61
 
62
+ Returned `final_pd_code` strings are normalized so the smallest edge label is
63
+ `1`. This is applied only at the final JSON boundary; the C++ backend keeps its
64
+ internal numbering unchanged while simplifying.
65
+
50
66
  The default `max_paths=-1` uses deterministic heuristic green-path sampling in
51
67
  the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
52
- enumeration for a manageable input.
68
+ enumeration for a manageable input. Use `reduction_round=K` to cap applied
69
+ mid-simplification rounds; the default `-1` runs until stable. Use
70
+ `verbose=True` to forward timestamped C++ progress logs to stderr. Verbose log
71
+ lines use local wall-clock time in `YYYY-MM-DD HH:MM:SS` format.
53
72
 
54
73
  Batch use:
55
74
 
@@ -76,7 +95,7 @@ Python process needs a 64-bit compiler target.
76
95
  Command-line use also supports multi-line PD-code files:
77
96
 
78
97
  ```sh
79
- python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
98
+ python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1 --verbose
80
99
  ```
81
100
 
82
101
  ## Build And Publish
@@ -14,8 +14,20 @@ use, the package compiles a cached local dynamic library through
14
14
  `cpp-simple-interface`; later calls reuse that library through `ctypes`. A C++14
15
15
  compiler compatible with `g++` must be available at runtime.
16
16
 
17
+ Runtime dependencies are handled per platform. On Windows, the interface uses
18
+ `objdump -p` or `dumpbin /DEPENDENTS` when available, then caches MinGW runtime
19
+ DLLs such as `libstdc++-6.dll`, `libgcc_s_*.dll`, and `libwinpthread-1.dll`
20
+ next to the generated DLL. On Linux it adds `$ORIGIN` rpath and can inspect
21
+ `ldd`; on macOS it adds `@loader_path` rpath and can inspect `otool -L`. Load
22
+ failures are wrapped with platform-specific dependency hints.
23
+
24
+ The core C++ source and header are not stored as permanent generated copies in
25
+ this subproject. The custom Poetry build backend syncs them from the repository
26
+ root during `poetry build`, embeds them in the wheel and sdist, then removes the
27
+ temporary copies from the working tree.
28
+
17
29
  Calls use the C++ library's default preprocessing pipeline: R1-move removal
18
- followed by nugatory-crossing removal.
30
+ followed by nugatory-crossing removal, then iterative mid-simplification.
19
31
 
20
32
  ## Example
21
33
 
@@ -24,12 +36,19 @@ import cpp_pd_code_simplify_interface as simplify
24
36
 
25
37
  pd_code = "PD[X[1,5,2,4],X[3,1,4,6],X[5,3,6,2]]"
26
38
  result = simplify.simplify(pd_code)
27
- print(result["simplification_found"])
39
+ print(result["final_pd_code"])
28
40
  ```
29
41
 
42
+ Returned `final_pd_code` strings are normalized so the smallest edge label is
43
+ `1`. This is applied only at the final JSON boundary; the C++ backend keeps its
44
+ internal numbering unchanged while simplifying.
45
+
30
46
  The default `max_paths=-1` uses deterministic heuristic green-path sampling in
31
47
  the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
32
- enumeration for a manageable input.
48
+ enumeration for a manageable input. Use `reduction_round=K` to cap applied
49
+ mid-simplification rounds; the default `-1` runs until stable. Use
50
+ `verbose=True` to forward timestamped C++ progress logs to stderr. Verbose log
51
+ lines use local wall-clock time in `YYYY-MM-DD HH:MM:SS` format.
33
52
 
34
53
  Batch use:
35
54
 
@@ -56,7 +75,7 @@ Python process needs a 64-bit compiler target.
56
75
  Command-line use also supports multi-line PD-code files:
57
76
 
58
77
  ```sh
59
- python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1
78
+ python -m cpp_pd_code_simplify_interface --pd-file inputs.pd --max-paths -1 --verbose
60
79
  ```
61
80
 
62
81
  ## Build And Publish
@@ -15,17 +15,12 @@ from poetry.core.masonry import api as poetry_api
15
15
  PROJECT_ROOT = Path(__file__).resolve().parents[1]
16
16
  REPO_ROOT = PROJECT_ROOT.parents[1]
17
17
 
18
- SOURCE_FILES = [
18
+ EMBEDDED_SOURCE_FILES = [
19
19
  (
20
20
  REPO_ROOT / "src" / "pdcode_simplify.cpp",
21
21
  PROJECT_ROOT / "cpp_pd_code_simplify_interface" / "data" / "src" / "pdcode_simplify.cpp",
22
22
  "cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp",
23
23
  ),
24
- (
25
- PROJECT_ROOT / "cpp_pd_code_simplify_interface" / "data" / "src" / "native_interface.cpp",
26
- PROJECT_ROOT / "cpp_pd_code_simplify_interface" / "data" / "src" / "native_interface.cpp",
27
- "cpp_pd_code_simplify_interface/data/src/native_interface.cpp",
28
- ),
29
24
  (
30
25
  REPO_ROOT / "include" / "pdcode_simplify" / "pdcode_simplify.hpp",
31
26
  PROJECT_ROOT
@@ -38,23 +33,39 @@ SOURCE_FILES = [
38
33
  ),
39
34
  ]
40
35
 
36
+ STATIC_SOURCE_FILES = [
37
+ (
38
+ PROJECT_ROOT / "cpp_pd_code_simplify_interface" / "data" / "src" / "native_interface.cpp",
39
+ PROJECT_ROOT / "cpp_pd_code_simplify_interface" / "data" / "src" / "native_interface.cpp",
40
+ "cpp_pd_code_simplify_interface/data/src/native_interface.cpp",
41
+ ),
42
+ ]
43
+
44
+ SOURCE_FILES = EMBEDDED_SOURCE_FILES + STATIC_SOURCE_FILES
45
+
41
46
 
42
47
  def _sync_cpp_sources() -> None:
43
48
  missing = []
44
- for source, target, _ in SOURCE_FILES:
49
+ for source, target, _ in EMBEDDED_SOURCE_FILES:
45
50
  if source.exists():
46
- if source.resolve() != target.resolve():
51
+ if (
52
+ source.resolve() != target.resolve()
53
+ and (not target.exists() or source.read_bytes() != target.read_bytes())
54
+ ):
47
55
  target.parent.mkdir(parents=True, exist_ok=True)
48
56
  shutil.copyfile(source, target)
49
57
  elif not target.exists():
50
58
  missing.append((source, target))
59
+ for source, target, _ in STATIC_SOURCE_FILES:
60
+ if not source.exists() and not target.exists():
61
+ missing.append((source, target))
51
62
  if missing:
52
63
  details = ", ".join(f"{source} or {target}" for source, target in missing)
53
64
  raise FileNotFoundError(f"cpp-pd-code-simplify source files were not found: {details}")
54
65
 
55
66
 
56
67
  def _clean_cpp_sources() -> None:
57
- for source, target, _ in SOURCE_FILES:
68
+ for source, target, _ in EMBEDDED_SOURCE_FILES:
58
69
  if source.exists() and target.exists() and source.resolve() != target.resolve():
59
70
  target.unlink()
60
71
 
@@ -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>
@@ -49,7 +50,11 @@ struct GreenCrossing {
49
50
 
50
51
  struct SimplifierOptions {
51
52
  int max_paths = -1;
53
+ int max_threads = -1;
52
54
  bool ban_heuristic = false;
55
+ bool require_applicable = false;
56
+ bool verbose = false;
57
+ std::function<void(const std::string&)> progress;
53
58
  };
54
59
 
55
60
  struct LinkComponentSummary {
@@ -82,13 +87,6 @@ struct RandomInflationResult {
82
87
  int type_ii_moves = 0;
83
88
  };
84
89
 
85
- struct ReidemeisterSimplificationResult {
86
- PDCode code;
87
- std::size_t crossingless_components = 0;
88
- int type_i_moves = 0;
89
- int type_ii_moves = 0;
90
- };
91
-
92
90
  struct PDSimplificationResult {
93
91
  PDCode code;
94
92
  std::size_t crossingless_components = 0;
@@ -107,8 +105,27 @@ struct SimplificationResult {
107
105
  std::size_t tested_green_paths = 0;
108
106
  };
109
107
 
108
+ struct MidSimplificationApplyResult {
109
+ PDCode code;
110
+ std::size_t crossingless_components = 0;
111
+ };
112
+
113
+ struct ReductionResult {
114
+ PDCode code;
115
+ std::size_t crossingless_components = 0;
116
+ int mid_simplification_rounds = 0;
117
+ int heuristic_failover_rounds = 0;
118
+ int reidemeister_i_moves = 0;
119
+ int nugatory_crossing_moves = 0;
120
+ std::size_t tested_red_paths = 0;
121
+ std::size_t tested_green_paths = 0;
122
+ std::string last_path_search_mode;
123
+ bool stopped_by_round_limit = false;
124
+ };
125
+
110
126
  PDCODE_SIMPLIFY_API PDCode parse_pd_code(const std::string& text);
111
127
  PDCODE_SIMPLIFY_API std::string format_pd_code(const PDCode& code);
128
+ PDCODE_SIMPLIFY_API std::string format_final_pd_code(const PDCode& code);
112
129
  PDCODE_SIMPLIFY_API std::string format_endpoint(const Endpoint& endpoint);
113
130
  PDCODE_SIMPLIFY_API std::string format_direction(Direction direction);
114
131
 
@@ -130,10 +147,6 @@ PDCODE_SIMPLIFY_API RandomInflationResult randomly_increase_crossings(
130
147
  const PDCode& code,
131
148
  const RandomInflationOptions& options = RandomInflationOptions{});
132
149
 
133
- PDCODE_SIMPLIFY_API ReidemeisterSimplificationResult simplify_reidemeister_i_ii(
134
- const PDCode& code,
135
- std::size_t known_crossingless_components = 0);
136
-
137
150
  PDCODE_SIMPLIFY_API PDSimplificationResult simplify_pd_code(
138
151
  const PDCode& code,
139
152
  std::size_t known_crossingless_components = 0);
@@ -142,6 +155,17 @@ PDCODE_SIMPLIFY_API SimplificationResult find_simplification(
142
155
  const PDCode& code,
143
156
  const SimplifierOptions& options = SimplifierOptions{});
144
157
 
158
+ PDCODE_SIMPLIFY_API MidSimplificationApplyResult apply_simplification_witness(
159
+ const PDCode& code,
160
+ const SimplificationResult& result,
161
+ std::size_t known_crossingless_components = 0);
162
+
163
+ PDCODE_SIMPLIFY_API ReductionResult reduce_pd_code(
164
+ const PDCode& code,
165
+ std::size_t known_crossingless_components = 0,
166
+ const SimplifierOptions& options = SimplifierOptions{},
167
+ int reduction_round = -1);
168
+
145
169
  PDCODE_SIMPLIFY_API std::ostream& operator<<(std::ostream& out, const Endpoint& endpoint);
146
170
 
147
171
  } // namespace pdcode_simplify
@@ -2,13 +2,34 @@
2
2
 
3
3
  #include <cstdlib>
4
4
  #include <cstring>
5
+ #include <ctime>
5
6
  #include <exception>
7
+ #include <iomanip>
8
+ #include <iostream>
6
9
  #include <sstream>
7
10
  #include <string>
8
11
  #include <vector>
9
12
 
10
13
  namespace {
11
14
 
15
+ std::string local_timestamp() {
16
+ const std::time_t now = std::time(nullptr);
17
+ std::tm local{};
18
+ #if defined(_WIN32)
19
+ localtime_s(&local, &now);
20
+ #else
21
+ localtime_r(&now, &local);
22
+ #endif
23
+ std::ostringstream out;
24
+ out << std::put_time(&local, "%Y-%m-%d %H:%M:%S");
25
+ return out.str();
26
+ }
27
+
28
+ void print_progress_log(const std::string& message) {
29
+ std::cerr << "[pdcode-simplify " << local_timestamp() << "] "
30
+ << message << '\n';
31
+ }
32
+
12
33
  bool denotes_crossingless_unknot(const std::string& text) {
13
34
  std::string compact;
14
35
  for (char c : text) {
@@ -55,14 +76,14 @@ void append_component_counts(
55
76
  }
56
77
 
57
78
  std::string result_to_json(
58
- const pdcode_simplify::SimplificationResult& result,
79
+ const pdcode_simplify::ReductionResult& result,
59
80
  const pdcode_simplify::ComponentAnalysis& input_components,
60
- const pdcode_simplify::ComponentAnalysis* after_removal_components,
61
- const pdcode_simplify::PDSimplificationResult& pd_simplification,
62
- const pdcode_simplify::ComponentAnalysis& search_components) {
81
+ const pdcode_simplify::ComponentAnalysis& final_components,
82
+ const pdcode_simplify::ComponentAnalysis* after_removal_components) {
63
83
  std::ostringstream out;
64
84
  out << "{";
65
- out << "\"simplification_found\":" << (result.found ? "true" : "false") << ",";
85
+ out << "\"simplification_found\":"
86
+ << (result.mid_simplification_rounds > 0 ? "true" : "false") << ",";
66
87
  out << "\"input_components\":{";
67
88
  append_component_counts(out, input_components);
68
89
  out << "},";
@@ -71,50 +92,21 @@ std::string result_to_json(
71
92
  append_component_counts(out, *after_removal_components);
72
93
  out << "},";
73
94
  }
74
- out << "\"pd_simplification\":{"
75
- << "\"enabled\":true,"
76
- << "\"reidemeister_i_moves\":" << pd_simplification.reidemeister_i_moves << ","
77
- << "\"nugatory_crossing_moves\":" << pd_simplification.nugatory_crossing_moves << ","
78
- << "\"output_crossings\":" << pd_simplification.code.size()
79
- << "},";
80
- out << "\"search_components\":{";
81
- append_component_counts(out, search_components);
95
+ out << "\"final_pd_code\":\"" << json_escape(pdcode_simplify::format_final_pd_code(result.code))
96
+ << "\",";
97
+ out << "\"final_crossings\":" << result.code.size() << ",";
98
+ out << "\"final_components\":{";
99
+ append_component_counts(out, final_components);
82
100
  out << "},";
101
+ out << "\"mid_simplification_rounds\":" << result.mid_simplification_rounds << ",";
102
+ out << "\"heuristic_failover_rounds\":" << result.heuristic_failover_rounds << ",";
103
+ out << "\"reidemeister_i_moves\":" << result.reidemeister_i_moves << ",";
104
+ out << "\"nugatory_crossing_moves\":" << result.nugatory_crossing_moves << ",";
83
105
  out << "\"tested_red_paths\":" << result.tested_red_paths << ",";
84
106
  out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
85
- out << "\"path_search_mode\":\"" << json_escape(result.path_search_mode) << "\"";
86
- if (result.found) {
87
- out << ",";
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
- }
107
+ out << "\"last_path_search_mode\":\"" << json_escape(result.last_path_search_mode) << "\",";
108
+ out << "\"stopped_by_round_limit\":"
109
+ << (result.stopped_by_round_limit ? "true" : "false");
118
110
  out << "}";
119
111
  return out.str();
120
112
  }
@@ -139,6 +131,9 @@ char* pdcode_simplify_run_json(
139
131
  const char* pd_text,
140
132
  int max_paths,
141
133
  int ban_heuristic,
134
+ int reduction_round,
135
+ int max_thread,
136
+ int verbose,
142
137
  unsigned long long known_crossingless_components,
143
138
  const int* removed_crossings,
144
139
  unsigned long long removed_crossing_count) {
@@ -150,7 +145,12 @@ char* pdcode_simplify_run_json(
150
145
  const std::string text(pd_text);
151
146
  pdcode_simplify::SimplifierOptions options;
152
147
  options.max_paths = max_paths;
148
+ options.max_threads = max_thread;
153
149
  options.ban_heuristic = ban_heuristic != 0;
150
+ options.verbose = verbose != 0;
151
+ options.progress = [](const std::string& message) {
152
+ print_progress_log(message);
153
+ };
154
154
 
155
155
  const pdcode_simplify::PDCode code = pdcode_simplify::parse_pd_code(text);
156
156
  std::size_t crossingless = static_cast<std::size_t>(known_crossingless_components);
@@ -170,17 +170,15 @@ char* pdcode_simplify_run_json(
170
170
  code, removed, crossingless);
171
171
  }
172
172
 
173
- const auto pd_simplification = pdcode_simplify::simplify_pd_code(code, crossingless);
174
- const auto search_components = pdcode_simplify::analyze_components(
175
- pd_simplification.code,
176
- pd_simplification.crossingless_components);
177
- const auto result = pdcode_simplify::find_simplification(pd_simplification.code, options);
173
+ const auto result =
174
+ pdcode_simplify::reduce_pd_code(code, crossingless, options, reduction_round);
175
+ const auto final_components =
176
+ pdcode_simplify::analyze_components(result.code, result.crossingless_components);
178
177
  return copy_string(result_to_json(
179
178
  result,
180
179
  input_components,
181
- has_removal ? &after_removal_components : nullptr,
182
- pd_simplification,
183
- search_components));
180
+ final_components,
181
+ has_removal ? &after_removal_components : nullptr));
184
182
  } catch (const std::exception& error) {
185
183
  return copy_string(std::string("{\"error\":\"") + json_escape(error.what()) + "\"}");
186
184
  } catch (...) {