cunei-tools 1.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anonymous
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,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: cunei-tools
3
+ Version: 1.0.0
4
+ Summary: Cuneiform NLP utilities: word segmentation (with pre-trained Akkadian/Sumerian/Elamite models) and Latin-Unicode script conversion
5
+ Author-email: Chuanjun Zhou <chuanjunzhou@berkeley.edu>, Adam Anderson <adam.anderson@factgrid.eu>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ancient-world-citation-analysis/cunei-tools
8
+ Project-URL: Repository, https://github.com/ancient-world-citation-analysis/cunei-tools
9
+ Project-URL: Paper, https://openreview.net/forum?id=WdmrVUDpzt
10
+ Project-URL: Experiments, https://github.com/ancient-world-citation-analysis/cuneiform-experiments
11
+ Keywords: cuneiform,nlp,segmentation,akkadian,sumerian,elamite
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Text Processing :: Linguistic
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: convert
25
+ Requires-Dist: pandas; extra == "convert"
26
+ Provides-Extra: all
27
+ Requires-Dist: pandas; extra == "all"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # cunei-tools
33
+
34
+ Cuneiform NLP utilities for word segmentation and script conversion across Akkadian, Sumerian, and Elamite.
35
+
36
+ Developed as part of *"What Transfers Across Cuneiform? Script-Level Substrate and Language-Specific Regularities via Segmentation and Representation"* (Chuanjun Zhou and Adam Anderson, EMNLP 2026 Main).
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install cunei-tools
42
+ # Or from source:
43
+ git clone https://github.com/ancient-world-citation-analysis/cunei-tools
44
+ cd cunei-tools && pip install -e .
45
+ ```
46
+
47
+ ## Quick start with pre-trained models
48
+
49
+ Pre-trained segmentation models for all three languages ship with the
50
+ package (trained on the paper's corpora with each language's published
51
+ optimal threshold):
52
+
53
+ ```python
54
+ from cunei_tools import CuneiSeg
55
+ seg = CuneiSeg.pretrained("akk") # or "sux", "elx"
56
+ words = seg.segment(unicode_sign_stream)
57
+ ```
58
+
59
+ ## cunei-seg: Word Boundary Segmentation
60
+
61
+ Recovers word boundaries from unsegmented Unicode cuneiform using transitional probability. Achieves **F1 > 0.96** across three languages.
62
+
63
+ | Language | F1 | Precision | Recall | Threshold |
64
+ |----------|------|-----------|--------|-----------|
65
+ | Akkadian | 0.969 | 0.946 | 0.992 | 0.70 |
66
+ | Sumerian | 0.971 | 0.946 | 0.998 | 0.60 |
67
+ | Elamite | 0.973 | 0.993 | 0.955 | 0.90 |
68
+
69
+ ### Python API
70
+
71
+ ```python
72
+ from cunei_tools import CuneiSeg
73
+
74
+ # Train on segmented documents
75
+ seg = CuneiSeg(lang="akk")
76
+ seg.train([
77
+ "π’€€π’ˆΎ π’€π’Œ… π’Š­",
78
+ "𒂍 π’ˆΎ π’€€π’ˆΎ",
79
+ # ... your segmented corpus
80
+ ])
81
+
82
+ # Segment new text
83
+ seg.segment("π’€€π’ˆΎπ’€π’Œ…π’Š­")
84
+ # β†’ "π’€€π’ˆΎ π’€π’Œ… π’Š­"
85
+
86
+ # Find optimal threshold
87
+ metrics = seg.find_optimal_threshold(gold_documents)
88
+ print(metrics)
89
+ # β†’ {'threshold': 0.70, 'f1': 0.969, 'precision': 0.946, 'recall': 0.992}
90
+
91
+ # Save and load
92
+ seg.save("akk_model.json")
93
+ seg2 = CuneiSeg.load("akk_model.json")
94
+ ```
95
+
96
+ ### Command Line
97
+
98
+ ```bash
99
+ # Segment from file
100
+ cunei-seg --model akk_model.json --input unsegmented.txt --output segmented.txt
101
+
102
+ # Segment single text
103
+ cunei-seg --model akk_model.json --text "π’€€π’ˆΎπ’€π’Œ…π’Š­"
104
+ ```
105
+
106
+ ## cunei-conv: Script Conversion
107
+
108
+ Convert between Latin transliteration and Unicode cuneiform using 18,000+ sign mappings from Nuolenna and Akkademia.
109
+
110
+ ### Python API
111
+
112
+ ```python
113
+ from cunei_tools import CuneiConv
114
+
115
+ conv = CuneiConv()
116
+ conv.load_sign_lists() # from GitHub
117
+
118
+ # Latin β†’ Unicode
119
+ conv.to_unicode("a-na") # β†’ "π’€€ π’ˆΎ"
120
+ conv.to_unicode("Ε‘u-un-ki-ik") # β†’ "π’‹— π’Œ¦ π’†  π’……"
121
+
122
+ # Unicode β†’ Latin
123
+ conv.to_latin("π’€€π’ˆΎ") # β†’ "a na"
124
+
125
+ # Detailed conversion with quality check
126
+ result = conv.to_unicode_detailed("a-na ba-x")
127
+ # β†’ {'unicode': 'π’€€ π’ˆΎ 𒁀 x', 'clean': False, 'unmatched': ['x']}
128
+
129
+ # Batch conversion rate
130
+ stats = conv.conversion_rate(["a-na", "ba-ab", "unknown-sign"])
131
+ # β†’ {'total': 3, 'clean': 2, 'rate': 0.667, ...}
132
+
133
+ # Add manual corrections
134
+ conv.load_manual_corrections("corrections.csv")
135
+
136
+ # Save for offline use
137
+ conv.save("sign_dict.json")
138
+ conv2 = CuneiConv.load_from_file("sign_dict.json")
139
+ ```
140
+
141
+ ### Command Line
142
+
143
+ ```bash
144
+ # Convert to Unicode
145
+ cunei-conv --mode to-unicode --text "a-na ba-ab"
146
+
147
+ # Convert file
148
+ cunei-conv --mode to-unicode --input latin.txt --output unicode.txt --lang akk
149
+
150
+ # Reverse: Unicode to Latin
151
+ cunei-conv --mode to-latin --text "π’€€π’ˆΎ"
152
+ ```
153
+
154
+ ## Supported Languages
155
+
156
+ - **Akkadian** (`akk`) β€” Semitic, ~1.25M tokens tested
157
+ - **Sumerian** (`sux`) β€” language isolate, ~146K tokens tested
158
+ - **Elamite** (`elx`) β€” isolating/agglutinative, ~13K tokens tested
159
+
160
+ The tools are language-agnostic at their core β€” they work with any cuneiform script that uses Unicode code points in the U+12000–U+1254F range. Language-specific parameters (thresholds, normalization) improve accuracy.
161
+
162
+ ## Citation
163
+
164
+ If you use cunei-tools, please cite:
165
+
166
+ ```bibtex
167
+ @inproceedings{zhou-anderson-2026-transfers,
168
+ title = "What Transfers Across Cuneiform? Script-Level Substrate and Language-Specific Regularities via Segmentation and Representation",
169
+ author = "Zhou, Chuanjun and Anderson, Adam",
170
+ booktitle = "Proceedings of the 2026 Conference on Empirical Methods in Natural Language Processing",
171
+ year = "2026",
172
+ address = "Budapest, Hungary",
173
+ publisher = "Association for Computational Linguistics",
174
+ }
175
+ ```
176
+
177
+ ## Acknowledgments
178
+
179
+ Sign list data comes from [Nuolenna](https://github.com/situx/Nuolenna) and [Akkademia](https://github.com/gaigutherz/Akkademia).
180
+
181
+
182
+ ## Team
183
+
184
+ Chuanjun Zhou and Adam Anderson (University of California, Berkeley; FactGrid / TokenWorks LLC)
185
+
186
+ ## License
187
+
188
+ MIT
@@ -0,0 +1,157 @@
1
+ # cunei-tools
2
+
3
+ Cuneiform NLP utilities for word segmentation and script conversion across Akkadian, Sumerian, and Elamite.
4
+
5
+ Developed as part of *"What Transfers Across Cuneiform? Script-Level Substrate and Language-Specific Regularities via Segmentation and Representation"* (Chuanjun Zhou and Adam Anderson, EMNLP 2026 Main).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install cunei-tools
11
+ # Or from source:
12
+ git clone https://github.com/ancient-world-citation-analysis/cunei-tools
13
+ cd cunei-tools && pip install -e .
14
+ ```
15
+
16
+ ## Quick start with pre-trained models
17
+
18
+ Pre-trained segmentation models for all three languages ship with the
19
+ package (trained on the paper's corpora with each language's published
20
+ optimal threshold):
21
+
22
+ ```python
23
+ from cunei_tools import CuneiSeg
24
+ seg = CuneiSeg.pretrained("akk") # or "sux", "elx"
25
+ words = seg.segment(unicode_sign_stream)
26
+ ```
27
+
28
+ ## cunei-seg: Word Boundary Segmentation
29
+
30
+ Recovers word boundaries from unsegmented Unicode cuneiform using transitional probability. Achieves **F1 > 0.96** across three languages.
31
+
32
+ | Language | F1 | Precision | Recall | Threshold |
33
+ |----------|------|-----------|--------|-----------|
34
+ | Akkadian | 0.969 | 0.946 | 0.992 | 0.70 |
35
+ | Sumerian | 0.971 | 0.946 | 0.998 | 0.60 |
36
+ | Elamite | 0.973 | 0.993 | 0.955 | 0.90 |
37
+
38
+ ### Python API
39
+
40
+ ```python
41
+ from cunei_tools import CuneiSeg
42
+
43
+ # Train on segmented documents
44
+ seg = CuneiSeg(lang="akk")
45
+ seg.train([
46
+ "π’€€π’ˆΎ π’€π’Œ… π’Š­",
47
+ "𒂍 π’ˆΎ π’€€π’ˆΎ",
48
+ # ... your segmented corpus
49
+ ])
50
+
51
+ # Segment new text
52
+ seg.segment("π’€€π’ˆΎπ’€π’Œ…π’Š­")
53
+ # β†’ "π’€€π’ˆΎ π’€π’Œ… π’Š­"
54
+
55
+ # Find optimal threshold
56
+ metrics = seg.find_optimal_threshold(gold_documents)
57
+ print(metrics)
58
+ # β†’ {'threshold': 0.70, 'f1': 0.969, 'precision': 0.946, 'recall': 0.992}
59
+
60
+ # Save and load
61
+ seg.save("akk_model.json")
62
+ seg2 = CuneiSeg.load("akk_model.json")
63
+ ```
64
+
65
+ ### Command Line
66
+
67
+ ```bash
68
+ # Segment from file
69
+ cunei-seg --model akk_model.json --input unsegmented.txt --output segmented.txt
70
+
71
+ # Segment single text
72
+ cunei-seg --model akk_model.json --text "π’€€π’ˆΎπ’€π’Œ…π’Š­"
73
+ ```
74
+
75
+ ## cunei-conv: Script Conversion
76
+
77
+ Convert between Latin transliteration and Unicode cuneiform using 18,000+ sign mappings from Nuolenna and Akkademia.
78
+
79
+ ### Python API
80
+
81
+ ```python
82
+ from cunei_tools import CuneiConv
83
+
84
+ conv = CuneiConv()
85
+ conv.load_sign_lists() # from GitHub
86
+
87
+ # Latin β†’ Unicode
88
+ conv.to_unicode("a-na") # β†’ "π’€€ π’ˆΎ"
89
+ conv.to_unicode("Ε‘u-un-ki-ik") # β†’ "π’‹— π’Œ¦ π’†  π’……"
90
+
91
+ # Unicode β†’ Latin
92
+ conv.to_latin("π’€€π’ˆΎ") # β†’ "a na"
93
+
94
+ # Detailed conversion with quality check
95
+ result = conv.to_unicode_detailed("a-na ba-x")
96
+ # β†’ {'unicode': 'π’€€ π’ˆΎ 𒁀 x', 'clean': False, 'unmatched': ['x']}
97
+
98
+ # Batch conversion rate
99
+ stats = conv.conversion_rate(["a-na", "ba-ab", "unknown-sign"])
100
+ # β†’ {'total': 3, 'clean': 2, 'rate': 0.667, ...}
101
+
102
+ # Add manual corrections
103
+ conv.load_manual_corrections("corrections.csv")
104
+
105
+ # Save for offline use
106
+ conv.save("sign_dict.json")
107
+ conv2 = CuneiConv.load_from_file("sign_dict.json")
108
+ ```
109
+
110
+ ### Command Line
111
+
112
+ ```bash
113
+ # Convert to Unicode
114
+ cunei-conv --mode to-unicode --text "a-na ba-ab"
115
+
116
+ # Convert file
117
+ cunei-conv --mode to-unicode --input latin.txt --output unicode.txt --lang akk
118
+
119
+ # Reverse: Unicode to Latin
120
+ cunei-conv --mode to-latin --text "π’€€π’ˆΎ"
121
+ ```
122
+
123
+ ## Supported Languages
124
+
125
+ - **Akkadian** (`akk`) β€” Semitic, ~1.25M tokens tested
126
+ - **Sumerian** (`sux`) β€” language isolate, ~146K tokens tested
127
+ - **Elamite** (`elx`) β€” isolating/agglutinative, ~13K tokens tested
128
+
129
+ The tools are language-agnostic at their core β€” they work with any cuneiform script that uses Unicode code points in the U+12000–U+1254F range. Language-specific parameters (thresholds, normalization) improve accuracy.
130
+
131
+ ## Citation
132
+
133
+ If you use cunei-tools, please cite:
134
+
135
+ ```bibtex
136
+ @inproceedings{zhou-anderson-2026-transfers,
137
+ title = "What Transfers Across Cuneiform? Script-Level Substrate and Language-Specific Regularities via Segmentation and Representation",
138
+ author = "Zhou, Chuanjun and Anderson, Adam",
139
+ booktitle = "Proceedings of the 2026 Conference on Empirical Methods in Natural Language Processing",
140
+ year = "2026",
141
+ address = "Budapest, Hungary",
142
+ publisher = "Association for Computational Linguistics",
143
+ }
144
+ ```
145
+
146
+ ## Acknowledgments
147
+
148
+ Sign list data comes from [Nuolenna](https://github.com/situx/Nuolenna) and [Akkademia](https://github.com/gaigutherz/Akkademia).
149
+
150
+
151
+ ## Team
152
+
153
+ Chuanjun Zhou and Adam Anderson (University of California, Berkeley; FactGrid / TokenWorks LLC)
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,6 @@
1
+ """
2
+ cunei-tools: Cuneiform NLP utilities
3
+ """
4
+ __version__ = "1.0.0"
5
+ from cunei_tools.segmenter import CuneiSeg
6
+ from cunei_tools.converter import CuneiConv
@@ -0,0 +1,94 @@
1
+ """
2
+ Command-line interface for cunei-tools.
3
+
4
+ Usage:
5
+ # Segment unsegmented Unicode cuneiform
6
+ cunei-seg --model model.json --input text.txt --output segmented.txt
7
+ cunei-seg --model model.json --text "π’€€π’ˆΎπ’€π’Œ…"
8
+
9
+ # Convert transliteration to Unicode
10
+ cunei-conv --mode to-unicode --text "a-na ba-ab"
11
+ cunei-conv --mode to-latin --text "π’€€π’ˆΎ"
12
+ cunei-conv --mode to-unicode --input texts.txt --output converted.txt
13
+ """
14
+
15
+ import argparse
16
+ import sys
17
+
18
+
19
+ def seg_main():
20
+ parser = argparse.ArgumentParser(
21
+ description='cunei-seg: Word boundary segmentation for cuneiform'
22
+ )
23
+ parser.add_argument('--model', required=True, help='Path to trained model JSON')
24
+ parser.add_argument('--text', help='Single text to segment')
25
+ parser.add_argument('--input', help='Input file (one text per line)')
26
+ parser.add_argument('--output', help='Output file (default: stdout)')
27
+ parser.add_argument('--threshold', type=float, help='Override threshold')
28
+ args = parser.parse_args()
29
+
30
+ from cunei_tools import CuneiSeg
31
+ seg = CuneiSeg.load(args.model)
32
+
33
+ if args.threshold:
34
+ seg.threshold = args.threshold
35
+
36
+ if args.text:
37
+ print(seg.segment(args.text))
38
+ elif args.input:
39
+ with open(args.input, 'r', encoding='utf-8') as f:
40
+ lines = [l.strip() for l in f]
41
+ results = seg.segment_batch(lines)
42
+ out = open(args.output, 'w', encoding='utf-8') if args.output else sys.stdout
43
+ for r in results:
44
+ out.write(r + '\n')
45
+ if args.output:
46
+ out.close()
47
+ else:
48
+ parser.print_help()
49
+
50
+
51
+ def conv_main():
52
+ parser = argparse.ArgumentParser(
53
+ description='cunei-conv: Cuneiform transliteration converter'
54
+ )
55
+ parser.add_argument('--mode', required=True, choices=['to-unicode', 'to-latin'],
56
+ help='Conversion direction')
57
+ parser.add_argument('--dict', help='Path to sign dictionary JSON')
58
+ parser.add_argument('--text', help='Single text to convert')
59
+ parser.add_argument('--input', help='Input file (one text per line)')
60
+ parser.add_argument('--output', help='Output file (default: stdout)')
61
+ parser.add_argument('--lang', choices=['akk', 'sux', 'elx'],
62
+ help='Language for specialized normalization')
63
+ args = parser.parse_args()
64
+
65
+ from cunei_tools import CuneiConv
66
+
67
+ if args.dict:
68
+ conv = CuneiConv.load_from_file(args.dict)
69
+ else:
70
+ conv = CuneiConv()
71
+ conv.load_sign_lists()
72
+
73
+ def convert(text):
74
+ if args.mode == 'to-unicode':
75
+ return conv.to_unicode(text, lang=args.lang)
76
+ else:
77
+ return conv.to_latin(text)
78
+
79
+ if args.text:
80
+ print(convert(args.text))
81
+ elif args.input:
82
+ with open(args.input, 'r', encoding='utf-8') as f:
83
+ lines = [l.strip() for l in f]
84
+ out = open(args.output, 'w', encoding='utf-8') if args.output else sys.stdout
85
+ for line in lines:
86
+ out.write(convert(line) + '\n')
87
+ if args.output:
88
+ out.close()
89
+ else:
90
+ parser.print_help()
91
+
92
+
93
+ if __name__ == '__main__':
94
+ print("Use 'cunei-seg' or 'cunei-conv' commands.")