pd-code-delete-nugatory 0.0.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TopologicalKnotIndexer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: pd-code-delete-nugatory
3
+ Version: 0.0.1
4
+ Summary: delete all nugatory crossing from a pd_code (link or knot).
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: GGN_2015
8
+ Author-email: neko@jlulug.org
9
+ Requires-Python: >=3.10
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: pd_code_de_r1
18
+ Requires-Dist: pd_code_pre_nxt
19
+ Description-Content-Type: text/markdown
20
+
21
+ # pd_code_delete_nugatory
22
+ delete all nugatory crossing from a pd_code (link or knot).
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install pd-code-delete-nugatory
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ import pd_code_delete_nugatory
34
+
35
+
36
+ pd_code = [[8, 11, 9, 12], [12, 9, 13, 10], [10, 13, 11, 14], [7, 14, 8, 1], [4, 1, 5, 2], [2, 5, 3, 6], [6, 3, 7, 4]]
37
+
38
+ # output a 6-crossing knot
39
+ print(pd_code_delete_nugatory.erase_all_nugatory(pd_code))
40
+ ```
41
+
@@ -0,0 +1,20 @@
1
+ # pd_code_delete_nugatory
2
+ delete all nugatory crossing from a pd_code (link or knot).
3
+
4
+ ## Install
5
+
6
+ ```bash
7
+ pip install pd-code-delete-nugatory
8
+ ```
9
+
10
+ ## Usage
11
+
12
+ ```python
13
+ import pd_code_delete_nugatory
14
+
15
+
16
+ pd_code = [[8, 11, 9, 12], [12, 9, 13, 10], [10, 13, 11, 14], [7, 14, 8, 1], [4, 1, 5, 2], [2, 5, 3, 6], [6, 3, 7, 4]]
17
+
18
+ # output a 6-crossing knot
19
+ print(pd_code_delete_nugatory.erase_all_nugatory(pd_code))
20
+ ```
@@ -0,0 +1,160 @@
1
+ import pd_code_de_r1
2
+ import pd_code_pre_nxt
3
+ from typing import Optional, Any
4
+ import json
5
+
6
+ def keep_safe(nxt:dict[Any, set], v):
7
+ if nxt.get(v) is None:
8
+ nxt[v] = set()
9
+
10
+ def add_single_edge(nxt:dict[Any, set], v, u):
11
+ keep_safe(nxt, v)
12
+ if u not in nxt[v]:
13
+ nxt[v].add(u)
14
+
15
+ def add_double_edge(nxt:dict[Any, set], v1, v2):
16
+ for v, u in [(v1, v2), (v2, v1)]:
17
+ add_single_edge(nxt, v, u)
18
+
19
+ def get_base_graph(weak_pd_code:list[list]):
20
+
21
+ # 获得邻接表
22
+ nxt = dict[Any, set]()
23
+ for i in range(len(weak_pd_code)):
24
+ for item in weak_pd_code[i]:
25
+ add_double_edge(nxt, item, f"c_{i}")
26
+ return nxt
27
+
28
+ def dfs_1(node, nxt:dict[Any, set], vis:set):
29
+ if node in vis:
30
+ return
31
+ vis.add(node)
32
+ for nxt_node in nxt[node]:
33
+ dfs_1(nxt_node, nxt, vis)
34
+
35
+ def graph_cc_cnt(weak_pd_code:list[list]):
36
+ nxt = get_base_graph(weak_pd_code)
37
+
38
+ # 获取节点集合
39
+ node_set = [
40
+ item
41
+ for item in nxt]
42
+
43
+ # 开始 dfs
44
+ vis = set()
45
+ cnt = 0
46
+ for node in node_set:
47
+ if node not in vis:
48
+ cnt += 1
49
+ dfs_1(node, nxt, vis)
50
+ return cnt
51
+
52
+ def is_nugatory(pd_code:list[list], idx:int) -> bool:
53
+
54
+ # 由于没有 r1-move 因此一定每个交叉点四个编号互不相同
55
+ if len(set(pd_code[idx])) != 4:
56
+ raise AssertionError()
57
+
58
+ #获得直接删除这个交叉点后的不完整的 pd_code
59
+ bad_pd_code = pd_code[:idx] + pd_code[idx+1:]
60
+
61
+ # 如果底图的连通分支数增多,则说明是 nugatory
62
+ return graph_cc_cnt(bad_pd_code) > graph_cc_cnt(pd_code)
63
+
64
+ def get_index_of_nugatory(pd_code:list[list]) -> Optional[int]:
65
+ for i in range(len(pd_code)):
66
+ if is_nugatory(pd_code, i):
67
+ return i
68
+ return None
69
+
70
+ # 把 vf 改成 vt,其余不变
71
+ def replace_val(bad_pd_code:list[list], vf, vt)->list[list]:
72
+ return [
73
+ [item if item != vf else vt for item in crossing]
74
+ for crossing in bad_pd_code
75
+ ]
76
+
77
+ # 这里的 nxt 只有一个元素
78
+ # 是单向的 nxt
79
+ def dfs_2(num, vis:set, nxt:dict, new_num:dict):
80
+ if num in vis:
81
+ return
82
+ vis.add(num)
83
+ new_num[num] = len(vis) # 当前元素个数就是编号
84
+ if nxt.get(num) is None:
85
+ raise AssertionError()
86
+
87
+ # 遍历所有后继
88
+ for nxt_num in nxt[num]:
89
+ if nxt_num not in vis:
90
+ dfs_2(nxt_num, vis, nxt, new_num)
91
+
92
+ # 从 1 开始重新给所有元素编号
93
+ def renumber(pd_code:list[list]) -> list[list]:
94
+ pd_code = json.loads(json.dumps(pd_code))
95
+ num_set = set([
96
+ item
97
+ for crossing in pd_code
98
+ for item in crossing
99
+ ])
100
+
101
+ # 这里不能直接用 get_pre_nxt
102
+ # 因为 renumber 之前有不连贯的编号
103
+ # 而 get_pre_nxt 要求编号必须连贯(所以这里使用无向图 dfs)
104
+ nxt = dict()
105
+ for crossing in pd_code:
106
+ add_double_edge(nxt, crossing[0], crossing[2])
107
+ add_double_edge(nxt, crossing[1], crossing[3])
108
+
109
+ vis = set()
110
+ new_num = dict()
111
+ for num in num_set:
112
+ if num not in vis:
113
+ dfs_2(num, vis, nxt, new_num)
114
+
115
+ # 每个节点都必须有新的编号
116
+ if len(new_num) != len(num_set):
117
+ raise AssertionError()
118
+
119
+ return [
120
+ [new_num[item] for item in crossing]
121
+ for crossing in pd_code
122
+ ]
123
+
124
+ def erase_one_nugatory(pd_code:list[list], index:int) -> list[list]:
125
+ if len(set(pd_code[index])) != 4:
126
+ raise AssertionError()
127
+
128
+ ax, bx, cx, dx = pd_code[index]
129
+ pre, nxt = pd_code_pre_nxt.get_pre_nxt(pd_code)
130
+
131
+ # 获取当前连通分支
132
+ loop = [ax]
133
+ while True:
134
+ loop.append(nxt[loop[-1]])
135
+ if loop[-1] == ax: # 找到了第一个元素第二次出现,退出
136
+ loop = loop[:-1]
137
+ break
138
+
139
+ # 四个节点一定在同一个连通分支
140
+ if not (set([ax, bx, cx, dx]).issubset(set(loop))):
141
+ raise AssertionError()
142
+
143
+ # 直接删掉这个 crossing
144
+ bad_pd_code = pd_code[:index] + pd_code[index+1:]
145
+ new_pd_code = replace_val(bad_pd_code, ax, cx)
146
+ new_pd_code = replace_val(new_pd_code, dx, bx)
147
+ return renumber(new_pd_code)
148
+
149
+ def erase_all_nugatory(pd_code:list[list]):
150
+ pd_code = pd_code_de_r1.de_r1(pd_code)
151
+ while True:
152
+ index = get_index_of_nugatory(pd_code)
153
+ if index is None:
154
+ break
155
+ pd_code = erase_one_nugatory(pd_code, index)
156
+ return pd_code
157
+
158
+ if __name__ == "__main__":
159
+ pd_code = [[8, 11, 9, 12], [12, 9, 13, 10], [10, 13, 11, 14], [7, 14, 8, 1], [4, 1, 5, 2], [2, 5, 3, 6], [6, 3, 7, 4]]
160
+ print(erase_all_nugatory(pd_code))
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "pd-code-delete-nugatory"
3
+ version = "0.0.1"
4
+ description = "delete all nugatory crossing from a pd_code (link or knot)."
5
+ authors = [
6
+ {name = "GGN_2015",email = "neko@jlulug.org"}
7
+ ]
8
+ license = {text = "MIT"}
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "pd_code_de_r1",
13
+ "pd_code_pre_nxt"
14
+ ]
15
+
16
+
17
+ [build-system]
18
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
19
+ build-backend = "poetry.core.masonry.api"