glitchlings 0.4.4__cp313-cp313-manylinux_2_28_x86_64.whl → 0.4.5__cp313-cp313-manylinux_2_28_x86_64.whl
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.
Potentially problematic release.
This version of glitchlings might be problematic. Click here for more details.
- glitchlings/__init__.py +4 -0
- glitchlings/_zoo_rust.cpython-313-x86_64-linux-gnu.so +0 -0
- glitchlings/compat.py +2 -4
- glitchlings/config.py +2 -4
- glitchlings/data/__init__.py +1 -0
- glitchlings/data/hokey_assets.json +193 -0
- glitchlings/dlc/_shared.py +6 -6
- glitchlings/dlc/huggingface.py +6 -6
- glitchlings/dlc/prime.py +1 -1
- glitchlings/dlc/pytorch.py +3 -3
- glitchlings/dlc/pytorch_lightning.py +4 -10
- glitchlings/lexicon/_cache.py +3 -5
- glitchlings/lexicon/vector.py +6 -5
- glitchlings/lexicon/wordnet.py +4 -8
- glitchlings/util/hokey_generator.py +144 -0
- glitchlings/util/stretch_locator.py +140 -0
- glitchlings/util/stretchability.py +375 -0
- glitchlings/zoo/__init__.py +5 -1
- glitchlings/zoo/hokey.py +173 -0
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/METADATA +26 -5
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/RECORD +25 -19
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/WHEEL +0 -0
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/entry_points.txt +0 -0
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/licenses/LICENSE +0 -0
- {glitchlings-0.4.4.dist-info → glitchlings-0.4.5.dist-info}/top_level.txt +0 -0
glitchlings/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ from .zoo import (
|
|
|
5
5
|
Apostrofae,
|
|
6
6
|
Gaggle,
|
|
7
7
|
Glitchling,
|
|
8
|
+
Hokey,
|
|
8
9
|
Jargoyle,
|
|
9
10
|
Mim1c,
|
|
10
11
|
Redactyl,
|
|
@@ -15,6 +16,7 @@ from .zoo import (
|
|
|
15
16
|
Zeedub,
|
|
16
17
|
adjax,
|
|
17
18
|
apostrofae,
|
|
19
|
+
hokey,
|
|
18
20
|
is_rust_pipeline_enabled,
|
|
19
21
|
is_rust_pipeline_supported,
|
|
20
22
|
jargoyle,
|
|
@@ -42,6 +44,8 @@ __all__ = [
|
|
|
42
44
|
"adjax",
|
|
43
45
|
"Apostrofae",
|
|
44
46
|
"apostrofae",
|
|
47
|
+
"Hokey",
|
|
48
|
+
"hokey",
|
|
45
49
|
"Redactyl",
|
|
46
50
|
"redactyl",
|
|
47
51
|
"Reduple",
|
|
Binary file
|
glitchlings/compat.py
CHANGED
|
@@ -17,16 +17,14 @@ _MISSING = _MissingSentinel()
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class _MarkerProtocol(Protocol):
|
|
20
|
-
def evaluate(self, environment: dict[str, str]) -> bool:
|
|
21
|
-
...
|
|
20
|
+
def evaluate(self, environment: dict[str, str]) -> bool: ...
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class _RequirementProtocol(Protocol):
|
|
25
24
|
marker: _MarkerProtocol | None
|
|
26
25
|
name: str
|
|
27
26
|
|
|
28
|
-
def __init__(self, requirement: str) -> None:
|
|
29
|
-
...
|
|
27
|
+
def __init__(self, requirement: str) -> None: ...
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
try: # pragma: no cover - packaging is bundled with modern Python environments
|
glitchlings/config.py
CHANGED
|
@@ -19,8 +19,7 @@ except ModuleNotFoundError: # pragma: no cover - Python < 3.11
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class _TomllibModule(Protocol):
|
|
22
|
-
def load(self, fp: IO[bytes]) -> Any:
|
|
23
|
-
...
|
|
22
|
+
def load(self, fp: IO[bytes]) -> Any: ...
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
tomllib = cast(_TomllibModule, _tomllib)
|
|
@@ -29,8 +28,7 @@ tomllib = cast(_TomllibModule, _tomllib)
|
|
|
29
28
|
class _YamlModule(Protocol):
|
|
30
29
|
YAMLError: type[Exception]
|
|
31
30
|
|
|
32
|
-
def safe_load(self, stream: str) -> Any:
|
|
33
|
-
...
|
|
31
|
+
def safe_load(self, stream: str) -> Any: ...
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
yaml = cast(_YamlModule, importlib.import_module("yaml"))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Static data assets shared across Glitchlings implementations."""
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lexical_prior": {
|
|
3
|
+
"so": 0.92,
|
|
4
|
+
"no": 0.89,
|
|
5
|
+
"go": 0.72,
|
|
6
|
+
"yeah": 0.86,
|
|
7
|
+
"yay": 0.81,
|
|
8
|
+
"ya": 0.7,
|
|
9
|
+
"hey": 0.66,
|
|
10
|
+
"okay": 0.68,
|
|
11
|
+
"ok": 0.64,
|
|
12
|
+
"cool": 0.78,
|
|
13
|
+
"omg": 0.74,
|
|
14
|
+
"wow": 0.88,
|
|
15
|
+
"wee": 0.62,
|
|
16
|
+
"woo": 0.69,
|
|
17
|
+
"woohoo": 0.74,
|
|
18
|
+
"whoa": 0.71,
|
|
19
|
+
"woah": 0.7,
|
|
20
|
+
"yayyy": 0.75,
|
|
21
|
+
"yayyyy": 0.76,
|
|
22
|
+
"yas": 0.79,
|
|
23
|
+
"yass": 0.8,
|
|
24
|
+
"yaaas": 0.82,
|
|
25
|
+
"please": 0.53,
|
|
26
|
+
"pleaseee": 0.57,
|
|
27
|
+
"pleaseeee": 0.6,
|
|
28
|
+
"pleaseeeee": 0.63,
|
|
29
|
+
"lol": 0.83,
|
|
30
|
+
"lmao": 0.65,
|
|
31
|
+
"omggg": 0.75,
|
|
32
|
+
"omgggg": 0.76,
|
|
33
|
+
"squee": 0.64,
|
|
34
|
+
"hahaha": 0.6,
|
|
35
|
+
"haha": 0.56,
|
|
36
|
+
"really": 0.58,
|
|
37
|
+
"very": 0.49,
|
|
38
|
+
"love": 0.55,
|
|
39
|
+
"cute": 0.52,
|
|
40
|
+
"nice": 0.47,
|
|
41
|
+
"sweet": 0.45,
|
|
42
|
+
"yayness": 0.44,
|
|
43
|
+
"ugh": 0.5,
|
|
44
|
+
"aww": 0.61,
|
|
45
|
+
"yess": 0.81,
|
|
46
|
+
"yes": 0.9,
|
|
47
|
+
"pls": 0.48,
|
|
48
|
+
"pleeeease": 0.62,
|
|
49
|
+
"nooo": 0.88,
|
|
50
|
+
"noooo": 0.89,
|
|
51
|
+
"dang": 0.41,
|
|
52
|
+
"geez": 0.39,
|
|
53
|
+
"danggg": 0.44,
|
|
54
|
+
"dangit": 0.38,
|
|
55
|
+
"sick": 0.35,
|
|
56
|
+
"epic": 0.37,
|
|
57
|
+
"rad": 0.5,
|
|
58
|
+
"goal": 0.56,
|
|
59
|
+
"great": 0.46,
|
|
60
|
+
"awesome": 0.51,
|
|
61
|
+
"amazing": 0.52,
|
|
62
|
+
"perfect": 0.49,
|
|
63
|
+
"fantastic": 0.5,
|
|
64
|
+
"stellar": 0.48,
|
|
65
|
+
"yippee": 0.67,
|
|
66
|
+
"stoked": 0.48,
|
|
67
|
+
"yikes": 0.43,
|
|
68
|
+
"gosh": 0.41,
|
|
69
|
+
"heck": 0.36
|
|
70
|
+
},
|
|
71
|
+
"interjections": [
|
|
72
|
+
"wow",
|
|
73
|
+
"omg",
|
|
74
|
+
"hey",
|
|
75
|
+
"ugh",
|
|
76
|
+
"yay",
|
|
77
|
+
"yayyy",
|
|
78
|
+
"yayyyy",
|
|
79
|
+
"woo",
|
|
80
|
+
"woohoo",
|
|
81
|
+
"whoa",
|
|
82
|
+
"woah",
|
|
83
|
+
"whooo",
|
|
84
|
+
"ah",
|
|
85
|
+
"aw",
|
|
86
|
+
"aww",
|
|
87
|
+
"hmm",
|
|
88
|
+
"huh",
|
|
89
|
+
"yo",
|
|
90
|
+
"yikes",
|
|
91
|
+
"gah",
|
|
92
|
+
"phew",
|
|
93
|
+
"sheesh"
|
|
94
|
+
],
|
|
95
|
+
"intensifiers": [
|
|
96
|
+
"so",
|
|
97
|
+
"very",
|
|
98
|
+
"really",
|
|
99
|
+
"super",
|
|
100
|
+
"mega",
|
|
101
|
+
"ultra",
|
|
102
|
+
"too",
|
|
103
|
+
"way",
|
|
104
|
+
"crazy",
|
|
105
|
+
"insanely",
|
|
106
|
+
"totally",
|
|
107
|
+
"extremely",
|
|
108
|
+
"seriously",
|
|
109
|
+
"absolutely",
|
|
110
|
+
"completely",
|
|
111
|
+
"entirely",
|
|
112
|
+
"utterly",
|
|
113
|
+
"hella",
|
|
114
|
+
"wicked",
|
|
115
|
+
"truly"
|
|
116
|
+
],
|
|
117
|
+
"evaluatives": [
|
|
118
|
+
"cool",
|
|
119
|
+
"great",
|
|
120
|
+
"awesome",
|
|
121
|
+
"amazing",
|
|
122
|
+
"perfect",
|
|
123
|
+
"nice",
|
|
124
|
+
"sweet",
|
|
125
|
+
"lovely",
|
|
126
|
+
"loving",
|
|
127
|
+
"silly",
|
|
128
|
+
"wild",
|
|
129
|
+
"fun",
|
|
130
|
+
"funny",
|
|
131
|
+
"adorable",
|
|
132
|
+
"cute",
|
|
133
|
+
"fantastic",
|
|
134
|
+
"fabulous",
|
|
135
|
+
"brilliant",
|
|
136
|
+
"stellar",
|
|
137
|
+
"rad",
|
|
138
|
+
"epic",
|
|
139
|
+
"delightful",
|
|
140
|
+
"gorgeous"
|
|
141
|
+
],
|
|
142
|
+
"positive_lexicon": [
|
|
143
|
+
"love",
|
|
144
|
+
"loved",
|
|
145
|
+
"loving",
|
|
146
|
+
"like",
|
|
147
|
+
"liked",
|
|
148
|
+
"awesome",
|
|
149
|
+
"amazing",
|
|
150
|
+
"yay",
|
|
151
|
+
"great",
|
|
152
|
+
"good",
|
|
153
|
+
"fun",
|
|
154
|
+
"funny",
|
|
155
|
+
"blessed",
|
|
156
|
+
"excited",
|
|
157
|
+
"cool",
|
|
158
|
+
"best",
|
|
159
|
+
"beautiful",
|
|
160
|
+
"happy",
|
|
161
|
+
"happiest",
|
|
162
|
+
"joy",
|
|
163
|
+
"joyful",
|
|
164
|
+
"thrilled",
|
|
165
|
+
"ecstatic",
|
|
166
|
+
"stoked",
|
|
167
|
+
"pumped",
|
|
168
|
+
"glad"
|
|
169
|
+
],
|
|
170
|
+
"negative_lexicon": [
|
|
171
|
+
"bad",
|
|
172
|
+
"sad",
|
|
173
|
+
"angry",
|
|
174
|
+
"annoyed",
|
|
175
|
+
"mad",
|
|
176
|
+
"terrible",
|
|
177
|
+
"awful",
|
|
178
|
+
"hate",
|
|
179
|
+
"hated",
|
|
180
|
+
"crying",
|
|
181
|
+
"hurt",
|
|
182
|
+
"tired",
|
|
183
|
+
"worst",
|
|
184
|
+
"ugh",
|
|
185
|
+
"nope",
|
|
186
|
+
"upset",
|
|
187
|
+
"frustrated",
|
|
188
|
+
"drained",
|
|
189
|
+
"exhausted",
|
|
190
|
+
"bummed",
|
|
191
|
+
"grumpy"
|
|
192
|
+
]
|
|
193
|
+
}
|
glitchlings/dlc/_shared.py
CHANGED
|
@@ -67,10 +67,10 @@ def resolve_columns(dataset: Any, columns: Sequence[str] | None) -> list[str]:
|
|
|
67
67
|
raise ValueError("Unable to determine which dataset columns to corrupt.")
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def
|
|
70
|
+
def normalize_column_spec(
|
|
71
71
|
columns: str | int | Sequence[str | int] | None,
|
|
72
72
|
) -> list[str | int] | None:
|
|
73
|
-
"""
|
|
73
|
+
"""Normalize a column specification into a list of keys or indices.
|
|
74
74
|
|
|
75
75
|
Args:
|
|
76
76
|
columns: Column specification as a single value, sequence of values, or None.
|
|
@@ -87,10 +87,10 @@ def normalise_column_spec(
|
|
|
87
87
|
if isinstance(columns, (str, int)):
|
|
88
88
|
return [columns]
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
if not
|
|
90
|
+
normalized = list(columns)
|
|
91
|
+
if not normalized:
|
|
92
92
|
raise ValueError("At least one column must be specified")
|
|
93
|
-
return
|
|
93
|
+
return normalized
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
def is_textual_candidate(value: Any) -> bool:
|
|
@@ -147,7 +147,7 @@ def corrupt_text_value(value: Any, gaggle: Gaggle) -> Any:
|
|
|
147
147
|
__all__ = [
|
|
148
148
|
"corrupt_text_value",
|
|
149
149
|
"is_textual_candidate",
|
|
150
|
-
"
|
|
150
|
+
"normalize_column_spec",
|
|
151
151
|
"resolve_columns",
|
|
152
152
|
"resolve_environment",
|
|
153
153
|
]
|
glitchlings/dlc/huggingface.py
CHANGED
|
@@ -10,15 +10,15 @@ from ..util.adapters import coerce_gaggle
|
|
|
10
10
|
from ..zoo import Gaggle, Glitchling
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def
|
|
14
|
-
"""
|
|
13
|
+
def _normalize_columns(column: str | Sequence[str]) -> list[str]:
|
|
14
|
+
"""Normalize a column specification to a list."""
|
|
15
15
|
if isinstance(column, str):
|
|
16
16
|
return [column]
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
if not
|
|
18
|
+
normalized = list(column)
|
|
19
|
+
if not normalized:
|
|
20
20
|
raise ValueError("At least one column must be specified")
|
|
21
|
-
return
|
|
21
|
+
return normalized
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def _glitch_dataset(
|
|
@@ -29,7 +29,7 @@ def _glitch_dataset(
|
|
|
29
29
|
seed: int = 151,
|
|
30
30
|
) -> Any:
|
|
31
31
|
"""Apply glitchlings to the provided dataset columns."""
|
|
32
|
-
columns =
|
|
32
|
+
columns = _normalize_columns(column)
|
|
33
33
|
gaggle = coerce_gaggle(glitchlings, seed=seed)
|
|
34
34
|
return gaggle.corrupt_dataset(dataset, columns)
|
|
35
35
|
|
glitchlings/dlc/prime.py
CHANGED
|
@@ -117,7 +117,7 @@ def _as_gaggle(
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def _extract_completion_text(completion: Any) -> str:
|
|
120
|
-
"""
|
|
120
|
+
"""Normalize a completion payload into a plain string."""
|
|
121
121
|
if isinstance(completion, str):
|
|
122
122
|
return completion
|
|
123
123
|
|
glitchlings/dlc/pytorch.py
CHANGED
|
@@ -9,7 +9,7 @@ from ..compat import get_torch_dataloader, require_torch
|
|
|
9
9
|
from ..compat import torch as _torch_dependency
|
|
10
10
|
from ..util.adapters import coerce_gaggle
|
|
11
11
|
from ..zoo import Gaggle, Glitchling
|
|
12
|
-
from ._shared import corrupt_text_value, is_textual_candidate,
|
|
12
|
+
from ._shared import corrupt_text_value, is_textual_candidate, normalize_column_spec
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _apply_to_batch(batch: Any, targets: list[str | int] | None, gaggle: Gaggle) -> Any:
|
|
@@ -134,8 +134,8 @@ def _ensure_dataloader_class() -> type[Any]:
|
|
|
134
134
|
) -> _GlitchedDataLoader:
|
|
135
135
|
"""Return a lazily glitched view of the loader's batches."""
|
|
136
136
|
gaggle = coerce_gaggle(glitchlings, seed=seed)
|
|
137
|
-
|
|
138
|
-
return _GlitchedDataLoader(self, gaggle, columns=
|
|
137
|
+
normalized = normalize_column_spec(columns)
|
|
138
|
+
return _GlitchedDataLoader(self, gaggle, columns=normalized)
|
|
139
139
|
|
|
140
140
|
setattr(dataloader_cls, "glitch", glitch)
|
|
141
141
|
|
|
@@ -8,7 +8,7 @@ from typing import Any, cast
|
|
|
8
8
|
from ..compat import get_pytorch_lightning_datamodule, require_pytorch_lightning
|
|
9
9
|
from ..util.adapters import coerce_gaggle
|
|
10
10
|
from ..zoo import Gaggle, Glitchling
|
|
11
|
-
from ._shared import corrupt_text_value,
|
|
11
|
+
from ._shared import corrupt_text_value, normalize_column_spec
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def _glitch_batch(batch: Any, columns: list[str], gaggle: Gaggle) -> Any:
|
|
@@ -40,10 +40,7 @@ def _wrap_dataloader(dataloader: Any, columns: list[str], gaggle: Gaggle) -> Any
|
|
|
40
40
|
if isinstance(dataloader, Mapping):
|
|
41
41
|
mapping_type = cast(type[Any], dataloader.__class__)
|
|
42
42
|
return mapping_type(
|
|
43
|
-
{
|
|
44
|
-
key: _wrap_dataloader(value, columns, gaggle)
|
|
45
|
-
for key, value in dataloader.items()
|
|
46
|
-
}
|
|
43
|
+
{key: _wrap_dataloader(value, columns, gaggle) for key, value in dataloader.items()}
|
|
47
44
|
)
|
|
48
45
|
|
|
49
46
|
if isinstance(dataloader, list):
|
|
@@ -54,9 +51,7 @@ def _wrap_dataloader(dataloader: Any, columns: list[str], gaggle: Gaggle) -> Any
|
|
|
54
51
|
|
|
55
52
|
if isinstance(dataloader, Sequence) and not isinstance(dataloader, (str, bytes, bytearray)):
|
|
56
53
|
sequence_type = cast(type[Any], dataloader.__class__)
|
|
57
|
-
return sequence_type(
|
|
58
|
-
_wrap_dataloader(value, columns, gaggle) for value in dataloader
|
|
59
|
-
)
|
|
54
|
+
return sequence_type(_wrap_dataloader(value, columns, gaggle) for value in dataloader)
|
|
60
55
|
|
|
61
56
|
return _GlitchedDataLoader(dataloader, columns, gaggle)
|
|
62
57
|
|
|
@@ -89,7 +84,7 @@ def _glitch_datamodule(
|
|
|
89
84
|
) -> Any:
|
|
90
85
|
"""Return a proxy that applies glitchlings to batches from the datamodule."""
|
|
91
86
|
|
|
92
|
-
columns =
|
|
87
|
+
columns = normalize_column_spec(column)
|
|
93
88
|
if columns is None: # pragma: no cover - defensive
|
|
94
89
|
raise ValueError("At least one column must be specified")
|
|
95
90
|
# Lightning datamodules only support string column names (mapping keys)
|
|
@@ -212,4 +207,3 @@ else: # pragma: no cover - optional dependency
|
|
|
212
207
|
|
|
213
208
|
|
|
214
209
|
__all__ = ["LightningDataModule", "install"]
|
|
215
|
-
|
glitchlings/lexicon/_cache.py
CHANGED
|
@@ -19,7 +19,7 @@ class CacheSnapshot:
|
|
|
19
19
|
checksum: str | None = None
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def _normalize_entries(payload: Mapping[str, object]) -> CacheEntries:
|
|
23
23
|
"""Convert raw cache payloads into canonical mapping form."""
|
|
24
24
|
entries: CacheEntries = {}
|
|
25
25
|
for key, values in payload.items():
|
|
@@ -75,7 +75,7 @@ def load_cache(path: Path) -> CacheSnapshot:
|
|
|
75
75
|
else:
|
|
76
76
|
entries_payload = payload # legacy format without metadata
|
|
77
77
|
|
|
78
|
-
entries =
|
|
78
|
+
entries = _normalize_entries(entries_payload)
|
|
79
79
|
if checksum is not None:
|
|
80
80
|
expected = compute_checksum(entries)
|
|
81
81
|
if checksum != expected:
|
|
@@ -88,9 +88,7 @@ def load_cache(path: Path) -> CacheSnapshot:
|
|
|
88
88
|
|
|
89
89
|
def write_cache(path: Path, entries: Mapping[str, Sequence[str]]) -> CacheSnapshot:
|
|
90
90
|
"""Persist ``entries`` to ``path`` with checksum metadata."""
|
|
91
|
-
serialisable: CacheEntries = {
|
|
92
|
-
key: list(values) for key, values in sorted(entries.items())
|
|
93
|
-
}
|
|
91
|
+
serialisable: CacheEntries = {key: list(values) for key, values in sorted(entries.items())}
|
|
94
92
|
checksum = compute_checksum(serialisable)
|
|
95
93
|
payload = {
|
|
96
94
|
"__meta__": {
|
glitchlings/lexicon/vector.py
CHANGED
|
@@ -16,6 +16,9 @@ from ._cache import CacheSnapshot
|
|
|
16
16
|
from ._cache import load_cache as _load_cache_file
|
|
17
17
|
from ._cache import write_cache as _write_cache_file
|
|
18
18
|
|
|
19
|
+
# Minimum number of neighbors to consider for similarity queries
|
|
20
|
+
MIN_NEIGHBORS = 1
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
def _cosine_similarity(vector_a: Sequence[float], vector_b: Sequence[float]) -> float:
|
|
21
24
|
"""Return the cosine similarity between two dense vectors."""
|
|
@@ -304,7 +307,7 @@ class VectorLexicon(LexiconBackend):
|
|
|
304
307
|
"""Initialise the lexicon with an embedding ``source`` and optional cache."""
|
|
305
308
|
super().__init__(seed=seed)
|
|
306
309
|
self._adapter = _resolve_source(source)
|
|
307
|
-
self._max_neighbors = max(
|
|
310
|
+
self._max_neighbors = max(MIN_NEIGHBORS, max_neighbors)
|
|
308
311
|
self._min_similarity = min_similarity
|
|
309
312
|
self._cache: MutableMapping[str, list[str]] = {}
|
|
310
313
|
self._cache_path: Path | None
|
|
@@ -371,7 +374,7 @@ class VectorLexicon(LexiconBackend):
|
|
|
371
374
|
if cache_key in self._cache:
|
|
372
375
|
return self._cache[cache_key]
|
|
373
376
|
|
|
374
|
-
neighbor_limit = self._max_neighbors if limit is None else max(
|
|
377
|
+
neighbor_limit = self._max_neighbors if limit is None else max(MIN_NEIGHBORS, limit)
|
|
375
378
|
neighbors = self._fetch_neighbors(
|
|
376
379
|
original=original, normalized=normalized, limit=neighbor_limit
|
|
377
380
|
)
|
|
@@ -624,9 +627,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
624
627
|
)
|
|
625
628
|
iterator = lexicon.iter_vocabulary()
|
|
626
629
|
if args.limit is not None:
|
|
627
|
-
token_iter = (
|
|
628
|
-
token for index, token in enumerate(iterator) if index < args.limit
|
|
629
|
-
)
|
|
630
|
+
token_iter = (token for index, token in enumerate(iterator) if index < args.limit)
|
|
630
631
|
else:
|
|
631
632
|
token_iter = iterator
|
|
632
633
|
|
glitchlings/lexicon/wordnet.py
CHANGED
|
@@ -13,21 +13,17 @@ from ._cache import CacheSnapshot
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class _LemmaProtocol(Protocol):
|
|
16
|
-
def name(self) -> str:
|
|
17
|
-
...
|
|
16
|
+
def name(self) -> str: ...
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class _SynsetProtocol(Protocol):
|
|
21
|
-
def lemmas(self) -> Sequence[_LemmaProtocol]:
|
|
22
|
-
...
|
|
20
|
+
def lemmas(self) -> Sequence[_LemmaProtocol]: ...
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
class _WordNetResource(Protocol):
|
|
26
|
-
def synsets(self, word: str, pos: str | None = None) -> Sequence[_SynsetProtocol]:
|
|
27
|
-
...
|
|
24
|
+
def synsets(self, word: str, pos: str | None = None) -> Sequence[_SynsetProtocol]: ...
|
|
28
25
|
|
|
29
|
-
def ensure_loaded(self) -> None:
|
|
30
|
-
...
|
|
26
|
+
def ensure_loaded(self) -> None: ...
|
|
31
27
|
|
|
32
28
|
|
|
33
29
|
WordNetCorpusReaderFactory = Callable[[Any, Any], _WordNetResource]
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Hokey expressive lengthening generator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from .stretch_locator import StretchSite, apply_stretch, find_stretch_site
|
|
8
|
+
from .stretchability import RandomLike, StretchabilityAnalyzer, StretchabilityFeatures
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(slots=True)
|
|
12
|
+
class HokeyConfig:
|
|
13
|
+
rate: float = 0.3
|
|
14
|
+
extension_min: int = 2
|
|
15
|
+
extension_max: int = 5
|
|
16
|
+
base_p: float = 0.45
|
|
17
|
+
word_length_threshold: int = 6
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(slots=True)
|
|
21
|
+
class StretchEvent:
|
|
22
|
+
token_index: int
|
|
23
|
+
original: str
|
|
24
|
+
stretched: str
|
|
25
|
+
repeats: int
|
|
26
|
+
site: StretchSite
|
|
27
|
+
score: float
|
|
28
|
+
features: StretchabilityFeatures
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class NegativeBinomialSampler:
|
|
32
|
+
"""Sample stretch lengths from a clipped negative binomial distribution."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, base_p: float = 0.45) -> None:
|
|
35
|
+
self.base_p = base_p
|
|
36
|
+
|
|
37
|
+
def sample(
|
|
38
|
+
self,
|
|
39
|
+
rng: RandomLike,
|
|
40
|
+
*,
|
|
41
|
+
intensity: float,
|
|
42
|
+
minimum: int,
|
|
43
|
+
maximum: int,
|
|
44
|
+
) -> int:
|
|
45
|
+
minimum = max(0, int(minimum))
|
|
46
|
+
maximum = max(minimum, int(maximum))
|
|
47
|
+
if maximum == 0:
|
|
48
|
+
return 0
|
|
49
|
+
if maximum == minimum:
|
|
50
|
+
return maximum
|
|
51
|
+
|
|
52
|
+
r = max(1, int(round(1 + 2 * intensity)))
|
|
53
|
+
adjusted_p = self.base_p / (1.0 + 0.75 * max(0.0, intensity))
|
|
54
|
+
adjusted_p = max(0.05, min(0.95, adjusted_p))
|
|
55
|
+
failures = sum(self._geometric_sample(rng, adjusted_p) for _ in range(r))
|
|
56
|
+
extra = minimum + failures
|
|
57
|
+
return max(minimum, min(maximum, extra))
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def _geometric_sample(rng: RandomLike, p: float) -> int:
|
|
61
|
+
count = 0
|
|
62
|
+
while rng.random() > p:
|
|
63
|
+
count += 1
|
|
64
|
+
return count
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class HokeyGenerator:
|
|
68
|
+
"""Full expressive lengthening pipeline."""
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
analyzer: StretchabilityAnalyzer | None = None,
|
|
73
|
+
sampler: NegativeBinomialSampler | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
self.analyzer = analyzer or StretchabilityAnalyzer()
|
|
76
|
+
self.sampler = sampler or NegativeBinomialSampler()
|
|
77
|
+
|
|
78
|
+
def generate(
|
|
79
|
+
self,
|
|
80
|
+
text: str,
|
|
81
|
+
*,
|
|
82
|
+
rng: RandomLike,
|
|
83
|
+
config: HokeyConfig,
|
|
84
|
+
) -> tuple[str, list[StretchEvent]]:
|
|
85
|
+
if not text:
|
|
86
|
+
return text, []
|
|
87
|
+
|
|
88
|
+
if config.base_p != self.sampler.base_p:
|
|
89
|
+
self.sampler.base_p = config.base_p
|
|
90
|
+
|
|
91
|
+
tokens = self.analyzer.tokenise(text)
|
|
92
|
+
candidates = self.analyzer.analyse_tokens(tokens)
|
|
93
|
+
selected = self.analyzer.select_candidates(candidates, rate=config.rate, rng=rng)
|
|
94
|
+
if not selected:
|
|
95
|
+
return text, []
|
|
96
|
+
|
|
97
|
+
token_strings = [token.text for token in tokens]
|
|
98
|
+
events: list[StretchEvent] = []
|
|
99
|
+
|
|
100
|
+
for candidate in selected:
|
|
101
|
+
token_idx = candidate.token.index
|
|
102
|
+
original = token_strings[token_idx]
|
|
103
|
+
site = find_stretch_site(original)
|
|
104
|
+
if site is None:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
intensity = min(1.5, candidate.features.intensity() + 0.35 * candidate.score)
|
|
108
|
+
alpha_count = sum(1 for ch in original if ch.isalpha())
|
|
109
|
+
if config.word_length_threshold > 0 and alpha_count > config.word_length_threshold * 2:
|
|
110
|
+
continue
|
|
111
|
+
if config.word_length_threshold > 0 and alpha_count > config.word_length_threshold:
|
|
112
|
+
excess = alpha_count - config.word_length_threshold
|
|
113
|
+
intensity = intensity / (1.0 + 0.35 * excess)
|
|
114
|
+
if candidate.score < 0.35 and excess >= 2:
|
|
115
|
+
continue
|
|
116
|
+
intensity = max(0.05, intensity)
|
|
117
|
+
|
|
118
|
+
repeats = self.sampler.sample(
|
|
119
|
+
rng,
|
|
120
|
+
intensity=intensity,
|
|
121
|
+
minimum=config.extension_min,
|
|
122
|
+
maximum=config.extension_max,
|
|
123
|
+
)
|
|
124
|
+
if repeats <= 0:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
stretched_word = apply_stretch(original, site, repeats)
|
|
128
|
+
token_strings[token_idx] = stretched_word
|
|
129
|
+
events.append(
|
|
130
|
+
StretchEvent(
|
|
131
|
+
token_index=token_idx,
|
|
132
|
+
original=original,
|
|
133
|
+
stretched=stretched_word,
|
|
134
|
+
repeats=repeats,
|
|
135
|
+
site=site,
|
|
136
|
+
score=candidate.score,
|
|
137
|
+
features=candidate.features,
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return "".join(token_strings), events
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
__all__ = ["HokeyGenerator", "HokeyConfig", "StretchEvent", "NegativeBinomialSampler"]
|