daniel-wojahn commited on
Commit
46a7286
·
1 Parent(s): 671c107

codebase cleanup

Browse files
academic_article.md DELETED
@@ -1,122 +0,0 @@
1
- # A Computational Toolkit for Tibetan Textual Analysis: Methods and Applications of the Tibetan Text Metrics (TTM) Application
2
-
3
- **Abstract:** The study of Tibetan textual traditions, with its vast and complex corpus, presents unique challenges for quantitative analysis. Traditional philological methods, while essential, can be enhanced by computational tools that reveal large-scale patterns of similarity and variation. This paper introduces the Tibetan Text Metrics (TTM) web application, an accessible open-source toolkit designed to bridge this gap. TTM provides a suite of similarity metrics—including Jaccard, Normalized Longest Common Subsequence (LCS), TF-IDF Cosine Similarity, and semantic analysis using advanced embedding models (FastText and SentenceTransformers). A novel feature of the application is its AI-powered interpretation engine, which translates quantitative data into scholarly insights, making complex metrics accessible to a broader audience. By offering a user-friendly interface for sophisticated textual analysis, TTM empowers researchers to explore manuscript relationships, track textual transmission, and uncover new avenues for inquiry within Tibetan studies and the broader digital humanities landscape.
4
-
5
- ## 1. Introduction
6
-
7
- ### 1.1. The Challenge of Tibetan Textual Scholarship
8
-
9
- The Tibetan literary corpus is one of the world's most extensive, encompassing centuries of philosophy, history, and religious doctrine. The transmission of these texts has been a complex process, involving manual copying that resulted in a rich but challenging textual landscape of divergent manuscript lineages. This challenge is exemplified by the development of the TTM application itself, which originated from the analysis of multiple editions of the 17th-century legal text, *The Pronouncements in Sixteen Chapters* (*zhal lce bcu drug*). An initial attempt to create a critical edition using standard collation software like CollateX proved untenable; the variations between editions were so substantial that they produced a convoluted apparatus that obscured, rather than clarified, the texts' relationships. It became clear that a different approach was needed—one that could move beyond one-to-one textual comparison to provide a higher-level, quantitative overview of textual similarity. TTM was developed to meet this need, providing a toolkit to assess relationships at the chapter level and reveal the broader patterns of textual evolution that traditional methods might miss.
10
-
11
- ### 1.2. Digital Humanities and Under-Resourced Languages
12
-
13
- The rise of digital humanities has brought a wealth of computational tools to literary analysis. However, many of these tools are designed for well-resourced languages like English, leaving languages with fewer digital resources, such as Tibetan, underserved. The unique characteristics of the Tibetan script and language, including its syllabic nature and complex orthography, require specialized tools for effective processing and analysis. The Tibetan Text Metrics (TTM) project addresses this need by providing a tailored solution that respects the linguistic nuances of Tibetan, thereby making a vital contribution to the growing field of global digital humanities.
14
-
15
- ### 1.3. The Tibetan Text Metrics (TTM) Application
16
-
17
- This paper introduces the Tibetan Text Metrics (TTM) web application, a user-friendly, open-source tool designed to make sophisticated textual analysis accessible to scholars of Tibetan, regardless of their technical background. The application empowers researchers to move beyond manual comparison by providing a suite of computational metrics that reveal distinct aspects of textual relationships—from direct lexical overlap (Jaccard similarity) and shared narrative structure (Normalized LCS) to thematic parallels (TF-IDF) and deep semantic connections (FastText and Transformer-based embeddings). This article will detail the methodologies underpinning these metrics, describe the application's key features—including its novel AI-powered interpretation engine—and demonstrate its practical utility through a case study. By doing so, we aim to show how TTM can serve as a valuable assistant in the scholar's toolkit, augmenting traditional research methods and opening up new possibilities for the study of Tibetan textual history.
18
-
19
- ## 2. Methodology: A Multi-faceted Approach to Text Similarity
20
-
21
- To provide a holistic view of textual relationships, the TTM application employs a multi-faceted methodology that combines lexical, structural, and semantic analysis. This approach is built upon a foundation of Tibetan-specific text processing, ensuring that each metric is applied in a linguistically sound manner.
22
-
23
- ### 2.1. Text Pre-processing and Segmentation
24
-
25
- Meaningful computational analysis begins with careful pre-processing. The TTM application automates several crucial steps to prepare texts for comparison.
26
-
27
- **Segmentation:** Comparing entire texts, especially long ones, can obscure significant internal variations. TTM therefore defaults to a chapter-level analysis. It automatically segments texts using the Tibetan *sbrul shad* (༈), a common marker for section breaks. This allows for a more granular comparison, revealing similarities and differences at a structural level that mirrors the text's own divisions. If no marker is found, the application treats the entire file as a single segment and issues a warning.
28
-
29
- **Tokenization:** To analyze a text computationally, it must be broken down into individual units, or tokens. Given the syllabic nature of the Tibetan script, where morphemes are delimited by a *tsheg* (་), simple whitespace tokenization is inadequate. TTM leverages the `botok` library, a state-of-the-art tokenizer for Tibetan, which accurately identifies word boundaries, ensuring that the subsequent analysis is based on meaningful linguistic units.
30
-
31
- **Stopword Filtering:** Many words in a language are grammatically necessary but carry little unique semantic weight. These "stopwords" can skew similarity scores by creating an illusion of similarity based on common grammatical structures. TTM provides two levels of optional stopword filtering to address this:
32
-
33
- * The **Standard** list targets only the most frequent, low-information grammatical particles and punctuation (e.g., the instrumental particle `གིས་` (gis), the genitive particle `གི་` (gi), and the sentence-ending *shad* `།`).
34
- * The **Aggressive** list includes the standard particles but also removes a wider range of function words, such as pronouns (e.g., `འདི` (this), `དེ་` (that)), auxiliary verbs (e.g., `ཡིན་` (is)), and common quantifiers (e.g., `རྣམས་` (plural marker)).
35
-
36
- This tiered approach allows researchers to fine-tune their analysis, either preserving the grammatical structure or focusing purely on the substantive vocabulary of a text.
37
-
38
- ### 2.2. Lexical and Thematic Similarity Metrics
39
-
40
- These metrics focus on the vocabulary and key terms within the texts.
41
-
42
- **Jaccard Similarity:** This metric measures the direct overlap in vocabulary between two segments. It is calculated as the size of the intersection of the word sets divided by the size of their union. The result is a score from 0 to 1, representing the proportion of unique words that are common to both texts. Jaccard similarity is a straightforward and effective measure of shared vocabulary, independent of word order or frequency.
43
-
44
- **TF-IDF Cosine Similarity:** Term Frequency-Inverse Document Frequency (TF-IDF) is a statistical measure used to evaluate the importance of a word in a document relative to a collection of documents (a corpus). It gives higher weight to terms that are frequent in one document but rare across the corpus, thus identifying the words that are most characteristic of that document. TTM calculates a TF-IDF vector for each text segment and then uses cosine similarity to measure the angle between these vectors. A higher score indicates that two segments share more of the same characteristic terms, suggesting a thematic similarity.
45
-
46
- ### 2.3. Structural Similarity Metric
47
-
48
- **Normalized Longest Common Subsequence (LCS):** This metric moves beyond vocabulary to assess structural parallels. The LCS algorithm finds the longest sequence of words that appears in both texts in the same relative order, though not necessarily contiguously. For example, the LCS of "the brown fox jumps" and "the lazy brown dog jumps" is "the brown jumps". TTM normalizes the length of this subsequence to produce a score that reflects shared phrasing and narrative structure. A high LCS score can indicate direct textual borrowing or a shared structural template. To ensure performance, the LCS calculation is optimized with a custom Cython implementation.
49
-
50
- **LCS vs. Levenshtein Distance:** While Levenshtein distance is another string similarity metric that measures the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into another, LCS is more appropriate for Tibetan text analysis for several reasons:
51
-
52
- 1. Tibetan manuscripts often share common passages or structural elements: LCS is particularly effective at identifying these shared passages, which may be separated by varying amounts of different text.
53
-
54
- 2. LCS focuses on meaningful shared content rather than character-level differences: Unlike Levenshtein distance, which is sensitive to every character change, LCS identifies the longest sequence of words in the same order, focusing on substantive content overlap.
55
-
56
- 3. LCS is less sensitive to minor variations that might occur in handwritten or OCR-processed texts: Tibetan manuscripts often contain variations due to scribal errors, regional differences, or OCR artifacts. LCS can still identify shared structural elements despite these variations, whereas Levenshtein distance might be disproportionately affected by them.
57
-
58
- ### 2.4. Semantic Similarity
59
-
60
- To capture similarities in meaning that may not be apparent from lexical overlap, TTM employs semantic similarity using word and sentence embeddings.
61
-
62
- **FastText Embeddings:** The application utilizes the official Facebook FastText model for Tibetan, which represents words as dense vectors in a high-dimensional space. A key advantage of FastText is its use of character n-grams, allowing it to generate meaningful vectors even for out-of-vocabulary words—a crucial feature for handling the orthographic variations common in Tibetan manuscripts. To create a single vector for an entire text segment, TTM employs a sophisticated TF-IDF weighted averaging of the word vectors, giving more weight to the embeddings of characteristic terms.
63
-
64
- **Hugging Face Models:** In addition to FastText, TTM integrates the `sentence-transformers` library, providing access to a wide range of pre-trained models from the Hugging Face Hub. This allows researchers to leverage powerful, context-aware models like LaBSE or XLM-RoBERTa, which can capture nuanced semantic relationships between entire sentences and paragraphs.
65
-
66
- ## 3. The TTM Web Application: Features and Functionality
67
-
68
- The TTM application is designed to be a practical tool for researchers. Its features are built to facilitate an intuitive workflow, from data input to the interpretation of results.
69
-
70
- ### 3.1. User Interface and Workflow
71
-
72
- Built with the Gradio framework, the application's interface is clean and straightforward. The workflow is designed to be linear and intuitive:
73
-
74
- 1. **File Upload:** Users begin by uploading one or more Tibetan `.txt` files.
75
- 2. **Configuration:** Users can then configure the analysis by selecting which metrics to compute, choosing an embedding model, and setting the desired level of stopword filtering.
76
- 3. **Execution:** A single "Run Analysis" button initiates the entire processing pipeline.
77
-
78
- This simple, step-by-step process removes the barriers of command-line tools and complex software setups, making the technology accessible to all scholars.
79
-
80
- ### 3.2. Data Visualization
81
-
82
- Understanding numerical similarity scores can be challenging. TTM addresses this by providing rich, interactive visualizations:
83
-
84
- * **Heatmaps:** For each similarity metric, the application generates a heatmap that provides an at-a-glance overview of the relationships between all text segments. Darker cells indicate higher similarity, allowing researchers to quickly identify areas of strong textual connection.
85
- * **Bar Charts:** A word count chart for each text provides a simple but effective visualization of the relative lengths of the segments, which is important context for interpreting the similarity scores.
86
-
87
- These visualizations are not only useful for analysis but are also publication-ready, allowing researchers to easily incorporate them into their own work.
88
-
89
- ### 3.3. AI-Powered Interpretation
90
-
91
- A standout feature of the TTM application is its AI-powered interpretation engine. While quantitative metrics are powerful, their scholarly significance is not always self-evident. The "Interpret Results" button addresses this challenge by sending the computed metrics to a large language model (Mistral 7B via the OpenRouter API).
92
-
93
- The AI then generates a qualitative analysis of the results, framed in the language of textual scholarship. This analysis typically includes:
94
-
95
- * An overview of the general patterns of similarity.
96
- * A discussion of notable chapters with particularly high or low similarity.
97
- * An interpretation of what the different metrics collectively suggest about the texts' relationship (e.g., lexical borrowing vs. structural parallels).
98
- * Suggestions for further scholarly investigation.
99
-
100
- This feature acts as a bridge between the quantitative data and its qualitative interpretation, helping researchers to understand the implications of their findings and to formulate new research questions.
101
-
102
- ## 5. Discussion and Future Directions
103
-
104
- ### 5.1. Interpreting the Metrics: A Holistic View
105
-
106
- The true analytical power of the TTM application lies not in any single metric, but in the synthesis of all of them. For example, a high Jaccard similarity combined with a low LCS score might suggest that two texts share a common vocabulary but arrange it differently, perhaps indicating a shared topic but different authorial styles. Conversely, a high LCS score with a moderate Jaccard similarity could point to a shared structural backbone or direct borrowing, even with significant lexical variation. The addition of semantic similarity further enriches this picture, revealing conceptual connections that might be missed by lexical and structural methods alone. The TTM application facilitates this holistic approach, encouraging a nuanced interpretation of textual relationships.
107
-
108
- ### 5.2. Limitations
109
-
110
- While powerful, the TTM application has limitations. The quality of the analysis is highly dependent on the quality of the input texts; poorly scanned or OCR'd texts may yield unreliable results. The performance of the semantic models, while state-of-the-art, may also vary depending on the specific domain of the texts being analyzed. Furthermore, the AI-powered interpretation, while a useful guide, is not a substitute for scholarly expertise and should be treated as a starting point for further investigation, not a definitive conclusion.
111
-
112
- ### 5.3. Future Work
113
-
114
- The TTM project is under active development, with several potential avenues for future enhancement. These include:
115
-
116
- * **Integration of More Models:** Expanding the library of available embedding models to include more domain-specific options.
117
- * **Enhanced Visualization:** Adding more advanced visualization tools, such as network graphs to show relationships between multiple texts.
118
- * **User-Trainable Models:** Exposing the functionality to train custom FastText models directly within the web UI, allowing researchers to create highly specialized models for their specific corpora.
119
-
120
- ## 6. Conclusion
121
-
122
- The Tibetan Text Metrics web application represents a significant step forward in making computational textual analysis accessible to the field of Tibetan studies. By combining a suite of powerful similarity metrics with an intuitive user interface and a novel AI-powered interpretation engine, TTM lowers the barrier to entry for digital humanities research. It provides scholars with a versatile tool to explore textual relationships, investigate manuscript histories, and generate new, data-driven insights. As such, TTM serves not as a replacement for traditional philology, but as a powerful complement, one that promises to enrich and expand the horizons of Tibetan textual scholarship.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
base-texts/.DS_Store DELETED
Binary file (6.15 kB)
 
base-texts/Bailey.txt DELETED
The diff for this file is too large to render. See raw diff
 
base-texts/Dolanji_16.txt DELETED
The diff for this file is too large to render. See raw diff
 
base-texts/Leiden_16.txt DELETED
The diff for this file is too large to render. See raw diff
 
base-texts/Ngari 8.txt DELETED
@@ -1 +0,0 @@
1
- ༈ དང་པོ།
 
 
pipeline/advanced_alignment.py DELETED
@@ -1,329 +0,0 @@
1
- """
2
- Advanced Tibetan Legal Manuscript Alignment Engine
3
- Juxta/CollateX-inspired alignment with Tibetan-specific enhancements
4
- """
5
-
6
- import difflib
7
- import re
8
- from typing import Dict, List, Tuple
9
- from dataclasses import dataclass
10
- from collections import defaultdict
11
- import logging
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
- @dataclass
16
- class AlignmentSegment:
17
- """Represents an aligned segment between texts."""
18
- text1_content: str
19
- text2_content: str
20
- alignment_type: str # 'match', 'gap', 'mismatch', 'transposition'
21
- confidence: float
22
- position_text1: int
23
- position_text2: int
24
- context: str = ""
25
-
26
- @dataclass
27
- class TibetanAlignmentResult:
28
- """Complete alignment result for Tibetan manuscripts."""
29
- segments: List[AlignmentSegment]
30
- transpositions: List[Tuple[int, int]]
31
- insertions: List[Dict]
32
- deletions: List[Dict]
33
- modifications: List[Dict]
34
- alignment_score: float
35
- structural_similarity: float
36
- scholarly_apparatus: Dict
37
-
38
- class TibetanLegalAligner:
39
- """
40
- Juxta/CollateX-inspired alignment engine for Tibetan legal manuscripts.
41
-
42
- Features:
43
- - Multi-level alignment (character → word → sentence → paragraph)
44
- - Transposition detection (content moves)
45
- - Tibetan-specific punctuation handling
46
- - Scholarly apparatus generation
47
- - Confidence scoring
48
- """
49
-
50
- def __init__(self, min_segment_length: int = 3, context_window: int = 15):
51
- self.min_segment_length = min_segment_length
52
- self.context_window = context_window
53
- self.tibetan_punctuation = r'[།༎༏༐༑༔་]'
54
-
55
- def tibetan_tokenize(self, text: str) -> List[str]:
56
- """Tibetan-specific tokenization respecting syllable boundaries."""
57
- # Split on Tibetan punctuation and spaces
58
- tokens = re.split(rf'{self.tibetan_punctuation}|\s+', text)
59
- return [token.strip() for token in tokens if token.strip()]
60
-
61
- def segment_by_syllables(self, text: str) -> List[str]:
62
- """Segment text into Tibetan syllables."""
63
- # Tibetan syllables typically end with ་ or punctuation
64
- syllables = re.findall(r'[^་]+་?', text)
65
- return [s.strip() for s in syllables if s.strip()]
66
-
67
- def multi_level_alignment(self, text1: str, text2: str) -> TibetanAlignmentResult:
68
- """
69
- Multi-level alignment inspired by Juxta/CollateX.
70
-
71
- Levels:
72
- 1. Character level (for precise changes)
73
- 2. Syllable level (Tibetan linguistic units)
74
- 3. Sentence level (punctuation-based)
75
- 4. Paragraph level (structural blocks)
76
- """
77
-
78
- # Level 1: Character-level alignment
79
- char_alignment = self.character_level_alignment(text1, text2)
80
-
81
- # Level 2: Syllable-level alignment
82
- syllable_alignment = self.syllable_level_alignment(text1, text2)
83
-
84
- # Level 3: Sentence-level alignment
85
- sentence_alignment = self.sentence_level_alignment(text1, text2)
86
-
87
- # Level 4: Structural alignment
88
- structural_alignment = self.structural_level_alignment(text1, text2)
89
-
90
- # Combine results with confidence scoring
91
- return self.combine_alignments(
92
- char_alignment, syllable_alignment,
93
- sentence_alignment, structural_alignment
94
- )
95
-
96
- def character_level_alignment(self, text1: str, text2: str) -> Dict:
97
- """Character-level precise alignment."""
98
- matcher = difflib.SequenceMatcher(None, text1, text2)
99
-
100
- segments = []
101
- for tag, i1, i2, j1, j2 in matcher.get_opcodes():
102
- segment = AlignmentSegment(
103
- text1_content=text1[i1:i2],
104
- text2_content=text2[j1:j2],
105
- alignment_type=self.map_opcode_to_type(tag),
106
- confidence=self.calculate_confidence(text1[i1:i2], text2[j1:j2]),
107
- position_text1=i1,
108
- position_text2=j1
109
- )
110
- segments.append(segment)
111
-
112
- return {'segments': segments, 'level': 'character'}
113
-
114
- def syllable_level_alignment(self, text1: str, text2: str) -> Dict:
115
- """Tibetan syllable-level alignment."""
116
- syllables1 = self.segment_by_syllables(text1)
117
- syllables2 = self.segment_by_syllables(text2)
118
-
119
- matcher = difflib.SequenceMatcher(None, syllables1, syllables2)
120
-
121
- segments = []
122
- for tag, i1, i2, j1, j2 in matcher.get_opcodes():
123
- content1 = ' '.join(syllables1[i1:i2])
124
- content2 = ' '.join(syllables2[j1:j2])
125
-
126
- segment = AlignmentSegment(
127
- text1_content=content1,
128
- text2_content=content2,
129
- alignment_type=self.map_opcode_to_type(tag),
130
- confidence=self.calculate_confidence(content1, content2),
131
- position_text1=i1,
132
- position_text2=j1
133
- )
134
- segments.append(segment)
135
-
136
- return {'segments': segments, 'level': 'syllable'}
137
-
138
- def sentence_level_alignment(self, text1: str, text2: str) -> Dict:
139
- """Sentence-level alignment using Tibetan punctuation."""
140
- sentences1 = self.tibetan_tokenize(text1)
141
- sentences2 = self.tibetan_tokenize(text2)
142
-
143
- matcher = difflib.SequenceMatcher(None, sentences1, sentences2)
144
-
145
- segments = []
146
- for tag, i1, i2, j1, j2 in matcher.get_opcodes():
147
- content1 = ' '.join(sentences1[i1:i2])
148
- content2 = ' '.join(sentences2[j1:j2])
149
-
150
- segment = AlignmentSegment(
151
- text1_content=content1,
152
- text2_content=content2,
153
- alignment_type=self.map_opcode_to_type(tag),
154
- confidence=self.calculate_confidence(content1, content2),
155
- position_text1=i1,
156
- position_text2=j1
157
- )
158
- segments.append(segment)
159
-
160
- return {'segments': segments, 'level': 'sentence'}
161
-
162
- def structural_level_alignment(self, text1: str, text2: str) -> Dict:
163
- """Structural-level alignment for larger text blocks."""
164
- # Paragraph-level segmentation
165
- paragraphs1 = text1.split('\n\n')
166
- paragraphs2 = text2.split('\n\n')
167
-
168
- matcher = difflib.SequenceMatcher(None, paragraphs1, paragraphs2)
169
-
170
- segments = []
171
- for tag, i1, i2, j1, j2 in matcher.get_opcodes():
172
- content1 = '\n\n'.join(paragraphs1[i1:i2])
173
- content2 = '\n\n'.join(paragraphs2[j1:j2])
174
-
175
- segment = AlignmentSegment(
176
- text1_content=content1,
177
- text2_content=content2,
178
- alignment_type=self.map_opcode_to_type(tag),
179
- confidence=self.calculate_confidence(content1, content2),
180
- position_text1=i1,
181
- position_text2=j1
182
- )
183
- segments.append(segment)
184
-
185
- return {'segments': segments, 'level': 'structural'}
186
-
187
- def detect_transpositions(self, segments: List[AlignmentSegment]) -> List[Tuple[int, int]]:
188
- """Detect content transpositions (moves) between texts."""
189
- transpositions = []
190
-
191
- # Look for identical content appearing in different positions
192
- content_map = defaultdict(list)
193
- for i, segment in enumerate(segments):
194
- if segment.alignment_type == 'match':
195
- content_map[segment.text1_content].append(i)
196
-
197
- # Detect moves where same content appears at different positions
198
- for content, positions in content_map.items():
199
- if len(positions) > 1:
200
- # Potential transposition detected
201
- transpositions.extend([(positions[i], positions[j])
202
- for i in range(len(positions))
203
- for j in range(i+1, len(positions))])
204
-
205
- return transpositions
206
-
207
- def map_opcode_to_type(self, opcode: str) -> str:
208
- """Map difflib opcode to alignment type."""
209
- mapping = {
210
- 'equal': 'match',
211
- 'delete': 'deletion',
212
- 'insert': 'insertion',
213
- 'replace': 'mismatch'
214
- }
215
- return mapping.get(opcode, 'unknown')
216
-
217
- def calculate_confidence(self, content1: str, content2: str) -> float:
218
- """Calculate alignment confidence score."""
219
- if not content1 and not content2:
220
- return 1.0
221
-
222
- if not content1 or not content2:
223
- return 0.0
224
-
225
- # Use Levenshtein distance for confidence
226
- distance = self.levenshtein_distance(content1, content2)
227
- max_len = max(len(content1), len(content2))
228
-
229
- return max(0.0, 1.0 - (distance / max_len)) if max_len > 0 else 1.0
230
-
231
- def levenshtein_distance(self, s1: str, s2: str) -> int:
232
- """Calculate Levenshtein distance between two strings."""
233
- if len(s1) < len(s2):
234
- return self.levenshtein_distance(s2, s1)
235
-
236
- if len(s2) == 0:
237
- return len(s1)
238
-
239
- previous_row = list(range(len(s2) + 1))
240
- for i, c1 in enumerate(s1):
241
- current_row = [i + 1]
242
- for j, c2 in enumerate(s2):
243
- insertions = previous_row[j + 1] + 1
244
- deletions = current_row[j] + 1
245
- substitutions = previous_row[j] + (c1 != c2)
246
- current_row.append(min(insertions, deletions, substitutions))
247
- previous_row = current_row
248
-
249
- return previous_row[-1]
250
-
251
- def generate_scholarly_apparatus(self, alignment: TibetanAlignmentResult) -> Dict:
252
- """Generate scholarly apparatus for critical edition."""
253
- return {
254
- 'sigla': {
255
- 'witness_a': 'Base text',
256
- 'witness_b': 'Variant text'
257
- },
258
- 'critical_notes': self.generate_critical_notes(alignment),
259
- 'alignment_summary': {
260
- 'total_segments': len(alignment.segments),
261
- 'exact_matches': len([s for s in alignment.segments if s.alignment_type == 'match']),
262
- 'variants': len([s for s in alignment.segments if s.alignment_type in ['mismatch', 'modification']]),
263
- 'transpositions': len(alignment.transpositions),
264
- 'confidence_score': sum(s.confidence for s in alignment.segments) / len(alignment.segments) if alignment.segments else 0
265
- }
266
- }
267
-
268
- def generate_critical_notes(self, alignment: TibetanAlignmentResult) -> List[str]:
269
- """Generate critical notes in scholarly format."""
270
- notes = []
271
- for segment in alignment.segments:
272
- if segment.alignment_type in ['mismatch', 'modification']:
273
- note = f"Variant: '{segment.text1_content}' → '{segment.text2_content}'"
274
- notes.append(note)
275
- return notes
276
-
277
- def combine_alignments(self, *alignments) -> TibetanAlignmentResult:
278
- """Combine multi-level alignments into final result."""
279
- # This would implement sophisticated combination logic
280
- # For now, return the highest confidence level
281
-
282
- # Use sentence-level as primary
283
- sentence_alignment = next(a for a in alignments if a['level'] == 'sentence')
284
-
285
- return TibetanAlignmentResult(
286
- segments=sentence_alignment['segments'],
287
- transpositions=[],
288
- insertions=[],
289
- deletions=[],
290
- modifications=[],
291
- alignment_score=0.85, # Placeholder
292
- structural_similarity=0.75, # Placeholder
293
- scholarly_apparatus={
294
- 'method': 'Juxta/CollateX-inspired multi-level alignment',
295
- 'levels': ['character', 'syllable', 'sentence', 'structural']
296
- }
297
- )
298
-
299
- # Integration function for existing codebase
300
- def enhanced_structural_analysis(text1: str, text2: str,
301
- file1_name: str = "Text 1",
302
- file2_name: str = "Text 2") -> dict:
303
- """
304
- Enhanced structural analysis using Juxta/CollateX-inspired algorithms.
305
-
306
- Args:
307
- text1: First text to analyze
308
- text2: Second text to analyze
309
- file1_name: Name for first text
310
- file2_name: Name for second text
311
-
312
- Returns:
313
- Comprehensive alignment analysis
314
- """
315
- aligner = TibetanLegalAligner()
316
- result = aligner.multi_level_alignment(text1, text2)
317
-
318
- return {
319
- 'alignment_segments': [{
320
- 'type': segment.alignment_type,
321
- 'content1': segment.text1_content,
322
- 'content2': segment.text2_content,
323
- 'confidence': segment.confidence
324
- } for segment in result.segments],
325
- 'transpositions': result.transpositions,
326
- 'scholarly_apparatus': result.scholarly_apparatus,
327
- 'alignment_score': result.alignment_score,
328
- 'structural_similarity': result.structural_similarity
329
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
results.csv DELETED
@@ -1,97 +0,0 @@
1
- Text Pair,Jaccard Similarity (%),Normalized LCS,Semantic Similarity,TF-IDF Cosine Sim,Chapter
2
- Ngari 9.txt vs Nepal12.txt,100.0,1.0,,1.0,1
3
- Ngari 9.txt vs Nepal12.txt,100.0,1.0,,1.0,2
4
- Ngari 9.txt vs Nepal12.txt,100.0,1.0,,1.0,3
5
- Ngari 9.txt vs Nepal12.txt,46.42857142857143,0.6112115732368897,,0.8407944127395544,4
6
- Ngari 9.txt vs Nepal12.txt,40.42553191489361,0.5191256830601093,,0.5026984774848224,5
7
- Ngari 9.txt vs Nepal12.txt,47.28260869565217,0.6107784431137725,,0.8380742568060093,6
8
- Ngari 9.txt vs Nepal12.txt,49.29178470254957,0.5285565939771547,,0.8409605475909782,7
9
- Ngari 9.txt vs Nepal12.txt,46.07218683651805,0.6053169734151329,,0.9306016557862976,8
10
- Ngari 9.txt vs Nepal12.txt,51.7557251908397,0.7000429737859906,,0.9600630844581352,9
11
- Ngari 9.txt vs Nepal12.txt,52.760736196319016,0.710204081632653,,0.9135878707769712,10
12
- Ngari 9.txt vs Nepal12.txt,14.92842535787321,0.08302507192766133,,0.698638890914812,11
13
- Ngari 9.txt vs Nepal12.txt,0.0,0.0,,0.0,12
14
- Ngari 9.txt vs Nepal12.txt,0.0,0.0,,0.0,13
15
- Ngari 9.txt vs Nepal12.txt,0.0,0.0,,0.0,14
16
- Ngari 9.txt vs Nepal12.txt,0.0,0.0,,0.0,15
17
- Ngari 9.txt vs Nepal12.txt,100.0,1.0,,1.0,16
18
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,1
19
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,2
20
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,3
21
- Ngari 9.txt vs LTWA.txt,47.752808988764045,0.603648424543947,,0.8414077093281586,4
22
- Ngari 9.txt vs LTWA.txt,48.40764331210191,0.6094808126410836,,0.6526135410649626,5
23
- Ngari 9.txt vs LTWA.txt,49.13294797687861,0.6297872340425532,,0.8252183235183391,6
24
- Ngari 9.txt vs LTWA.txt,35.53459119496855,0.4071058475203553,,0.8403529862077375,7
25
- Ngari 9.txt vs LTWA.txt,45.0,0.601965601965602,,0.9452297806160965,8
26
- Ngari 9.txt vs LTWA.txt,37.89126853377265,0.29986320109439124,,0.8760838478443608,9
27
- Ngari 9.txt vs LTWA.txt,51.632047477744806,0.6395222584147665,,0.9317016829510952,10
28
- Ngari 9.txt vs LTWA.txt,14.979757085020243,0.10742761225346202,,0.7111189597708231,11
29
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,12
30
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,13
31
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,14
32
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,15
33
- Ngari 9.txt vs LTWA.txt,0.0,0.0,,0.0,16
34
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,1
35
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,2
36
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,3
37
- Ngari 9.txt vs Leiden.txt,41.340782122905026,0.5282331511839709,,0.8525095366316284,4
38
- Ngari 9.txt vs Leiden.txt,36.80555555555556,0.4734042553191489,,0.5634721694429372,5
39
- Ngari 9.txt vs Leiden.txt,44.047619047619044,0.4728132387706856,,0.7698959290709281,6
40
- Ngari 9.txt vs Leiden.txt,35.67251461988304,0.3208020050125313,,0.784262930792386,7
41
- Ngari 9.txt vs Leiden.txt,41.01123595505618,0.4241099312929419,,0.9275267086147868,8
42
- Ngari 9.txt vs Leiden.txt,40.31209362808843,0.20184790334044064,,0.9076572014074583,9
43
- Ngari 9.txt vs Leiden.txt,50.445103857566764,0.6045733407696597,,0.9284684903895061,10
44
- Ngari 9.txt vs Leiden.txt,16.363636363636363,0.08736942070275404,,0.6999802304139516,11
45
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,12
46
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,13
47
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,14
48
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,15
49
- Ngari 9.txt vs Leiden.txt,0.0,0.0,,0.0,16
50
- Nepal12.txt vs LTWA.txt,0.0,0.0,,0.0,1
51
- Nepal12.txt vs LTWA.txt,0.0,0.0,,0.0,2
52
- Nepal12.txt vs LTWA.txt,0.0,0.0,,0.0,3
53
- Nepal12.txt vs LTWA.txt,56.493506493506494,0.6959706959706959,,0.8321482637014176,4
54
- Nepal12.txt vs LTWA.txt,39.71631205673759,0.5386666666666666,,0.7104447145077406,5
55
- Nepal12.txt vs LTWA.txt,48.795180722891565,0.5898004434589801,,0.8168699067131293,6
56
- Nepal12.txt vs LTWA.txt,34.954407294832826,0.3365548607163161,,0.861391898750807,7
57
- Nepal12.txt vs LTWA.txt,51.41509433962265,0.5873239436619718,,0.9310750730815768,8
58
- Nepal12.txt vs LTWA.txt,41.18705035971223,0.3156208277703605,,0.9075961630628558,9
59
- Nepal12.txt vs LTWA.txt,60.066006600660074,0.7040533037201555,,0.921390350997517,10
60
- Nepal12.txt vs LTWA.txt,63.6986301369863,0.7454220634211701,,0.9803189694519824,11
61
- Nepal12.txt vs LTWA.txt,48.275862068965516,0.5102639296187683,,0.7725258306356406,12
62
- Nepal12.txt vs LTWA.txt,58.203125,0.7364921030756443,,0.9543942889292814,13
63
- Nepal12.txt vs LTWA.txt,41.732283464566926,0.4332449160035367,,0.8497746214132795,14
64
- Nepal12.txt vs LTWA.txt,17.983651226158038,0.1474820143884892,,0.5779105517118261,15
65
- Nepal12.txt vs LTWA.txt,0.0,0.0,,0.0,16
66
- Nepal12.txt vs Leiden.txt,0.0,0.0,,0.0,1
67
- Nepal12.txt vs Leiden.txt,0.0,0.0,,0.0,2
68
- Nepal12.txt vs Leiden.txt,0.0,0.0,,0.0,3
69
- Nepal12.txt vs Leiden.txt,57.14285714285714,0.6788617886178862,,0.8403894964769358,4
70
- Nepal12.txt vs Leiden.txt,38.793103448275865,0.4935064935064935,,0.4684416871587978,5
71
- Nepal12.txt vs Leiden.txt,60.416666666666664,0.6386138613861386,,0.8441982917223785,6
72
- Nepal12.txt vs Leiden.txt,43.24324324324324,0.33632734530938124,,0.8839876637274263,7
73
- Nepal12.txt vs Leiden.txt,50.8235294117647,0.4953338119167265,,0.9373191281412603,8
74
- Nepal12.txt vs Leiden.txt,44.03927068723703,0.2242042672263029,,0.9196700291527228,9
75
- Nepal12.txt vs Leiden.txt,67.59581881533101,0.7226027397260274,,0.9462708958951278,10
76
- Nepal12.txt vs Leiden.txt,60.42780748663101,0.7003094501309212,,0.9722895878422901,11
77
- Nepal12.txt vs Leiden.txt,23.502304147465438,0.27245508982035926,,0.6893488630692246,12
78
- Nepal12.txt vs Leiden.txt,67.08333333333333,0.7506382978723404,,0.9466019120384076,13
79
- Nepal12.txt vs Leiden.txt,42.67782426778243,0.418426103646833,,0.8023010077421123,14
80
- Nepal12.txt vs Leiden.txt,31.17206982543641,0.2664756446991404,,0.757778410785804,15
81
- Nepal12.txt vs Leiden.txt,0.0,0.0,,0.0,16
82
- LTWA.txt vs Leiden.txt,53.5064935064935,0.6359163591635917,,0.9623337315161734,1
83
- LTWA.txt vs Leiden.txt,60.909090909090914,0.7578659370725034,,0.8852155398192683,2
84
- LTWA.txt vs Leiden.txt,64.1025641025641,0.7001044932079414,,0.8986878289296542,3
85
- LTWA.txt vs Leiden.txt,51.21951219512195,0.6568265682656826,,0.8596233249504219,4
86
- LTWA.txt vs Leiden.txt,40.0,0.5298701298701298,,0.6287776677298036,5
87
- LTWA.txt vs Leiden.txt,46.308724832214764,0.5415549597855228,,0.7796920776498958,6
88
- LTWA.txt vs Leiden.txt,43.233082706766915,0.49545136459062283,,0.8819142330949857,7
89
- LTWA.txt vs Leiden.txt,54.03050108932462,0.5373230373230373,,0.9477445373252964,8
90
- LTWA.txt vs Leiden.txt,38.75598086124402,0.1898707353252808,,0.887072472781142,9
91
- LTWA.txt vs Leiden.txt,66.32996632996633,0.7823310271420969,,0.9693004524579277,10
92
- LTWA.txt vs Leiden.txt,63.537906137184116,0.754516983859311,,0.9830176756030125,11
93
- LTWA.txt vs Leiden.txt,24.299065420560748,0.18152350081037277,,0.6278532648577805,12
94
- LTWA.txt vs Leiden.txt,60.1593625498008,0.7367521367521368,,0.9381662329597793,13
95
- LTWA.txt vs Leiden.txt,59.44444444444444,0.6746987951807228,,0.8771500136505623,14
96
- LTWA.txt vs Leiden.txt,35.37735849056604,0.39255014326647564,,0.6834100468628878,15
97
- LTWA.txt vs Leiden.txt,60.45081967213115,0.6875444839857652,,0.9482911929631709,16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
user_guide.md DELETED
@@ -1,190 +0,0 @@
1
- # Tibetan Text Metrics Web Application User Guide
2
-
3
- ## Introduction
4
-
5
- Welcome to the Tibetan Text Metrics Web Application! This user-friendly tool allows you to analyze textual similarities and variations in Tibetan manuscripts using multiple computational approaches. The application provides a graphical interface to the core functionalities of the Tibetan Text Metrics (TTM) project.
6
-
7
- ## Getting Started
8
-
9
- ### System Requirements
10
-
11
- - Modern web browser (Chrome, Firefox, Safari, or Edge)
12
- - For local installation: Python 3.10 or newer
13
- - Sufficient RAM for processing large texts (4GB minimum, 8GB recommended)
14
-
15
- ### Installation and Setup
16
-
17
- #### Online Demo
18
-
19
- The easiest way to try the application is through our Hugging Face Spaces demo:
20
- [daniel-wojahn/ttm-webapp-hf](https://huggingface.co/spaces/daniel-wojahn/ttm-webapp-hf)
21
-
22
- Note: The free tier of Hugging Face Spaces may have performance limitations compared to running locally.
23
-
24
- #### Local Installation
25
-
26
- 1. Clone the repository:
27
- ```bash
28
- git clone https://github.com/daniel-wojahn/tibetan-text-metrics.git
29
- cd tibetan-text-metrics/webapp
30
- ```
31
-
32
- 2. Create and activate a virtual environment:
33
- ```bash
34
- python -m venv venv
35
- source venv/bin/activate # On Windows: venv\Scripts\activate
36
- ```
37
-
38
- 3. Install dependencies:
39
- ```bash
40
- pip install -r requirements.txt
41
- ```
42
-
43
- 4. Run the application:
44
- ```bash
45
- python app.py
46
- ```
47
-
48
- 5. Open your browser and navigate to:
49
- ```
50
- http://localhost:7860
51
- ```
52
-
53
- ## Using the Application
54
-
55
- ### Step 1: Upload Your Tibetan Text Files
56
-
57
- 1. Click the "Upload Tibetan .txt files" button to select one or more `.txt` files containing Tibetan text.
58
- 2. Files should be in UTF-8 or UTF-16 encoding.
59
- 3. Maximum file size: 10MB per file (for optimal performance, use files under 1MB).
60
- 4. For best results, your texts should be segmented into chapters/sections using the Tibetan marker '༈' (*sbrul shad*).
61
-
62
- ### Step 2: Configure Analysis Options
63
-
64
- 1. **Semantic Similarity**: Choose whether to compute semantic similarity metrics.
65
- - "Yes" (default): Includes semantic similarity in the analysis (slower but more comprehensive).
66
- - "No": Skips semantic similarity calculation for faster processing.
67
-
68
- 2. **Embedding Model**: Select the model to use for semantic similarity analysis.
69
- - **sentence-transformers/all-MiniLM-L6-v2** (default): General purpose sentence embedding model (fastest option).
70
- - **sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2**: Multilingual model with good performance for many languages.
71
- - **buddhist-nlp/buddhist-sentence-similarity**: Optimized for Buddhist text similarity.
72
- - **xlm-roberta-base**: Multilingual model that includes Tibetan.
73
-
74
- 3. Click the "Run Analysis" button to start processing.
75
-
76
- ### Step 3: View and Interpret Results
77
-
78
- After processing, the application displays several visualizations and metrics:
79
-
80
- #### Word Count Chart
81
-
82
- Shows the number of words in each chapter/segment of each file, allowing you to compare the relative lengths of different texts.
83
-
84
- #### Similarity Metrics
85
-
86
- The application computes four different similarity metrics between corresponding chapters of different files:
87
-
88
- 1. **Jaccard Similarity (%)**: Measures vocabulary overlap between segments after filtering out common Tibetan stopwords. A higher percentage indicates a greater overlap in the significant vocabularies used in the two segments.
89
-
90
- 2. **Normalized LCS (Longest Common Subsequence)**: Measures the length of the longest sequence of words that appears in both text segments, maintaining their original relative order. A higher score suggests more significant shared phrasing, direct textual borrowing, or strong structural parallelism.
91
-
92
- 3. **Semantic Similarity**: Uses a transformer-based model to compute the cosine similarity between the semantic embeddings of text segments. This captures similarities in meaning even when different vocabulary is used.
93
-
94
- 4. **TF-IDF Cosine Similarity**: Compares texts based on their important, characteristic terms by giving higher weight to words that are frequent within a particular segment but relatively rare across the entire collection.
95
-
96
- #### Heatmap Visualizations
97
-
98
- Each metric has a corresponding heatmap visualization where:
99
- - Rows represent chapters/segments
100
- - Columns represent text pairs being compared
101
- - Color intensity indicates similarity (brighter = more similar)
102
-
103
- ### Tips for Effective Analysis
104
-
105
- 1. **Text Segmentation**: For meaningful chapter-level comparisons, ensure your texts are segmented using the Tibetan marker '༈' (*sbrul shad*).
106
-
107
- 2. **File Naming**: Use descriptive filenames to make the comparison results easier to interpret.
108
-
109
- 3. **Model Selection**:
110
- - For faster processing, use the default model or disable semantic similarity.
111
- - For Buddhist texts, the buddhist-nlp/buddhist-sentence-similarity model may provide better results.
112
-
113
- 4. **File Size**:
114
- - Keep individual files under 1MB for optimal performance.
115
- - Very large files (>10MB) are not supported and will trigger an error.
116
-
117
- 5. **Comparing Multiple Texts**: The application requires at least two text files to compute similarity metrics.
118
-
119
- ## Understanding the Metrics
120
-
121
- ### Jaccard Similarity (%)
122
-
123
- This metric quantifies the lexical overlap between two text segments by comparing their sets of unique words, after filtering out common Tibetan stopwords. It essentially answers the question: 'Of all the distinct, meaningful words found across these two segments, what proportion of them are present in both?'
124
-
125
- It is calculated as:
126
- ```
127
- (Number of common unique meaningful words) / (Total number of unique meaningful words in both texts combined) * 100
128
- ```
129
-
130
- Jaccard Similarity is insensitive to word order and word frequency; it only cares whether a unique meaningful word is present or absent. A higher percentage indicates a greater overlap in the significant vocabularies used in the two segments.
131
-
132
- ### Normalized LCS (Longest Common Subsequence)
133
-
134
- This metric measures the length of the longest sequence of words that appears in both text segments, maintaining their original relative order. Importantly, these words do not need to be directly adjacent (contiguous) in either text.
135
-
136
- For example, if Text A is 'the quick brown fox jumps' and Text B is 'the lazy cat and brown dog jumps high', the LCS is 'the brown jumps'.
137
-
138
- The length of this common subsequence is then normalized to provide a score. A higher Normalized LCS score suggests more significant shared phrasing, direct textual borrowing, or strong structural parallelism, as it reflects similarities in how ideas are ordered and expressed sequentially.
139
-
140
- Unlike other metrics, LCS does not filter out stopwords, allowing it to capture structural similarities and the flow of language, including the use of particles and common words that contribute to sentence construction.
141
-
142
- ### Semantic Similarity
143
-
144
- This metric utilizes transformer-based models to compute the cosine similarity between the semantic embeddings of text segments. The model converts each text segment into a high-dimensional vector that captures its semantic meaning.
145
-
146
- For texts exceeding the model's token limit, an automated chunking strategy is employed: texts are divided into overlapping chunks, each chunk is embedded, and the resulting chunk embeddings are averaged to produce a single representative vector for the entire segment before comparison.
147
-
148
- A higher score indicates that the texts express similar concepts or ideas, even if they use different vocabulary or phrasing.
149
-
150
- ### TF-IDF Cosine Similarity
151
-
152
- This metric first calculates Term Frequency-Inverse Document Frequency (TF-IDF) scores for each word in each text segment, after filtering out common Tibetan stopwords. TF-IDF gives higher weight to words that are frequent within a particular segment but relatively rare across the entire collection of segments.
153
-
154
- Each segment is then represented as a vector of these TF-IDF scores, and the cosine similarity is computed between these vectors. A score closer to 1 indicates that the two segments share more of these important, distinguishing terms, suggesting they cover similar specific topics or themes.
155
-
156
- ## Troubleshooting
157
-
158
- ### Common Issues and Solutions
159
-
160
- 1. **"Empty vocabulary" error**:
161
- - This can occur if a text contains only stopwords or if tokenization fails.
162
- - Solution: Check your input text to ensure it contains valid Tibetan content.
163
-
164
- 2. **Model loading errors**:
165
- - If a model fails to load, the application will continue without semantic similarity.
166
- - Solution: Try a different model or disable semantic similarity.
167
-
168
- 3. **Performance issues with large files**:
169
- - Solution: Split large files into smaller ones or use fewer files at once.
170
-
171
- 4. **No results displayed**:
172
- - Solution: Ensure you have uploaded at least two valid text files and that they contain comparable content.
173
-
174
- 5. **Encoding issues**:
175
- - If your text appears garbled, it may have encoding problems.
176
- - Solution: Ensure your files are saved in UTF-8 or UTF-16 encoding.
177
-
178
- ### Getting Help
179
-
180
- If you encounter issues not covered in this guide, please:
181
- 1. Check the [GitHub repository](https://github.com/daniel-wojahn/tibetan-text-metrics) for updates or known issues.
182
- 2. Submit an issue on GitHub with details about your problem.
183
-
184
- ## Acknowledgments
185
-
186
- The Tibetan Text Metrics project was developed as part of the [Law in Historic Tibet](https://www.law.ox.ac.uk/law-historic-tibet) project at the Centre for Socio-Legal Studies at the University of Oxford.
187
-
188
- ## License
189
-
190
- This project is licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0).