cppkh-interface 0.1.2__tar.gz → 0.2.0__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,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cppkh-interface
3
- Version: 0.1.2
4
- Summary: Python interface for cppkh Khovanov homology computation with runtime C++ compilation.
3
+ Version: 0.2.0
4
+ Summary: Dependency-free Python interface for cppkh with runtime C++ compilation.
5
5
  License-Expression: MIT
6
6
  Author: GGN_2015
7
7
  Author-email: neko@jlulug.org
@@ -12,10 +12,6 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Programming Language :: Python :: 3.14
15
- Requires-Dist: cpp-simple-interface (>=0.1.2)
16
- Requires-Dist: pd-code-de-r1
17
- Requires-Dist: pd-code-delete-nugatory
18
- Requires-Dist: pd-code-sanity (>=0.0.1)
19
15
  Project-URL: Documentation, https://github.com/GGN-2015/cppkh/blob/main/docs/PYTHON_PACKAGE.md
20
16
  Project-URL: Homepage, https://github.com/GGN-2015/cppkh
21
17
  Project-URL: Repository, https://github.com/GGN-2015/cppkh
@@ -26,9 +22,9 @@ Description-Content-Type: text/markdown
26
22
  `cppkh-interface` is a Python package for computing integer Khovanov homology
27
23
  with the C++ `cppkh` implementation.
28
24
 
29
- Version `0.1.2` resolves link crossing signs by tracing directed PD edge
30
- incidences with the SageMath convention. It does not infer signs from numeric
31
- arc-label ordering.
25
+ Version `0.1.3` has no runtime Python-package dependencies. Link crossing signs,
26
+ PD validation, R1 removal, and nugatory-crossing removal all use the bundled
27
+ canonical `cppkh` C++ source and its SageMath-compatible orientation rules.
32
28
 
33
29
  The package is compatible with the main `javakh-interface` function:
34
30
 
@@ -42,13 +38,13 @@ print(cppkh_interface.solve_many_khovanov([pd_code, pd_code]))
42
38
 
43
39
  Unlike wrappers that ship a prebuilt DLL or shared object, this package ships
44
40
  the `cppkh` C++ source file in built distributions and compiles a local
45
- executable on first use through `cpp-simple-interface`. The compiled executable
46
- is cached for later calls.
41
+ executable on first use using only Python's standard library. The compiled
42
+ executable is cached for later calls.
47
43
 
48
44
  In the repository checkout, the package does not keep a committed backup copy
49
45
  of the C++ source. The build backend copies `../../src/main.cpp` into the
50
- package data directory only while `poetry build` or `poetry publish --build` is
51
- running, then removes that temporary copy.
46
+ package data directory only while the PEP 517 build is running, then removes
47
+ that temporary copy.
52
48
 
53
49
  ## Install
54
50
 
@@ -56,17 +52,18 @@ running, then removes that temporary copy.
56
52
  pip install cppkh-interface
57
53
  ```
58
54
 
59
- A `g++` compatible compiler must be available at runtime. To select a compiler,
60
- set `CXX` before importing or calling the package:
55
+ A C++14 compiler must be available at runtime. The package looks at
56
+ `CPPKH_INTERFACE_CXX`, then `CXX`, then searches `PATH` for `g++`, `clang++`, or
57
+ `c++`. To select a compiler explicitly:
61
58
 
62
59
  ```sh
63
- CXX=clang++ python your_script.py
60
+ CPPKH_INTERFACE_CXX=clang++ python your_script.py
64
61
  ```
65
62
 
66
63
  Windows PowerShell:
67
64
 
68
65
  ```powershell
69
- $env:CXX = "C:\path\to\g++.exe"
66
+ $env:CPPKH_INTERFACE_CXX = "C:\path\to\g++.exe"
70
67
  python your_script.py
71
68
  ```
72
69
 
@@ -75,11 +72,13 @@ python your_script.py
75
72
  From this directory:
76
73
 
77
74
  ```sh
78
- poetry build
75
+ python -m build
79
76
  poetry publish
80
77
  ```
81
78
 
82
- Use `poetry publish --build` to build and upload in one command.
79
+ Do not use `poetry build` or `poetry publish --build`: Poetry's direct builder
80
+ bypasses the source-synchronizing PEP 517 backend. Build first with
81
+ `python -m build`, inspect/test the wheel, then publish the existing artifacts.
83
82
 
84
83
  For local testing:
85
84
 
@@ -0,0 +1,68 @@
1
+ # cppkh-interface
2
+
3
+ `cppkh-interface` is a Python package for computing integer Khovanov homology
4
+ with the C++ `cppkh` implementation.
5
+
6
+ Version `0.1.3` has no runtime Python-package dependencies. Link crossing signs,
7
+ PD validation, R1 removal, and nugatory-crossing removal all use the bundled
8
+ canonical `cppkh` C++ source and its SageMath-compatible orientation rules.
9
+
10
+ The package is compatible with the main `javakh-interface` function:
11
+
12
+ ```python
13
+ import cppkh_interface
14
+
15
+ pd_code = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
16
+ print(cppkh_interface.solve_khovanov(pd_code, de_r1=True, de_k8=True))
17
+ print(cppkh_interface.solve_many_khovanov([pd_code, pd_code]))
18
+ ```
19
+
20
+ Unlike wrappers that ship a prebuilt DLL or shared object, this package ships
21
+ the `cppkh` C++ source file in built distributions and compiles a local
22
+ executable on first use using only Python's standard library. The compiled
23
+ executable is cached for later calls.
24
+
25
+ In the repository checkout, the package does not keep a committed backup copy
26
+ of the C++ source. The build backend copies `../../src/main.cpp` into the
27
+ package data directory only while the PEP 517 build is running, then removes
28
+ that temporary copy.
29
+
30
+ ## Install
31
+
32
+ ```sh
33
+ pip install cppkh-interface
34
+ ```
35
+
36
+ A C++14 compiler must be available at runtime. The package looks at
37
+ `CPPKH_INTERFACE_CXX`, then `CXX`, then searches `PATH` for `g++`, `clang++`, or
38
+ `c++`. To select a compiler explicitly:
39
+
40
+ ```sh
41
+ CPPKH_INTERFACE_CXX=clang++ python your_script.py
42
+ ```
43
+
44
+ Windows PowerShell:
45
+
46
+ ```powershell
47
+ $env:CPPKH_INTERFACE_CXX = "C:\path\to\g++.exe"
48
+ python your_script.py
49
+ ```
50
+
51
+ ## Build And Publish
52
+
53
+ From this directory:
54
+
55
+ ```sh
56
+ python -m build
57
+ poetry publish
58
+ ```
59
+
60
+ Do not use `poetry build` or `poetry publish --build`: Poetry's direct builder
61
+ bypasses the source-synchronizing PEP 517 backend. Build first with
62
+ `python -m build`, inspect/test the wheel, then publish the existing artifacts.
63
+
64
+ For local testing:
65
+
66
+ ```sh
67
+ poetry run python -m cppkh_interface "[[1,5,2,4], [3,1,4,6], [5,3,6,2]]"
68
+ ```
@@ -1,207 +1,207 @@
1
- from __future__ import annotations
2
-
3
- import base64
4
- import hashlib
5
- import io
6
- import shutil
7
- import tarfile
8
- import tempfile
9
- import zipfile
10
- from pathlib import Path
11
-
12
- from poetry.core.masonry import api as poetry_api
13
-
14
-
15
- PROJECT_ROOT = Path(__file__).resolve().parents[1]
16
- REPO_ROOT = PROJECT_ROOT.parents[1]
17
- SOURCE = REPO_ROOT / "src" / "main.cpp"
18
- TARGET = PROJECT_ROOT / "cppkh_interface" / "data" / "src" / "main.cpp"
19
- WHEEL_SOURCE_NAME = "cppkh_interface/data/src/main.cpp"
20
-
21
-
22
- def _sync_cpp_source() -> None:
23
- if SOURCE.exists():
24
- TARGET.parent.mkdir(parents=True, exist_ok=True)
25
- shutil.copyfile(SOURCE, TARGET)
26
- return
27
- if TARGET.exists():
28
- return
29
- raise FileNotFoundError(
30
- "cppkh source file was not found. Expected either "
31
- f"{SOURCE} in the repository checkout or {TARGET} in the source tree."
32
- )
33
-
34
-
35
- def _clean_cpp_source() -> None:
36
- if TARGET.exists() and SOURCE.exists():
37
- TARGET.unlink()
38
-
39
-
40
- def _source_bytes() -> bytes:
41
- if TARGET.exists():
42
- return TARGET.read_bytes()
43
- if SOURCE.exists():
44
- return SOURCE.read_bytes()
45
- raise FileNotFoundError("cppkh source file was not available for packaging")
46
-
47
-
48
- def _wheel_hash_and_size(data: bytes) -> tuple[str, str]:
49
- digest = hashlib.sha256(data).digest()
50
- encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
51
- return f"sha256={encoded}", str(len(data))
52
-
53
-
54
- def _make_universal_wheel_metadata(data: bytes) -> bytes:
55
- text = data.decode("utf-8")
56
- lines = []
57
- tag_written = False
58
- root_written = False
59
- for line in text.splitlines():
60
- if line.startswith("Root-Is-Purelib:"):
61
- lines.append("Root-Is-Purelib: true")
62
- root_written = True
63
- elif line.startswith("Tag:"):
64
- if not tag_written:
65
- lines.append("Tag: py3-none-any")
66
- tag_written = True
67
- else:
68
- lines.append(line)
69
- if not root_written:
70
- lines.append("Root-Is-Purelib: true")
71
- if not tag_written:
72
- lines.append("Tag: py3-none-any")
73
- return ("\n".join(lines) + "\n").encode("utf-8")
74
-
75
-
76
- def _universal_wheel_path(wheel_path: Path, record_name: str) -> Path:
77
- dist_info = record_name.split("/", 1)[0]
78
- base = dist_info.removesuffix(".dist-info")
79
- return wheel_path.with_name(f"{base}-py3-none-any.whl")
80
-
81
-
82
- def _rewrite_wheel_with_source(wheel_path: Path) -> Path:
83
- source_data = _source_bytes()
84
- rows: list[tuple[str, str, str]] = []
85
-
86
- with zipfile.ZipFile(wheel_path, "r") as zin:
87
- record_name = next(
88
- (name for name in zin.namelist() if name.endswith(".dist-info/RECORD")),
89
- None,
90
- )
91
- if record_name is None:
92
- raise RuntimeError(f"wheel RECORD file not found in {wheel_path}")
93
- output_path = _universal_wheel_path(wheel_path, record_name)
94
- temp_path = output_path.with_name(output_path.name + ".tmp")
95
-
96
- with zipfile.ZipFile(temp_path, "w", compression=zipfile.ZIP_DEFLATED) as zout:
97
- wheel_metadata_name = record_name.removesuffix("RECORD") + "WHEEL"
98
-
99
- for item in zin.infolist():
100
- if item.filename in (WHEEL_SOURCE_NAME, record_name):
101
- continue
102
- data = zin.read(item.filename)
103
- if item.filename == wheel_metadata_name:
104
- data = _make_universal_wheel_metadata(data)
105
- zout.writestr(item, data)
106
- if not item.is_dir():
107
- digest, size = _wheel_hash_and_size(data)
108
- rows.append((item.filename, digest, size))
109
-
110
- source_info = zipfile.ZipInfo(WHEEL_SOURCE_NAME, date_time=(2016, 1, 1, 0, 0, 0))
111
- source_info.compress_type = zipfile.ZIP_DEFLATED
112
- zout.writestr(source_info, source_data)
113
- digest, size = _wheel_hash_and_size(source_data)
114
- rows.append((WHEEL_SOURCE_NAME, digest, size))
115
-
116
- rows.append((record_name, "", ""))
117
- record_text = "".join(f"{path},{digest},{size}\n" for path, digest, size in rows)
118
- record_info = zipfile.ZipInfo(record_name, date_time=(2016, 1, 1, 0, 0, 0))
119
- record_info.compress_type = zipfile.ZIP_DEFLATED
120
- zout.writestr(record_info, record_text.encode("utf-8"))
121
-
122
- temp_path.replace(output_path)
123
- if output_path != wheel_path and wheel_path.exists():
124
- wheel_path.unlink()
125
- return output_path
126
-
127
-
128
- def finalize_poetry_wheels() -> None:
129
- dist = PROJECT_ROOT / "dist"
130
- if not dist.exists():
131
- return
132
- for wheel_path in sorted(dist.glob("cppkh_interface-*.whl")):
133
- _rewrite_wheel_with_source(wheel_path)
134
-
135
-
136
- def _rewrite_sdist_with_source(sdist_path: Path) -> None:
137
- source_data = _source_bytes()
138
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz")
139
- temp_path = Path(temp_file.name)
140
- temp_file.close()
141
-
142
- try:
143
- with tarfile.open(sdist_path, "r:gz") as tin, tarfile.open(temp_path, "w:gz") as tout:
144
- names = tin.getnames()
145
- if not names:
146
- raise RuntimeError(f"sdist is empty: {sdist_path}")
147
- root = names[0].split("/", 1)[0]
148
- source_name = f"{root}/{WHEEL_SOURCE_NAME}"
149
-
150
- for member in tin.getmembers():
151
- if member.name == source_name:
152
- continue
153
- if member.isfile():
154
- extracted = tin.extractfile(member)
155
- if extracted is None:
156
- raise RuntimeError(f"could not read {member.name} from {sdist_path}")
157
- with extracted:
158
- tout.addfile(member, extracted)
159
- else:
160
- tout.addfile(member)
161
-
162
- source_info = tarfile.TarInfo(source_name)
163
- source_info.size = len(source_data)
164
- source_info.mtime = 0
165
- source_info.mode = 0o644
166
- tout.addfile(source_info, io.BytesIO(source_data))
167
-
168
- temp_path.replace(sdist_path)
169
- finally:
170
- if temp_path.exists():
171
- temp_path.unlink()
172
-
173
-
174
- def get_requires_for_build_wheel(config_settings=None):
175
- return poetry_api.get_requires_for_build_wheel(config_settings)
176
-
177
-
178
- def get_requires_for_build_sdist(config_settings=None):
179
- return poetry_api.get_requires_for_build_sdist(config_settings)
180
-
181
-
182
- def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
183
- _sync_cpp_source()
184
- try:
185
- return poetry_api.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
186
- finally:
187
- _clean_cpp_source()
188
-
189
-
190
- def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
191
- _sync_cpp_source()
192
- try:
193
- wheel_name = poetry_api.build_wheel(wheel_directory, config_settings, metadata_directory)
194
- final_path = _rewrite_wheel_with_source(Path(wheel_directory) / wheel_name)
195
- return final_path.name
196
- finally:
197
- _clean_cpp_source()
198
-
199
-
200
- def build_sdist(sdist_directory, config_settings=None):
201
- _sync_cpp_source()
202
- try:
203
- sdist_name = poetry_api.build_sdist(sdist_directory, config_settings)
204
- _rewrite_sdist_with_source(Path(sdist_directory) / sdist_name)
205
- return sdist_name
206
- finally:
207
- _clean_cpp_source()
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import hashlib
5
+ import io
6
+ import shutil
7
+ import tarfile
8
+ import tempfile
9
+ import zipfile
10
+ from pathlib import Path
11
+
12
+ from poetry.core.masonry import api as poetry_api
13
+
14
+
15
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
16
+ REPO_ROOT = PROJECT_ROOT.parents[1]
17
+ SOURCE = REPO_ROOT / "src" / "main.cpp"
18
+ TARGET = PROJECT_ROOT / "cppkh_interface" / "data" / "src" / "main.cpp"
19
+ WHEEL_SOURCE_NAME = "cppkh_interface/data/src/main.cpp"
20
+
21
+
22
+ def _sync_cpp_source() -> None:
23
+ if SOURCE.exists():
24
+ TARGET.parent.mkdir(parents=True, exist_ok=True)
25
+ shutil.copyfile(SOURCE, TARGET)
26
+ return
27
+ if TARGET.exists():
28
+ return
29
+ raise FileNotFoundError(
30
+ "cppkh source file was not found. Expected either "
31
+ f"{SOURCE} in the repository checkout or {TARGET} in the source tree."
32
+ )
33
+
34
+
35
+ def _clean_cpp_source() -> None:
36
+ if TARGET.exists() and SOURCE.exists():
37
+ TARGET.unlink()
38
+
39
+
40
+ def _source_bytes() -> bytes:
41
+ if TARGET.exists():
42
+ return TARGET.read_bytes()
43
+ if SOURCE.exists():
44
+ return SOURCE.read_bytes()
45
+ raise FileNotFoundError("cppkh source file was not available for packaging")
46
+
47
+
48
+ def _wheel_hash_and_size(data: bytes) -> tuple[str, str]:
49
+ digest = hashlib.sha256(data).digest()
50
+ encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
51
+ return f"sha256={encoded}", str(len(data))
52
+
53
+
54
+ def _make_universal_wheel_metadata(data: bytes) -> bytes:
55
+ text = data.decode("utf-8")
56
+ lines = []
57
+ tag_written = False
58
+ root_written = False
59
+ for line in text.splitlines():
60
+ if line.startswith("Root-Is-Purelib:"):
61
+ lines.append("Root-Is-Purelib: true")
62
+ root_written = True
63
+ elif line.startswith("Tag:"):
64
+ if not tag_written:
65
+ lines.append("Tag: py3-none-any")
66
+ tag_written = True
67
+ else:
68
+ lines.append(line)
69
+ if not root_written:
70
+ lines.append("Root-Is-Purelib: true")
71
+ if not tag_written:
72
+ lines.append("Tag: py3-none-any")
73
+ return ("\n".join(lines) + "\n").encode("utf-8")
74
+
75
+
76
+ def _universal_wheel_path(wheel_path: Path, record_name: str) -> Path:
77
+ dist_info = record_name.split("/", 1)[0]
78
+ base = dist_info.removesuffix(".dist-info")
79
+ return wheel_path.with_name(f"{base}-py3-none-any.whl")
80
+
81
+
82
+ def _rewrite_wheel_with_source(wheel_path: Path) -> Path:
83
+ source_data = _source_bytes()
84
+ rows: list[tuple[str, str, str]] = []
85
+
86
+ with zipfile.ZipFile(wheel_path, "r") as zin:
87
+ record_name = next(
88
+ (name for name in zin.namelist() if name.endswith(".dist-info/RECORD")),
89
+ None,
90
+ )
91
+ if record_name is None:
92
+ raise RuntimeError(f"wheel RECORD file not found in {wheel_path}")
93
+ output_path = _universal_wheel_path(wheel_path, record_name)
94
+ temp_path = output_path.with_name(output_path.name + ".tmp")
95
+
96
+ with zipfile.ZipFile(temp_path, "w", compression=zipfile.ZIP_DEFLATED) as zout:
97
+ wheel_metadata_name = record_name.removesuffix("RECORD") + "WHEEL"
98
+
99
+ for item in zin.infolist():
100
+ if item.filename in (WHEEL_SOURCE_NAME, record_name):
101
+ continue
102
+ data = zin.read(item.filename)
103
+ if item.filename == wheel_metadata_name:
104
+ data = _make_universal_wheel_metadata(data)
105
+ zout.writestr(item, data)
106
+ if not item.is_dir():
107
+ digest, size = _wheel_hash_and_size(data)
108
+ rows.append((item.filename, digest, size))
109
+
110
+ source_info = zipfile.ZipInfo(WHEEL_SOURCE_NAME, date_time=(2016, 1, 1, 0, 0, 0))
111
+ source_info.compress_type = zipfile.ZIP_DEFLATED
112
+ zout.writestr(source_info, source_data)
113
+ digest, size = _wheel_hash_and_size(source_data)
114
+ rows.append((WHEEL_SOURCE_NAME, digest, size))
115
+
116
+ rows.append((record_name, "", ""))
117
+ record_text = "".join(f"{path},{digest},{size}\n" for path, digest, size in rows)
118
+ record_info = zipfile.ZipInfo(record_name, date_time=(2016, 1, 1, 0, 0, 0))
119
+ record_info.compress_type = zipfile.ZIP_DEFLATED
120
+ zout.writestr(record_info, record_text.encode("utf-8"))
121
+
122
+ temp_path.replace(output_path)
123
+ if output_path != wheel_path and wheel_path.exists():
124
+ wheel_path.unlink()
125
+ return output_path
126
+
127
+
128
+ def finalize_poetry_wheels() -> None:
129
+ dist = PROJECT_ROOT / "dist"
130
+ if not dist.exists():
131
+ return
132
+ for wheel_path in sorted(dist.glob("cppkh_interface-*.whl")):
133
+ _rewrite_wheel_with_source(wheel_path)
134
+
135
+
136
+ def _rewrite_sdist_with_source(sdist_path: Path) -> None:
137
+ source_data = _source_bytes()
138
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tar.gz")
139
+ temp_path = Path(temp_file.name)
140
+ temp_file.close()
141
+
142
+ try:
143
+ with tarfile.open(sdist_path, "r:gz") as tin, tarfile.open(temp_path, "w:gz") as tout:
144
+ names = tin.getnames()
145
+ if not names:
146
+ raise RuntimeError(f"sdist is empty: {sdist_path}")
147
+ root = names[0].split("/", 1)[0]
148
+ source_name = f"{root}/{WHEEL_SOURCE_NAME}"
149
+
150
+ for member in tin.getmembers():
151
+ if member.name == source_name:
152
+ continue
153
+ if member.isfile():
154
+ extracted = tin.extractfile(member)
155
+ if extracted is None:
156
+ raise RuntimeError(f"could not read {member.name} from {sdist_path}")
157
+ with extracted:
158
+ tout.addfile(member, extracted)
159
+ else:
160
+ tout.addfile(member)
161
+
162
+ source_info = tarfile.TarInfo(source_name)
163
+ source_info.size = len(source_data)
164
+ source_info.mtime = 0
165
+ source_info.mode = 0o644
166
+ tout.addfile(source_info, io.BytesIO(source_data))
167
+
168
+ temp_path.replace(sdist_path)
169
+ finally:
170
+ if temp_path.exists():
171
+ temp_path.unlink()
172
+
173
+
174
+ def get_requires_for_build_wheel(config_settings=None):
175
+ return poetry_api.get_requires_for_build_wheel(config_settings)
176
+
177
+
178
+ def get_requires_for_build_sdist(config_settings=None):
179
+ return poetry_api.get_requires_for_build_sdist(config_settings)
180
+
181
+
182
+ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
183
+ _sync_cpp_source()
184
+ try:
185
+ return poetry_api.prepare_metadata_for_build_wheel(metadata_directory, config_settings)
186
+ finally:
187
+ _clean_cpp_source()
188
+
189
+
190
+ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
191
+ _sync_cpp_source()
192
+ try:
193
+ wheel_name = poetry_api.build_wheel(wheel_directory, config_settings, metadata_directory)
194
+ final_path = _rewrite_wheel_with_source(Path(wheel_directory) / wheel_name)
195
+ return final_path.name
196
+ finally:
197
+ _clean_cpp_source()
198
+
199
+
200
+ def build_sdist(sdist_directory, config_settings=None):
201
+ _sync_cpp_source()
202
+ try:
203
+ sdist_name = poetry_api.build_sdist(sdist_directory, config_settings)
204
+ _rewrite_sdist_with_source(Path(sdist_directory) / sdist_name)
205
+ return sdist_name
206
+ finally:
207
+ _clean_cpp_source()
@@ -1,25 +1,29 @@
1
- from .main import (
2
- CppkhInterfaceError,
3
- compile_cppkh,
4
- compute_many_pd,
5
- compute_pd,
6
- get_cppkh_executable,
7
- normalize_pd_code,
8
- normalize_pd_codes,
9
- simplify_pd,
10
- solve_many_khovanov,
11
- solve_khovanov,
12
- )
13
-
14
- __all__ = [
15
- "CppkhInterfaceError",
16
- "compile_cppkh",
17
- "compute_many_pd",
18
- "compute_pd",
19
- "get_cppkh_executable",
20
- "normalize_pd_code",
21
- "normalize_pd_codes",
22
- "simplify_pd",
23
- "solve_many_khovanov",
24
- "solve_khovanov",
25
- ]
1
+ from .main import (
2
+ CppkhInterfaceError,
3
+ compile_cppkh,
4
+ compile_cppkh_shared,
5
+ compute_signed_variants,
6
+ compute_many_pd,
7
+ compute_pd,
8
+ get_cppkh_executable,
9
+ normalize_pd_code,
10
+ normalize_pd_codes,
11
+ simplify_pd,
12
+ solve_many_khovanov,
13
+ solve_khovanov,
14
+ )
15
+
16
+ __all__ = [
17
+ "CppkhInterfaceError",
18
+ "compile_cppkh",
19
+ "compile_cppkh_shared",
20
+ "compute_signed_variants",
21
+ "compute_many_pd",
22
+ "compute_pd",
23
+ "get_cppkh_executable",
24
+ "normalize_pd_code",
25
+ "normalize_pd_codes",
26
+ "simplify_pd",
27
+ "solve_many_khovanov",
28
+ "solve_khovanov",
29
+ ]
@@ -1,4 +1,4 @@
1
- from .main import main
2
-
3
- if __name__ == "__main__":
4
- raise SystemExit(main())
1
+ from .main import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())