Centering-Lgram
Discourse cohesion analysis based on Centering Theory (Grosz, Joshi, and Weinstein, 1983). Measures how entities and topics flow across sentences — pronouns, repetitions, entity continuity.
Note: Cohesion (bağdaşıklık) = surface grammatical/lexical links. Coherence (tutarlılık) = deeper semantic unity. Centering Theory models cohesion.
⚠️ What This Tool Does NOT Measure
- Factual accuracy — a high cohesion score does NOT mean the content is correct
- Hallucination — a fluent-sounding LLM output can still be completely false
- Overall quality — cohesion is ONE dimension of text quality, not the whole picture
Correct positioning: Use centering-lgram as a complementary evaluator alongside faithfulness/accuracy checkers. It measures "how smoothly does this read?", not "is this correct?".
| What we measure | What we DON'T measure |
|---|---|
| Entity flow across sentences | Factual correctness |
| Pronoun resolution quality | Hallucination / faithfulness |
| Topic continuity / shifts | Logical reasoning |
| Readability (Flesch) | Relevance to prompt |
| Lexical repetition chains | Domain accuracy |
Quick Start
pip install centering-lgram
python -m spacy download en_core_web_sm
from lgram import TextAnalyzer
ta = TextAnalyzer()
r = ta.analyze("AI helps doctors. It speeds up diagnosis. These tools save lives.")
print(r.overall_cohesion) # 0.0-1.0 (exact value depends on the spaCy model)
print(r.quality) # "high"
For best results, use the medium model:
ta = TextAnalyzer("en_core_web_md")
Genre Calibration
Empirically derived transition patterns. Method: Tukey's fence (p75 + 1.5×IQR).
Brown Corpus (1960s)
NLTK Brown: 1.1M words, 500 files, 15 categories. n=30/genre, HIGH confidence.
| Genre | Rough normal (p25–p75) | Flag > (Tukey) | Continue mean | Conf. |
|---|---|---|---|---|
| Narrative | 11.5% – 27.3% | 51.0% | 49.8% | HIGH |
| Expository | 16.7% – 33.3% | 58.2% | 29.2% | HIGH |
| Essay | 11.5% – 27.7% | 52.0% | 30.7% | HIGH |
Modern Corpus (2020s) — n=30/genre, HIGH confidence
| Genre | Rough normal (p25–p75) | Flag > (Tukey) | Continue mean | Conf. |
|---|---|---|---|---|
| Narrative | 0.0% – 25.0% | 62.5% | 25.0% | HIGH |
| Expository | 0.0% – 25.0% | 62.5% | 32.3% | HIGH |
| Essay | 0.0% – 25.0% | 62.5% | 20.3% | HIGH |
Note: Single-author corpus. Stylistic homogeneity may narrow distributions.
Cross-Validation: Wikipedia (multi-author, 2024)
n=12, MEDIUM confidence. Small sample — treat as observational, not conclusive.
| Genre | n | Rough normal | Flag > | Continue | Conf. |
|---|---|---|---|---|---|
| Expository (Wikipedia) | 12 | 23.0% – 34.6% | 52.0% | 34.5% | MEDIUM |
Wikipedia's 52% flag threshold is broadly consistent with Brown's 58%. Direction is as expected (multi-author corpora cluster together, single-author is the outlier). However, at n=12 this is an observation, not a validated claim. Two caveats: (1) Wikipedia's strict editing guidelines may make it a distinct sub-genre, not representative of general 2020s expository writing. (2) The 6% gap could be measurement noise, temporal change, or genre artifact — the current data cannot distinguish between these explanations.
Findings
- Rough-Shift >50% is abnormal — all corpora agree. Flag thresholds: 51-63%.
- Wikipedia cross-validates the multi-author finding — Brown (58%) and Wikipedia (52%) cluster together, while the single-author corpus (63%) diverges. Consistent with the stylistic homogeneity hypothesis. However, n=12 precludes strong conclusions about temporal stability.
- Wikipedia is not "general 2020s writing" — its strict editing guidelines may constitute a distinct sub-genre. More diverse modern sources needed.
Calibration is reproducible: python -m lgram.brown_calibration
⚠️ Reliability note: Scores depend on the underlying embedding model. For production use, pick ONE model (recommended:
en_core_web_md) and standardize on it. Compare texts only within the same genre — cross-genre comparison is meaningless because different genres have different natural transition patterns.
Core Concepts
Centering Theory tracks three discourse centers per utterance:
| Center | Notation | Definition |
|---|---|---|
| Forward Centers | Cf | Entities ordered by grammatical salience |
| Backward Center | Cb | Entity linking to previous utterance |
| Preferred Center | Cp | Highest-ranked Cf |
Five transition types between utterances:
| Transition | Rule | Quality |
|---|---|---|
| Establish | First utterance | — |
| Continue | Cb(Ui) = Cb(Ui-1) = Cp(Ui) | Best |
| Retain | Cb(Ui) = Cb(Ui-1) ≠ Cp(Ui) | Good |
| Smooth-Shift | Cb(Ui) ≠ Cb(Ui-1) = Cp(Ui) | OK |
| Rough-Shift | Cb(Ui) ≠ Cb(Ui-1) ≠ Cp(Ui) | Poor |
API — TextAnalyzer (High-Level)
from lgram import TextAnalyzer
ta = TextAnalyzer() # default: en_core_web_sm
ta = TextAnalyzer("en_core_web_md") # better vectors
ta = TextAnalyzer(use_sentence_transformers=True) # best quality
Core Analysis
| Method | Description |
|---|---|
analyze(text) |
Full analysis → TextReport (sentences, paragraphs, transitions, entities) |
analyze_batch(texts, labels) |
Compare multiple texts with rankings |
analyze_llm(response, prompt?) |
LLM output quality: high/medium/low + prompt comparison |
Cohesion Metrics
| Method | Source | Description |
|---|---|---|
entity_grid_score(text) |
Barzilay & Lapata 2005 | Entity role persistence (S/O/X/-) across sentences |
lexical_chain_score(text) |
Halliday & Hasan 1976 | Noun repetition + similarity chains |
build_cohesion_graph(text) |
Graph-based | Sentence adjacency graph (density, centrality, communities) |
cohesion_trend(text) |
Sliding window | Cohesion change across text (improving/declining/stable) |
cohesion_heatmap(text) |
Matrix | N×N sentence similarity with weak pair detection |
combined_score(text) |
Hybrid | Cohesion × 0.6 + Readability × 0.4 |
Segmentation & Quality
| Method | Description |
|---|---|
texttile_segments(text) |
Hearst 1994 topic segmentation |
hybrid_boundaries(text) |
Centering + TextTiling intersection (high confidence) |
suggest_improvements(text) |
Find weak points + fix suggestions |
annotate_weak_points(text) |
Mark <<<WEAK>>> at cohesion breaks |
diff_cohesion(original, revised) |
Compare two versions |
readability_score(text) |
Flesch Reading Ease + statistics |
Export
| Method | Output |
|---|---|
to_dict(report) |
JSON-serializable dict |
to_json(report) |
JSON string |
to_summary(report) |
Human-readable report |
API — EnhancedCenteringTheory (Low-Level)
from lgram import EnhancedCenteringTheory
import spacy
nlp = spacy.load("en_core_web_sm")
ct = EnhancedCenteringTheory(nlp)
state = ct.analyze_utterance("John went to the store.")
ct.update_discourse("He bought milk.")
result = ct.evaluate_cohesion(["John went.", "He bought milk.", "The store was busy."])
Key methods: compute_forward_centers, compute_backward_center, determine_transition, extract_clauses, detect_boundaries, validate_sequence, visualize, compare_texts, stream_start/feed/flush, save/load, reset.
CLI
centering-lgram analyze --text "John went to the store. He bought milk."
centering-lgram clauses --text "She left because she was tired."
centering-lgram full --text "John left. He was tired because he worked late."
centering-lgram score --text "Alice met Bob. She greeted him."
centering-lgram info
centering-lgram version
How It Works
Salience Ranking
Cf ordered by: grammatical role (S=4 > O=3 > other=2 > poss=1) + POS (PRON=3 > PROPN=2 > NOUN=1) + position + entity type (PERSON/ORG/GPE bonus) + pronoun antecedent bonus.
Backward Center (5-level cascade)
- Possessive scan — "his"/"her" → matched person entity
- Direct match — entity appears in both Cf lists
- Pronoun resolution — gender-aware (he→male, she→female)
- Coreference — entity type matching + vector similarity fallback
- Compound plural — multiple persons → "they"
Gender-Aware Pronoun Resolution
120+ name gender map (English + Turkish) + title detection (Mr/Mrs) + suffix heuristics. Male pronoun "he" does NOT match female entity "Alice".
Clause Detection
Dependency parse: main, conj, advcl, ccomp, acl, relcl. Separator tokens (commas, conjunctions) assigned to following clause.
Architecture
lgram/
__init__.py # Package exports
analyzer.py 924 # TextAnalyzer (17 methods)
benchmark.py 290 # CohesionBenchmark (4 tests)
cli.py 238 # 6 CLI commands
core.py 7 # Re-export hub
utils.py 20 # Logging
models/
__init__.py 7 # Sub-package exports
centering_theory.py 1122 # Core engine
tests/
test_lgram.py 209 # 15 core tests
test_edges.py 312 # 34 edge case tests
docs/
RESEARCH.md # Literature survey
IMPLEMENTATION_PLAN.md # Implementation plan
Dependencies: spacy>=3.4.0 only. Optional: sentence-transformers for MiniLM.
Use Cases
| Domain | Application |
|---|---|
| LLM Evaluation | Cohesion scoring for GPT/Claude/Llama output |
| Education | Essay scoring, writing assistant feedback — CAEAS EFL essay feedback tool |
| Linguistics | Discourse analysis research |
| Content Quality | Blog/news fluency audits |
| Translation | Cross-language cohesion comparison |
| Dialogue | Conversation flow naturalness |
| Forensics | Statement consistency analysis |
License
MIT — see LICENSE. Copyright (c) 2025 Ilker Atagun.
Metadata
Release files for centering-lgram 2.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| centering_lgram-2.3.1.tar.gz | 134.3 kB | Without provenance | Details for centering_lgram-2.3.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| centering_lgram-2.3.1-py3-none-any.whl | Python 3 | none | any | Details for centering_lgram-2.3.1-py3-none-any.whl |
Total release size: 265.7 kB
Release files / centering_lgram-2.3.1.tar.gz
| Download URL | centering_lgram-2.3.1.tar.gz |
|---|---|
| Size | 134.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4220196311abaf71f30ef7ef2d3d34c0ae986489788a756b162082bdb40e95f5
|
|
BLAKE2b-256 checksum How to use checksums |
23a30917aee336a28a3ec455ca72b9aea6be769f9c157faa31e97d6d2df9b184
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / centering_lgram-2.3.1-py3-none-any.whl
| Download URL | centering_lgram-2.3.1-py3-none-any.whl |
|---|---|
| Size | 131.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9b24a765ce5236ff8c72905b6947c5653b561730451429832aa274878b30d8e8
|
|
BLAKE2b-256 checksum How to use checksums |
09b8873d986778ca8b7de8dc4d21f0b80ee2ded1ecd058fe60fc1a78b760fb57
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for centering-lgram 2.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.