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.
- pd_code_delete_nugatory-0.0.1/LICENSE +21 -0
- pd_code_delete_nugatory-0.0.1/PKG-INFO +41 -0
- pd_code_delete_nugatory-0.0.1/README.md +20 -0
- pd_code_delete_nugatory-0.0.1/pd_code_delete_nugatory/__init__.py +0 -0
- pd_code_delete_nugatory-0.0.1/pd_code_delete_nugatory/main.py +160 -0
- pd_code_delete_nugatory-0.0.1/pyproject.toml +19 -0
|
@@ -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
|
+
```
|
|
File without changes
|
|
@@ -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"
|