Metadata-Version: 2.4
Name: asymgram
Version: 0.2.5
Summary: Utilities for CoNLL-U dependency tree objects, manipulation, evaluation, and error checking.
Author-email: Tsy Yih <yihtsy@outlook.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Yihtsy/asymgram
Project-URL: Repository, https://github.com/Yihtsy/asymgram
Project-URL: Issues, https://github.com/Yihtsy/asymgram/issues
Keywords: CoNLL-U,Universal Dependencies,dependency grammar,dependency parsing,corpus linguistics,treebank
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: conllu>=6.0
Requires-Dist: numpy>=1.23
Provides-Extra: nlp
Requires-Dist: spacy>=3.0; extra == "nlp"
Requires-Dist: spacy-stanza>=1.0; extra == "nlp"
Requires-Dist: stanza>=1.0; extra == "nlp"
Dynamic: license-file

<p align="center">
  <img src="assets/asymgram-logo.png" alt="Asymgram logo" width="720">
</p>

<p align="center">
  <a href="https://pypi.org/project/asymgram/"><img alt="Release" src="https://img.shields.io/pypi/v/asymgram.svg?label=release"></a>
  <a href="https://pypi.org/project/asymgram/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/asymgram.svg"></a>
  <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green.svg"></a>
  <a href="https://pypi.org/project/asymgram/"><img alt="PyPI downloads" src="https://img.shields.io/pypi/dm/asymgram.svg?label=downloads"></a>
  <a href="https://github.com/Yihtsy/asymgram/issues"><img alt="GitHub issues" src="https://img.shields.io/github/issues/Yihtsy/asymgram.svg?label=issues"></a>
</p>

# Asymgram

**Asymgram** stands for **Asymmetrency Grammar**, a syntactic framework under development that is closely related to dependency grammar. The framework is designed to support syntactic annotation, manipulation, and structural analysis of dependency-based linguistic data.

The Python package `asymgram` is a small toolkit for working with CoNLL-U dependency treebanks. It wraps parsed CoNLL-U tokens and sentences with convenient object references, provides tree manipulation helpers, evaluates dependency parses, and includes diagnostics for common annotation and format errors.

The package is designed for corpus linguistics, dependency grammar research, Universal Dependencies style treebank work, and quick inspection of parser output.

## Features

- Enhanced CoNLL-U objects: `Token`, `TokenList`, `SentenceList`, and `SentenceLists`.
- Context-aware token navigation, including head lookup, dependent lookup, sibling lookup, subtree span extraction, and node depth.
- Structural unit and tree-query helpers for strings, catenae, components, constituents, phrases, connectedness, projectivity, crossing dependencies, heads, children, siblings, dependent subtrees, and clause-type extraction.
- Query syntax and subcorpus extraction helpers for finding syntactic patterns and writing filtered CoNLL-U subsets.
- Sentence and token manipulation helpers for merging tokens, splitting tokens, inserting tokens, removing non-word lines, removing leaf nodes, merging sentences, and splitting sentences at marginal subtrees.
- Python rule-based scheme conversion helpers for UD-style function-dependent structures and SUD-style function-head structures.
- Node- and corpus-level metrics, including dependency distance, directional dependency distance, hierarchy level, tree depth, sentence length, frequency tables, UAS, and LAS.
- CoNLL-U readers, writers, text reconstruction, CHAT-to-CoNLL-U conversion, XML corpus conversion, and compact CoNLL-U subset formats such as ConLLUB and ConLLUC.
- Language-specific postprocessing profiles for refining parser and treebank output.
- Error checking for common CoNLL-U and dependency treebank issues, including invalid IDs, malformed HEAD values, self-loops, cycles, missing roots, multiple roots, invalid UPOS/DEPREL labels, malformed FEATS/DEPS, punctuation mismatches, and suspicious UPOS-DEPREL combinations.

## Installation

```bash
pip install asymgram
```

For local development:

```bash
git clone https://github.com/Yihtsy/asymgram.git
cd asymgram
pip install -e .
```

Optional NLP-related dependencies can be installed with:

```bash
pip install "asymgram[nlp]"
```

## Quick Start

```python
import asymgram as ag

text = """
# sent_id = demo-1
# text = I saw her.
1	I	_	PRON	_	_	2	nsubj	2:nsubj	_
2	saw	_	VERB	_	_	0	root	0:root	_
3	her	_	PRON	_	_	2	obj	2:obj	SpaceAfter=No
4	.	_	PUNCT	_	_	2	punct	2:punct	_
"""

sentences = ag.parse(text)
sentence = sentences[0]
root = sentence.get_roottoken()

print(root.form)
print([tok.form for tok in root.get_deptokens(include_punct=True)])
print(sentences.to_tokenized_text())
```


## Structural Queries

`asymgram` includes helpers for dependency-grammar structural units and node relations.

```python
import asymgram as ag

sent = sentences[0]

# Generic structural units
ag.is_string(sent, [1, 2])
ag.is_catena(sent, [3, 6])
ag.is_component(sent, [1, 2])
ag.is_constituent(sent, [4, 5, 6])
ag.is_phrase(sent, [4, 5, 6])

# Tree-level checks
ag.is_connected_tree(sent)
ag.is_projective_tree(sent)
ag.has_crossing_dependencies(sent)

# Node relations and metrics
node = ag.resolve_token(sent, "saw")
head = ag.get_head(sent, node)
children = ag.get_children(sent, node)
siblings = ag.get_siblings(sent, node)
metrics = ag.node_metrics(sent, node)

# Phrase and clause extraction
vp_text = ag.extract_verb_phrase(sent, node, as_text=True)
clause_type = ag.clause_type_for_node(sent, node)
main_clause = ag.extract_main_clause(sent, node, as_text=True)
```

## Syntax Queries And Subcorpora

Use a compact query syntax to find matching tokens or extract matching sentences.

```python
import asymgram as ag

sentences = ag.read_conllu("treebank.conllu")

roots = ag.find_syntax_matches(sentences, "upos=VERB deprel=root")
relative_clause_sentences = ag.extract_subcorpus(
    sentences,
    "clause_type=relative_clause",
)

ag.write_subcorpus(
    sentences,
    "relative-clauses.conllu",
    query="clause_type=relative_clause",
)
```

## Scheme Conversion

As of `0.2.3`, `ud_to_sud()` and `sud_to_ud()` interpret the complete pinned universal conversion logic using the embedded Python GRS runtime and the bundled official `.grs` rules, including imported rules and lexicon files. No Grewpy, OCaml runtime, external executable, or model is required. The reference revision is `7cd14fd1a5e106c18f2ddc9a597e02fe86fddb78` from [SUD conversions](https://github.com/surfacesyntacticud/conversions).

The APIs accept `TokenList`, `SentenceList`, `SentenceLists`, a file path, or a directory path. Memory inputs return copies unless `in_place=True`. File outputs default to `name.sud.conllu` or `name.ud.conllu`; directory outputs default to sibling `name_sud` or `name_ud` directories. Use `output_path` or `output_dir` to override these, `recursive=True` for subdirectories, and `overwrite=True` to replace existing files. Files preserve source comments and replace output atomically on success.

These conversions are not lossless recovery of the original UD. Enhanced DEPS and empty nodes are removed, MWT rows are preserved, and reverse rules can choose a different analysis. Explicit `target_deprels=("case",)` selects the older lightweight, experimental local-alternation mode instead of the complete conversion; it is not the official SUD scheme and has no general round-trip guarantee.

```python
import asymgram as ag

sentences = ag.read_conllu("treebank.conllu")

# Apply the full universal UD-to-SUD conversion.
sud_like = ag.ud_to_sud(sentences)

# Convert a file; the default output is treebank.sud.conllu.
ag.ud_to_sud("treebank.conllu")

# Apply the full universal SUD-to-UD conversion.
ud_style = ag.sud_to_ud(sud_like)

# Convert a directory, preserving relative paths and filenames.
ag.ud_to_sud("ud_corpus", output_dir="sud_corpus", recursive=True)
```

## Error Checking

`asymgram.error_checker` can inspect raw CoNLL-U text, files, parsed sentences, or sentence lists.

```python
from asymgram.error_checker import check_conllu_text, check_file, generate_error_report, generate_summary_report

diagnostics = check_conllu_text(text)

for diagnostic in diagnostics:
    print(diagnostic.level, diagnostic.code, diagnostic.sent_id, diagnostic.token_id, diagnostic.message)

file_diagnostics = check_file("treebank.conllu")
error_report = generate_error_report("treebank.conllu", "treebank-error-report.md")
summary_report = generate_summary_report("treebank.conllu", "treebank-summary.md")
```

Each diagnostic is represented by a `Diagnostic` object:

```python
Diagnostic(
    code="SELF_LOOP",
    level="ERROR",
    message="token cannot take itself as HEAD",
    sent_id="demo-1",
    token_id=3,
)
```

Diagnostic levels are:

- `ERROR`: structural or format problems that are usually automatically decidable.
- `WARNING`: strong annotation warnings that should be manually checked.

`generate_error_report()` focuses on diagnostics and review candidates. `generate_summary_report()` is for descriptive corpus statistics such as UPOS, DEPREL, root-UPOS, and UPOS-DEPREL frequency tables.

## Common APIs

```python
import asymgram as ag

sentences = ag.read_conllu("sample.conllu")
sentences.to_conllu("copy.conllu")

sentence = ag.get_sentence_by_id(sentences, "demo-1")
token = ag.get_token_by_position(sentence, 2)

root = sentences[0].get_roottoken()
children = root.get_deptokens()
span = root.get_subtree_span()

uas = ag.uas(gold_sentences, predicted_sentences)
las = ag.las(gold_sentences, predicted_sentences)
```

## Conversion And Postprocessing

```python
import asymgram as ag

sentences = ag.corpus_xml_file_to_sentencelist("corpus.xml", profile="generic")
sentences = ag.add_stanza_syntax(sentences, lang="zh")

profiles = ag.available_postprocess_profiles()
refined = ag.apply_postprocess_profile(sentences, profile="zh_stanza_default")
```

## Notes

`asymgram` follows the CoNLL-U conventions used by Universal Dependencies, but it is not limited to official UD treebanks. Some diagnostics intentionally return warnings rather than hard errors because many unusual UPOS-DEPREL combinations can be valid in ellipsis, multiword expressions, headless constructions, dates, measurements, names, and language-specific analyses.

## Citation

If you use `asymgram` in academic work, cite the software version you used:

```bibtex
@software{yih_asymgram_2026,
  author = {Yih, Tsy},
  title = {Asymgram},
  year = {2026},
  version = {0.2.5},
  url = {https://github.com/Yihtsy/Asymgram},
  note = {Python package}
}
```

## Acknowledgements And Logo Design

The name "AG" is also chosen in honor of the author's favorite esports club: Chengdu AG (All Gamers).

The Asymgram logo reflects the framework's core idea: the relation between two words can be reversed, so each word may govern the other under a different analytical choice, forming a cycle. The upper arrow draws visual inspiration from the KPL Silver Dragon, while the lower arrow echoes Han Xin's spear or Mingdao Siming. On the left, the letter A resembles half of Gongsun Li's tilted umbrella; on the right, the letter G hides Guan Yu's horseshoe.

“AG”这一名称也向作者最喜爱的电竞俱乐部成都AG超玩会（All Gamers）致意。

Asymgram 的 Logo 体现了这一框架的核心思想：两个词之间的关系可以在不同分析选择下互为逆向，因此二者都可能成为支配方，并形成一个循环结构。上方箭头的视觉意象参考了 KPL 银龙杯，下方箭头则呼应韩信的长枪或名刀司命。左侧的字母 A 像半把倾倒的公孙离之伞，右侧的字母 G 中则藏有类似关羽马蹄的形态。

## License

`asymgram` is released under the MIT License.
