JinghuiLuAstronaut commited on
Commit
37fae15
·
verified ·
1 Parent(s): 009ed09

Add files using upload-large-folder tool

Browse files
Files changed (20) hide show
  1. LTA_openwebtext_dualt/logs/lm1b_classic_dirichlet_every1k_infer_watch/infer_lta_lm1b_classic_dirichlet_len256_gbs512_4gpu_10k_save1k_20260523_step_0016000_t1p45.log +20 -0
  2. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_elffile.py +108 -0
  3. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_manylinux.py +262 -0
  4. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_musllinux.py +85 -0
  5. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_parser.py +393 -0
  6. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/dependency_groups.py +302 -0
  7. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/pylock.py +905 -0
  8. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/utils.py +296 -0
  9. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/cohere_asr/feature_extraction_cohere_asr.py +374 -0
  10. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/cohere_asr/processing_cohere_asr.py +188 -0
  11. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/chunk_utils.py +398 -0
  12. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/loss.py +104 -0
  13. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/protein.py +330 -0
  14. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/residue_constants.py +979 -0
  15. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/rigid_utils.py +1243 -0
  16. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/ovis2/modeling_ovis2.py +730 -0
  17. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/__init__.py +29 -0
  18. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/configuration_textnet.py +94 -0
  19. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/image_processing_pil_textnet.py +134 -0
  20. LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/modeling_textnet.py +417 -0
LTA_openwebtext_dualt/logs/lm1b_classic_dirichlet_every1k_infer_watch/infer_lta_lm1b_classic_dirichlet_len256_gbs512_4gpu_10k_save1k_20260523_step_0016000_t1p45.log ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [watch-classic-1k] 2026-05-23_16:05:04 infer runs/lta_lm1b_classic_dirichlet_len256_gbs512_4gpu_10k_save1k_20260523/step_0016000.pt -> docs/lta_samples/metrics_20260523/lm1b_classic_dirichlet_len256_every1k_normal_steps_state_t1p45_c1024_n256/lta_lm1b_classic_dirichlet_len256_gbs512_4gpu_10k_save1k_20260523/step_0016000
2
+ [ckpt] runs/lta_lm1b_classic_dirichlet_len256_gbs512_4gpu_10k_save1k_20260523/step_0016000.pt step=16000
3
+ [decode] steps128_c1024_t1p45 generated 16/256
4
+ [decode] steps128_c1024_t1p45 generated 32/256
5
+ [decode] steps128_c1024_t1p45 generated 48/256
6
+ [decode] steps128_c1024_t1p45 generated 64/256
7
+ [decode] steps128_c1024_t1p45 generated 80/256
8
+ [decode] steps128_c1024_t1p45 generated 96/256
9
+ [decode] steps128_c1024_t1p45 generated 112/256
10
+ [decode] steps128_c1024_t1p45 generated 128/256
11
+ [decode] steps128_c1024_t1p45 generated 144/256
12
+ [decode] steps128_c1024_t1p45 generated 160/256
13
+ [decode] steps128_c1024_t1p45 generated 176/256
14
+ [decode] steps128_c1024_t1p45 generated 192/256
15
+ [decode] steps128_c1024_t1p45 generated 208/256
16
+ [decode] steps128_c1024_t1p45 generated 224/256
17
+ [decode] steps128_c1024_t1p45 generated 240/256
18
+ [decode] steps128_c1024_t1p45 generated 256/256
19
+ [summary] {"name": "steps128_c1024_t1p45", "step": 16000, "decode_steps": 128, "concentration_max": 1024.0, "raw_genppl": 13.810265002566238, "stripped_genppl": 13.59726076657564, "sample_entropy": 1.4121913855304282, "distinct_1": 0.0078125, "distinct_2": 0.028247549019607843, "top_token_mass": 0.387176513671875, "raw_kept": 256, "stripped_kept": 256}
20
+ [watch-classic-1k] 2026-05-23_16:07:59 done step_0016000
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_elffile.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ELF file parser.
3
+
4
+ This provides a class ``ELFFile`` that parses an ELF executable in a similar
5
+ interface to ``ZipFile``. Only the read interface is implemented.
6
+
7
+ ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import enum
13
+ import os
14
+ import struct
15
+ from typing import IO
16
+
17
+
18
+ class ELFInvalid(ValueError):
19
+ pass
20
+
21
+
22
+ class EIClass(enum.IntEnum):
23
+ C32 = 1
24
+ C64 = 2
25
+
26
+
27
+ class EIData(enum.IntEnum):
28
+ Lsb = 1
29
+ Msb = 2
30
+
31
+
32
+ class EMachine(enum.IntEnum):
33
+ I386 = 3
34
+ S390 = 22
35
+ Arm = 40
36
+ X8664 = 62
37
+ AArc64 = 183
38
+
39
+
40
+ class ELFFile:
41
+ """
42
+ Representation of an ELF executable.
43
+ """
44
+
45
+ def __init__(self, f: IO[bytes]) -> None:
46
+ self._f = f
47
+
48
+ try:
49
+ ident = self._read("16B")
50
+ except struct.error as e:
51
+ raise ELFInvalid("unable to parse identification") from e
52
+ magic = bytes(ident[:4])
53
+ if magic != b"\x7fELF":
54
+ raise ELFInvalid(f"invalid magic: {magic!r}")
55
+
56
+ self.capacity = ident[4] # Format for program header (bitness).
57
+ self.encoding = ident[5] # Data structure encoding (endianness).
58
+
59
+ try:
60
+ # e_fmt: Format for program header.
61
+ # p_fmt: Format for section header.
62
+ # p_idx: Indexes to find p_type, p_offset, and p_filesz.
63
+ e_fmt, self._p_fmt, self._p_idx = {
64
+ (1, 1): ("<HHIIIIIHHH", "<IIIIIIII", (0, 1, 4)), # 32-bit LSB.
65
+ (1, 2): (">HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB.
66
+ (2, 1): ("<HHIQQQIHHH", "<IIQQQQQQ", (0, 2, 5)), # 64-bit LSB.
67
+ (2, 2): (">HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB.
68
+ }[(self.capacity, self.encoding)]
69
+ except KeyError as e:
70
+ raise ELFInvalid(
71
+ f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})"
72
+ ) from e
73
+
74
+ try:
75
+ (
76
+ _,
77
+ self.machine, # Architecture type.
78
+ _,
79
+ _,
80
+ self._e_phoff, # Offset of program header.
81
+ _,
82
+ self.flags, # Processor-specific flags.
83
+ _,
84
+ self._e_phentsize, # Size of section.
85
+ self._e_phnum, # Number of sections.
86
+ ) = self._read(e_fmt)
87
+ except struct.error as e:
88
+ raise ELFInvalid("unable to parse machine and section information") from e
89
+
90
+ def _read(self, fmt: str) -> tuple[int, ...]:
91
+ return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))
92
+
93
+ @property
94
+ def interpreter(self) -> str | None:
95
+ """
96
+ The path recorded in the ``PT_INTERP`` section header.
97
+ """
98
+ for index in range(self._e_phnum):
99
+ self._f.seek(self._e_phoff + self._e_phentsize * index)
100
+ try:
101
+ data = self._read(self._p_fmt)
102
+ except struct.error:
103
+ continue
104
+ if data[self._p_idx[0]] != 3: # Not PT_INTERP.
105
+ continue
106
+ self._f.seek(data[self._p_idx[1]])
107
+ return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0")
108
+ return None
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_manylinux.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import collections
4
+ import contextlib
5
+ import functools
6
+ import os
7
+ import re
8
+ import sys
9
+ import warnings
10
+ from typing import Generator, Iterator, NamedTuple, Sequence
11
+
12
+ from ._elffile import EIClass, EIData, ELFFile, EMachine
13
+
14
+ EF_ARM_ABIMASK = 0xFF000000
15
+ EF_ARM_ABI_VER5 = 0x05000000
16
+ EF_ARM_ABI_FLOAT_HARD = 0x00000400
17
+
18
+ _ALLOWED_ARCHS = {
19
+ "x86_64",
20
+ "aarch64",
21
+ "ppc64",
22
+ "ppc64le",
23
+ "s390x",
24
+ "loongarch64",
25
+ "riscv64",
26
+ }
27
+
28
+
29
+ # `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
30
+ # as the type for `path` until then.
31
+ @contextlib.contextmanager
32
+ def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]:
33
+ try:
34
+ with open(path, "rb") as f:
35
+ yield ELFFile(f)
36
+ except (OSError, TypeError, ValueError):
37
+ yield None
38
+
39
+
40
+ def _is_linux_armhf(executable: str) -> bool:
41
+ # hard-float ABI can be detected from the ELF header of the running
42
+ # process
43
+ # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
44
+ with _parse_elf(executable) as f:
45
+ return (
46
+ f is not None
47
+ and f.capacity == EIClass.C32
48
+ and f.encoding == EIData.Lsb
49
+ and f.machine == EMachine.Arm
50
+ and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5
51
+ and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD
52
+ )
53
+
54
+
55
+ def _is_linux_i686(executable: str) -> bool:
56
+ with _parse_elf(executable) as f:
57
+ return (
58
+ f is not None
59
+ and f.capacity == EIClass.C32
60
+ and f.encoding == EIData.Lsb
61
+ and f.machine == EMachine.I386
62
+ )
63
+
64
+
65
+ def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool:
66
+ if "armv7l" in archs:
67
+ return _is_linux_armhf(executable)
68
+ if "i686" in archs:
69
+ return _is_linux_i686(executable)
70
+ return any(arch in _ALLOWED_ARCHS for arch in archs)
71
+
72
+
73
+ # If glibc ever changes its major version, we need to know what the last
74
+ # minor version was, so we can build the complete list of all versions.
75
+ # For now, guess what the highest minor version might be, assume it will
76
+ # be 50 for testing. Once this actually happens, update the dictionary
77
+ # with the actual value.
78
+ _LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50)
79
+
80
+
81
+ class _GLibCVersion(NamedTuple):
82
+ major: int
83
+ minor: int
84
+
85
+
86
+ def _glibc_version_string_confstr() -> str | None:
87
+ """
88
+ Primary implementation of glibc_version_string using os.confstr.
89
+ """
90
+ # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
91
+ # to be broken or missing. This strategy is used in the standard library
92
+ # platform module.
93
+ # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
94
+ try:
95
+ # Should be a string like "glibc 2.17".
96
+ version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION")
97
+ assert version_string is not None
98
+ _, version = version_string.rsplit()
99
+ except (AssertionError, AttributeError, OSError, ValueError):
100
+ # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
101
+ return None
102
+ return version
103
+
104
+
105
+ def _glibc_version_string_ctypes() -> str | None:
106
+ """
107
+ Fallback implementation of glibc_version_string using ctypes.
108
+ """
109
+ try:
110
+ import ctypes # noqa: PLC0415
111
+ except ImportError:
112
+ return None
113
+
114
+ # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
115
+ # manpage says, "If filename is NULL, then the returned handle is for the
116
+ # main program". This way we can let the linker do the work to figure out
117
+ # which libc our process is actually using.
118
+ #
119
+ # We must also handle the special case where the executable is not a
120
+ # dynamically linked executable. This can occur when using musl libc,
121
+ # for example. In this situation, dlopen() will error, leading to an
122
+ # OSError. Interestingly, at least in the case of musl, there is no
123
+ # errno set on the OSError. The single string argument used to construct
124
+ # OSError comes from libc itself and is therefore not portable to
125
+ # hard code here. In any case, failure to call dlopen() means we
126
+ # can proceed, so we bail on our attempt.
127
+ try:
128
+ process_namespace = ctypes.CDLL(None)
129
+ except OSError:
130
+ return None
131
+
132
+ try:
133
+ gnu_get_libc_version = process_namespace.gnu_get_libc_version
134
+ except AttributeError:
135
+ # Symbol doesn't exist -> therefore, we are not linked to
136
+ # glibc.
137
+ return None
138
+
139
+ # Call gnu_get_libc_version, which returns a string like "2.5"
140
+ gnu_get_libc_version.restype = ctypes.c_char_p
141
+ version_str: str = gnu_get_libc_version()
142
+ # py2 / py3 compatibility:
143
+ if not isinstance(version_str, str):
144
+ version_str = version_str.decode("ascii")
145
+
146
+ return version_str
147
+
148
+
149
+ def _glibc_version_string() -> str | None:
150
+ """Returns glibc version string, or None if not using glibc."""
151
+ return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
152
+
153
+
154
+ def _parse_glibc_version(version_str: str) -> _GLibCVersion:
155
+ """Parse glibc version.
156
+
157
+ We use a regexp instead of str.split because we want to discard any
158
+ random junk that might come after the minor version -- this might happen
159
+ in patched/forked versions of glibc (e.g. Linaro's version of glibc
160
+ uses version strings like "2.20-2014.11"). See gh-3588.
161
+ """
162
+ m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
163
+ if not m:
164
+ warnings.warn(
165
+ f"Expected glibc version with 2 components major.minor, got: {version_str}",
166
+ RuntimeWarning,
167
+ stacklevel=2,
168
+ )
169
+ return _GLibCVersion(-1, -1)
170
+ return _GLibCVersion(int(m.group("major")), int(m.group("minor")))
171
+
172
+
173
+ @functools.lru_cache
174
+ def _get_glibc_version() -> _GLibCVersion:
175
+ version_str = _glibc_version_string()
176
+ if version_str is None:
177
+ return _GLibCVersion(-1, -1)
178
+ return _parse_glibc_version(version_str)
179
+
180
+
181
+ # From PEP 513, PEP 600
182
+ def _is_compatible(arch: str, version: _GLibCVersion) -> bool:
183
+ sys_glibc = _get_glibc_version()
184
+ if sys_glibc < version:
185
+ return False
186
+ # Check for presence of _manylinux module.
187
+ try:
188
+ import _manylinux # noqa: PLC0415
189
+ except ImportError:
190
+ return True
191
+ if hasattr(_manylinux, "manylinux_compatible"):
192
+ result = _manylinux.manylinux_compatible(version[0], version[1], arch)
193
+ if result is not None:
194
+ return bool(result)
195
+ return True
196
+ if version == _GLibCVersion(2, 5) and hasattr(_manylinux, "manylinux1_compatible"):
197
+ return bool(_manylinux.manylinux1_compatible)
198
+ if version == _GLibCVersion(2, 12) and hasattr(
199
+ _manylinux, "manylinux2010_compatible"
200
+ ):
201
+ return bool(_manylinux.manylinux2010_compatible)
202
+ if version == _GLibCVersion(2, 17) and hasattr(
203
+ _manylinux, "manylinux2014_compatible"
204
+ ):
205
+ return bool(_manylinux.manylinux2014_compatible)
206
+ return True
207
+
208
+
209
+ _LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = {
210
+ # CentOS 7 w/ glibc 2.17 (PEP 599)
211
+ _GLibCVersion(2, 17): "manylinux2014",
212
+ # CentOS 6 w/ glibc 2.12 (PEP 571)
213
+ _GLibCVersion(2, 12): "manylinux2010",
214
+ # CentOS 5 w/ glibc 2.5 (PEP 513)
215
+ _GLibCVersion(2, 5): "manylinux1",
216
+ }
217
+
218
+
219
+ def platform_tags(archs: Sequence[str]) -> Iterator[str]:
220
+ """Generate manylinux tags compatible to the current platform.
221
+
222
+ :param archs: Sequence of compatible architectures.
223
+ The first one shall be the closest to the actual architecture and be the part of
224
+ platform tag after the ``linux_`` prefix, e.g. ``x86_64``.
225
+ The ``linux_`` prefix is assumed as a prerequisite for the current platform to
226
+ be manylinux-compatible.
227
+
228
+ :returns: An iterator of compatible manylinux tags.
229
+ """
230
+ if not _have_compatible_abi(sys.executable, archs):
231
+ return
232
+ # Oldest glibc to be supported regardless of architecture is (2, 17).
233
+ too_old_glibc2 = _GLibCVersion(2, 16)
234
+ if set(archs) & {"x86_64", "i686"}:
235
+ # On x86/i686 also oldest glibc to be supported is (2, 5).
236
+ too_old_glibc2 = _GLibCVersion(2, 4)
237
+ current_glibc = _GLibCVersion(*_get_glibc_version())
238
+ glibc_max_list = [current_glibc]
239
+ # We can assume compatibility across glibc major versions.
240
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=24636
241
+ #
242
+ # Build a list of maximum glibc versions so that we can
243
+ # output the canonical list of all glibc from current_glibc
244
+ # down to too_old_glibc2, including all intermediary versions.
245
+ for glibc_major in range(current_glibc.major - 1, 1, -1):
246
+ glibc_minor = _LAST_GLIBC_MINOR[glibc_major]
247
+ glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor))
248
+ for arch in archs:
249
+ for glibc_max in glibc_max_list:
250
+ if glibc_max.major == too_old_glibc2.major:
251
+ min_minor = too_old_glibc2.minor
252
+ else:
253
+ # For other glibc major versions oldest supported is (x, 0).
254
+ min_minor = -1
255
+ for glibc_minor in range(glibc_max.minor, min_minor, -1):
256
+ glibc_version = _GLibCVersion(glibc_max.major, glibc_minor)
257
+ if _is_compatible(arch, glibc_version):
258
+ yield "manylinux_{}_{}_{}".format(*glibc_version, arch)
259
+
260
+ # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
261
+ if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version):
262
+ yield f"{legacy_tag}_{arch}"
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_musllinux.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PEP 656 support.
2
+
3
+ This module implements logic to detect if the currently running Python is
4
+ linked against musl, and what musl version is used.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import functools
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ from typing import Iterator, NamedTuple, Sequence
14
+
15
+ from ._elffile import ELFFile
16
+
17
+
18
+ class _MuslVersion(NamedTuple):
19
+ major: int
20
+ minor: int
21
+
22
+
23
+ def _parse_musl_version(output: str) -> _MuslVersion | None:
24
+ lines = [n for n in (n.strip() for n in output.splitlines()) if n]
25
+ if len(lines) < 2 or lines[0][:4] != "musl":
26
+ return None
27
+ m = re.match(r"Version (\d+)\.(\d+)", lines[1])
28
+ if not m:
29
+ return None
30
+ return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
31
+
32
+
33
+ @functools.lru_cache
34
+ def _get_musl_version(executable: str) -> _MuslVersion | None:
35
+ """Detect currently-running musl runtime version.
36
+
37
+ This is done by checking the specified executable's dynamic linking
38
+ information, and invoking the loader to parse its output for a version
39
+ string. If the loader is musl, the output would be something like::
40
+
41
+ musl libc (x86_64)
42
+ Version 1.2.2
43
+ Dynamic Program Loader
44
+ """
45
+ try:
46
+ with open(executable, "rb") as f:
47
+ ld = ELFFile(f).interpreter
48
+ except (OSError, TypeError, ValueError):
49
+ return None
50
+ if ld is None or "musl" not in ld:
51
+ return None
52
+ proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True)
53
+ return _parse_musl_version(proc.stderr)
54
+
55
+
56
+ def platform_tags(archs: Sequence[str]) -> Iterator[str]:
57
+ """Generate musllinux tags compatible to the current platform.
58
+
59
+ :param archs: Sequence of compatible architectures.
60
+ The first one shall be the closest to the actual architecture and be the part of
61
+ platform tag after the ``linux_`` prefix, e.g. ``x86_64``.
62
+ The ``linux_`` prefix is assumed as a prerequisite for the current platform to
63
+ be musllinux-compatible.
64
+
65
+ :returns: An iterator of compatible musllinux tags.
66
+ """
67
+ sys_musl = _get_musl_version(sys.executable)
68
+ if sys_musl is None: # Python not dynamically linked against musl.
69
+ return
70
+ for arch in archs:
71
+ for minor in range(sys_musl.minor, -1, -1):
72
+ yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
73
+
74
+
75
+ if __name__ == "__main__": # pragma: no cover
76
+ import sysconfig
77
+
78
+ plat = sysconfig.get_platform()
79
+ assert plat.startswith("linux-"), "not linux"
80
+
81
+ print("plat:", plat)
82
+ print("musl:", _get_musl_version(sys.executable))
83
+ print("tags:", end=" ")
84
+ for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
85
+ print(t, end="\n ")
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/_parser.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Handwritten parser of dependency specifiers.
2
+
3
+ The docstring for each __parse_* function contains EBNF-inspired grammar representing
4
+ the implementation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import ast
10
+ from typing import List, Literal, NamedTuple, Sequence, Tuple, Union
11
+
12
+ from ._tokenizer import DEFAULT_RULES, Tokenizer
13
+
14
+
15
+ class Node:
16
+ __slots__ = ("value",)
17
+
18
+ def __init__(self, value: str) -> None:
19
+ self.value = value
20
+
21
+ def __str__(self) -> str:
22
+ return self.value
23
+
24
+ def __repr__(self) -> str:
25
+ return f"<{self.__class__.__name__}({self.value!r})>"
26
+
27
+ def serialize(self) -> str:
28
+ raise NotImplementedError
29
+
30
+ def __getstate__(self) -> str:
31
+ # Return just the value string for compactness and stability.
32
+ return self.value
33
+
34
+ def _restore_value(self, value: object) -> None:
35
+ if not isinstance(value, str):
36
+ raise TypeError(
37
+ f"Cannot restore {self.__class__.__name__} value from {value!r}"
38
+ )
39
+ self.value = value
40
+
41
+ def __setstate__(self, state: object) -> None:
42
+ if isinstance(state, str):
43
+ # New format (26.2+): just the value string.
44
+ self._restore_value(state)
45
+ return
46
+ if isinstance(state, tuple) and len(state) == 2:
47
+ # Old format (packaging <= 26.0, __slots__): (None, {slot: value}).
48
+ _, slot_dict = state
49
+ if isinstance(slot_dict, dict) and "value" in slot_dict:
50
+ self._restore_value(slot_dict["value"])
51
+ return
52
+ if isinstance(state, dict) and "value" in state:
53
+ # Old format (packaging <= 25.0, no __slots__): plain __dict__.
54
+ self._restore_value(state["value"])
55
+ return
56
+ raise TypeError(f"Cannot restore {self.__class__.__name__} from {state!r}")
57
+
58
+
59
+ class Variable(Node):
60
+ __slots__ = ()
61
+
62
+ def serialize(self) -> str:
63
+ return str(self)
64
+
65
+
66
+ class Value(Node):
67
+ __slots__ = ()
68
+
69
+ def serialize(self) -> str:
70
+ return f'"{self}"'
71
+
72
+
73
+ class Op(Node):
74
+ __slots__ = ()
75
+
76
+ def serialize(self) -> str:
77
+ return str(self)
78
+
79
+
80
+ MarkerLogical = Literal["and", "or"]
81
+ MarkerVar = Union[Variable, Value]
82
+ MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
83
+ MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
84
+ MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]]
85
+
86
+
87
+ class ParsedRequirement(NamedTuple):
88
+ name: str
89
+ url: str
90
+ extras: list[str]
91
+ specifier: str
92
+ marker: MarkerList | None
93
+
94
+
95
+ # --------------------------------------------------------------------------------------
96
+ # Recursive descent parser for dependency specifier
97
+ # --------------------------------------------------------------------------------------
98
+ def parse_requirement(source: str) -> ParsedRequirement:
99
+ return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES))
100
+
101
+
102
+ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
103
+ """
104
+ requirement = WS? IDENTIFIER WS? extras WS? requirement_details
105
+ """
106
+ tokenizer.consume("WS")
107
+
108
+ name_token = tokenizer.expect(
109
+ "IDENTIFIER", expected="package name at the start of dependency specifier"
110
+ )
111
+ name = name_token.text
112
+ tokenizer.consume("WS")
113
+
114
+ extras = _parse_extras(tokenizer)
115
+ tokenizer.consume("WS")
116
+
117
+ url, specifier, marker = _parse_requirement_details(tokenizer)
118
+ tokenizer.expect("END", expected="end of dependency specifier")
119
+
120
+ return ParsedRequirement(name, url, extras, specifier, marker)
121
+
122
+
123
+ def _parse_requirement_details(
124
+ tokenizer: Tokenizer,
125
+ ) -> tuple[str, str, MarkerList | None]:
126
+ """
127
+ requirement_details = AT URL (WS requirement_marker?)?
128
+ | specifier WS? (requirement_marker)?
129
+ """
130
+
131
+ specifier = ""
132
+ url = ""
133
+ marker = None
134
+
135
+ if tokenizer.check("AT"):
136
+ tokenizer.read()
137
+ tokenizer.consume("WS")
138
+
139
+ url_start = tokenizer.position
140
+ url = tokenizer.expect("URL", expected="URL after @").text
141
+ if tokenizer.check("END", peek=True):
142
+ return (url, specifier, marker)
143
+
144
+ tokenizer.expect("WS", expected="whitespace after URL")
145
+
146
+ # The input might end after whitespace.
147
+ if tokenizer.check("END", peek=True):
148
+ return (url, specifier, marker)
149
+
150
+ marker = _parse_requirement_marker(
151
+ tokenizer,
152
+ span_start=url_start,
153
+ expected="semicolon (after URL and whitespace)",
154
+ )
155
+ else:
156
+ specifier_start = tokenizer.position
157
+ specifier = _parse_specifier(tokenizer)
158
+ tokenizer.consume("WS")
159
+
160
+ if tokenizer.check("END", peek=True):
161
+ return (url, specifier, marker)
162
+
163
+ marker = _parse_requirement_marker(
164
+ tokenizer,
165
+ span_start=specifier_start,
166
+ expected=(
167
+ "comma (within version specifier), semicolon (after version specifier)"
168
+ if specifier
169
+ else "semicolon (after name with no version specifier)"
170
+ ),
171
+ )
172
+
173
+ return (url, specifier, marker)
174
+
175
+
176
+ def _parse_requirement_marker(
177
+ tokenizer: Tokenizer, *, span_start: int, expected: str
178
+ ) -> MarkerList:
179
+ """
180
+ requirement_marker = SEMICOLON marker WS?
181
+ """
182
+
183
+ if not tokenizer.check("SEMICOLON"):
184
+ tokenizer.raise_syntax_error(
185
+ f"Expected {expected} or end",
186
+ span_start=span_start,
187
+ span_end=None,
188
+ )
189
+ tokenizer.read()
190
+
191
+ marker = _parse_marker(tokenizer)
192
+ tokenizer.consume("WS")
193
+
194
+ return marker
195
+
196
+
197
+ def _parse_extras(tokenizer: Tokenizer) -> list[str]:
198
+ """
199
+ extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
200
+ """
201
+ if not tokenizer.check("LEFT_BRACKET", peek=True):
202
+ return []
203
+
204
+ with tokenizer.enclosing_tokens(
205
+ "LEFT_BRACKET",
206
+ "RIGHT_BRACKET",
207
+ around="extras",
208
+ ):
209
+ tokenizer.consume("WS")
210
+ extras = _parse_extras_list(tokenizer)
211
+ tokenizer.consume("WS")
212
+
213
+ return extras
214
+
215
+
216
+ def _parse_extras_list(tokenizer: Tokenizer) -> list[str]:
217
+ """
218
+ extras_list = identifier (wsp* ',' wsp* identifier)*
219
+ """
220
+ extras: list[str] = []
221
+
222
+ if not tokenizer.check("IDENTIFIER"):
223
+ return extras
224
+
225
+ extras.append(tokenizer.read().text)
226
+
227
+ while True:
228
+ tokenizer.consume("WS")
229
+ if tokenizer.check("IDENTIFIER", peek=True):
230
+ tokenizer.raise_syntax_error("Expected comma between extra names")
231
+ elif not tokenizer.check("COMMA"):
232
+ break
233
+
234
+ tokenizer.read()
235
+ tokenizer.consume("WS")
236
+
237
+ extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma")
238
+ extras.append(extra_token.text)
239
+
240
+ return extras
241
+
242
+
243
+ def _parse_specifier(tokenizer: Tokenizer) -> str:
244
+ """
245
+ specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS
246
+ | WS? version_many WS?
247
+ """
248
+ with tokenizer.enclosing_tokens(
249
+ "LEFT_PARENTHESIS",
250
+ "RIGHT_PARENTHESIS",
251
+ around="version specifier",
252
+ ):
253
+ tokenizer.consume("WS")
254
+ parsed_specifiers = _parse_version_many(tokenizer)
255
+ tokenizer.consume("WS")
256
+
257
+ return parsed_specifiers
258
+
259
+
260
+ def _parse_version_many(tokenizer: Tokenizer) -> str:
261
+ """
262
+ version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)?
263
+ """
264
+ parsed_specifiers = ""
265
+ while tokenizer.check("SPECIFIER"):
266
+ span_start = tokenizer.position
267
+ parsed_specifiers += tokenizer.read().text
268
+ if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True):
269
+ tokenizer.raise_syntax_error(
270
+ ".* suffix can only be used with `==` or `!=` operators",
271
+ span_start=span_start,
272
+ span_end=tokenizer.position + 1,
273
+ )
274
+ if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True):
275
+ tokenizer.raise_syntax_error(
276
+ "Local version label can only be used with `==` or `!=` operators",
277
+ span_start=span_start,
278
+ span_end=tokenizer.position,
279
+ )
280
+ tokenizer.consume("WS")
281
+ if not tokenizer.check("COMMA"):
282
+ break
283
+ parsed_specifiers += tokenizer.read().text
284
+ tokenizer.consume("WS")
285
+
286
+ return parsed_specifiers
287
+
288
+
289
+ # --------------------------------------------------------------------------------------
290
+ # Recursive descent parser for marker expression
291
+ # --------------------------------------------------------------------------------------
292
+ def parse_marker(source: str) -> MarkerList:
293
+ return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES))
294
+
295
+
296
+ def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList:
297
+ retval = _parse_marker(tokenizer)
298
+ tokenizer.expect("END", expected="end of marker expression")
299
+ return retval
300
+
301
+
302
+ def _parse_marker(tokenizer: Tokenizer) -> MarkerList:
303
+ """
304
+ marker = marker_atom (BOOLOP marker_atom)+
305
+ """
306
+ expression = [_parse_marker_atom(tokenizer)]
307
+ while tokenizer.check("BOOLOP"):
308
+ token = tokenizer.read()
309
+ expr_right = _parse_marker_atom(tokenizer)
310
+ expression.extend((token.text, expr_right))
311
+ return expression
312
+
313
+
314
+ def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom:
315
+ """
316
+ marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS?
317
+ | WS? marker_item WS?
318
+ """
319
+
320
+ tokenizer.consume("WS")
321
+ if tokenizer.check("LEFT_PARENTHESIS", peek=True):
322
+ with tokenizer.enclosing_tokens(
323
+ "LEFT_PARENTHESIS",
324
+ "RIGHT_PARENTHESIS",
325
+ around="marker expression",
326
+ ):
327
+ tokenizer.consume("WS")
328
+ marker: MarkerAtom = _parse_marker(tokenizer)
329
+ tokenizer.consume("WS")
330
+ else:
331
+ marker = _parse_marker_item(tokenizer)
332
+ tokenizer.consume("WS")
333
+ return marker
334
+
335
+
336
+ def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem:
337
+ """
338
+ marker_item = WS? marker_var WS? marker_op WS? marker_var WS?
339
+ """
340
+ tokenizer.consume("WS")
341
+ marker_var_left = _parse_marker_var(tokenizer)
342
+ tokenizer.consume("WS")
343
+ marker_op = _parse_marker_op(tokenizer)
344
+ tokenizer.consume("WS")
345
+ marker_var_right = _parse_marker_var(tokenizer)
346
+ tokenizer.consume("WS")
347
+ return (marker_var_left, marker_op, marker_var_right)
348
+
349
+
350
+ def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503
351
+ """
352
+ marker_var = VARIABLE | QUOTED_STRING
353
+ """
354
+ if tokenizer.check("VARIABLE"):
355
+ return process_env_var(tokenizer.read().text.replace(".", "_"))
356
+ elif tokenizer.check("QUOTED_STRING"):
357
+ return process_python_str(tokenizer.read().text)
358
+ else:
359
+ tokenizer.raise_syntax_error(
360
+ message="Expected a marker variable or quoted string"
361
+ )
362
+
363
+
364
+ def process_env_var(env_var: str) -> Variable:
365
+ if env_var in ("platform_python_implementation", "python_implementation"):
366
+ return Variable("platform_python_implementation")
367
+ else:
368
+ return Variable(env_var)
369
+
370
+
371
+ def process_python_str(python_str: str) -> Value:
372
+ value = ast.literal_eval(python_str)
373
+ return Value(str(value))
374
+
375
+
376
+ def _parse_marker_op(tokenizer: Tokenizer) -> Op:
377
+ """
378
+ marker_op = IN | NOT IN | OP
379
+ """
380
+ if tokenizer.check("IN"):
381
+ tokenizer.read()
382
+ return Op("in")
383
+ elif tokenizer.check("NOT"):
384
+ tokenizer.read()
385
+ tokenizer.expect("WS", expected="whitespace after 'not'")
386
+ tokenizer.expect("IN", expected="'in' after 'not'")
387
+ return Op("not in")
388
+ elif tokenizer.check("OP"):
389
+ return Op(tokenizer.read().text)
390
+ else:
391
+ return tokenizer.raise_syntax_error(
392
+ "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in"
393
+ )
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/dependency_groups.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from collections.abc import Mapping, Sequence
5
+
6
+ from .errors import _ErrorCollector
7
+ from .requirements import Requirement
8
+
9
+ __all__ = [
10
+ "CyclicDependencyGroup",
11
+ "DependencyGroupInclude",
12
+ "DependencyGroupResolver",
13
+ "DuplicateGroupNames",
14
+ "InvalidDependencyGroupObject",
15
+ "resolve_dependency_groups",
16
+ ]
17
+
18
+
19
+ def __dir__() -> list[str]:
20
+ return __all__
21
+
22
+
23
+ # -----------
24
+ # Error Types
25
+ # -----------
26
+
27
+
28
+ class DuplicateGroupNames(ValueError):
29
+ """
30
+ The same dependency groups were defined twice, with different non-normalized names.
31
+ """
32
+
33
+
34
+ class CyclicDependencyGroup(ValueError):
35
+ """
36
+ The dependency group includes form a cycle.
37
+ """
38
+
39
+ def __init__(self, requested_group: str, group: str, include_group: str) -> None:
40
+ self.requested_group = requested_group
41
+ self.group = group
42
+ self.include_group = include_group
43
+
44
+ if include_group == group:
45
+ reason = f"{group} includes itself"
46
+ else:
47
+ reason = f"{include_group} -> {group}, {group} -> {include_group}"
48
+ super().__init__(
49
+ "Cyclic dependency group include while resolving "
50
+ f"{requested_group}: {reason}"
51
+ )
52
+
53
+
54
+ # in the PEP 735 spec, the tables in dependency group lists were described as
55
+ # "Dependency Object Specifiers", but the only defined type of object was a
56
+ # "Dependency Group Include" -- hence the naming of this error as "Object"
57
+ class InvalidDependencyGroupObject(ValueError):
58
+ """
59
+ A member of a dependency group was identified as a dict, but was not in a valid
60
+ format.
61
+ """
62
+
63
+
64
+ # ------------------------
65
+ # Object Model & Interface
66
+ # ------------------------
67
+
68
+
69
+ class DependencyGroupInclude:
70
+ __slots__ = ("include_group",)
71
+
72
+ def __init__(self, include_group: str) -> None:
73
+ """
74
+ Initialize a DependencyGroupInclude.
75
+
76
+ :param include_group: The name of the group referred to by this include.
77
+ """
78
+ self.include_group = include_group
79
+
80
+ def __repr__(self) -> str:
81
+ return f"{self.__class__.__name__}({self.include_group!r})"
82
+
83
+
84
+ class DependencyGroupResolver:
85
+ """
86
+ A resolver for Dependency Group data.
87
+
88
+ This class handles caching, name normalization, cycle detection, and other
89
+ parsing requirements. There are only two public methods for exploring the data:
90
+ ``lookup()`` and ``resolve()``.
91
+
92
+ :param dependency_groups: A mapping, as provided via pyproject
93
+ ``[dependency-groups]``.
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
99
+ ) -> None:
100
+ errors = _ErrorCollector()
101
+
102
+ self.dependency_groups = _normalize_group_names(dependency_groups, errors)
103
+
104
+ # a map of group names to parsed data
105
+ self._parsed_groups: dict[
106
+ str, tuple[Requirement | DependencyGroupInclude, ...]
107
+ ] = {}
108
+ # a map of group names to their ancestors, used for cycle detection
109
+ self._include_graph_ancestors: dict[str, tuple[str, ...]] = {}
110
+ # a cache of completed resolutions to Requirement lists
111
+ self._resolve_cache: dict[str, tuple[Requirement, ...]] = {}
112
+
113
+ errors.finalize("[dependency-groups] data was invalid")
114
+
115
+ def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]:
116
+ """
117
+ Lookup a group name, returning the parsed dependency data for that group.
118
+ This will not resolve includes.
119
+
120
+ :param group: the name of the group to lookup
121
+ """
122
+ group = _normalize_name(group)
123
+
124
+ with _ErrorCollector().on_exit(
125
+ f"[dependency-groups] data for {group!r} was malformed"
126
+ ) as errors:
127
+ return self._parse_group(group, errors)
128
+
129
+ def resolve(self, group: str) -> tuple[Requirement, ...]:
130
+ """
131
+ Resolve a dependency group to a list of requirements.
132
+
133
+ :param group: the name of the group to resolve
134
+ """
135
+ group = _normalize_name(group)
136
+
137
+ with _ErrorCollector().on_exit(
138
+ f"[dependency-groups] data for {group!r} was malformed"
139
+ ) as errors:
140
+ return self._resolve(group, group, errors)
141
+
142
+ def _resolve(
143
+ self, group: str, requested_group: str, errors: _ErrorCollector
144
+ ) -> tuple[Requirement, ...]:
145
+ """
146
+ This is a helper for cached resolution to strings. It preserves the name of the
147
+ group which the user initially requested in order to present a clearer error in
148
+ the event that a cycle is detected.
149
+
150
+ :param group: The normalized name of the group to resolve.
151
+ :param requested_group: The group which was used in the original, user-facing
152
+ request.
153
+ """
154
+ if group in self._resolve_cache:
155
+ return self._resolve_cache[group]
156
+
157
+ parsed = self._parse_group(group, errors)
158
+
159
+ resolved_group = []
160
+
161
+ for item in parsed:
162
+ if isinstance(item, Requirement):
163
+ resolved_group.append(item)
164
+ elif isinstance(item, DependencyGroupInclude):
165
+ include_group = _normalize_name(item.include_group)
166
+
167
+ # if a group is cyclic, record the error
168
+ # otherwise, follow the include_group reference
169
+ #
170
+ # this allows us to examine all includes in a group, even in the
171
+ # presence of errors
172
+ if include_group in self._include_graph_ancestors.get(group, ()):
173
+ errors.error(
174
+ CyclicDependencyGroup(
175
+ requested_group, group, item.include_group
176
+ )
177
+ )
178
+ else:
179
+ self._include_graph_ancestors[include_group] = (
180
+ *self._include_graph_ancestors.get(group, ()),
181
+ group,
182
+ )
183
+ resolved_group.extend(
184
+ self._resolve(include_group, requested_group, errors)
185
+ )
186
+ else: # pragma: no cover
187
+ raise NotImplementedError(
188
+ f"Invalid dependency group item after parse: {item}"
189
+ )
190
+
191
+ # in the event that errors were detected, present the group as empty and do not
192
+ # cache the result
193
+ # this ensures that repeated access to a cyclic group will raise multiple errors
194
+ if errors.errors:
195
+ return ()
196
+
197
+ self._resolve_cache[group] = tuple(resolved_group)
198
+ return self._resolve_cache[group]
199
+
200
+ def _parse_group(
201
+ self, group: str, errors: _ErrorCollector
202
+ ) -> tuple[Requirement | DependencyGroupInclude, ...]:
203
+ # short circuit -- never do the work twice
204
+ if group in self._parsed_groups:
205
+ return self._parsed_groups[group]
206
+
207
+ if group not in self.dependency_groups:
208
+ errors.error(LookupError(f"Dependency group '{group}' not found"))
209
+ return ()
210
+
211
+ raw_group = self.dependency_groups[group]
212
+ if isinstance(raw_group, str):
213
+ errors.error(
214
+ TypeError(
215
+ f"Dependency group {group!r} contained a string rather than a list."
216
+ )
217
+ )
218
+ return ()
219
+
220
+ if not isinstance(raw_group, Sequence):
221
+ errors.error(
222
+ TypeError(f"Dependency group {group!r} is not a sequence type.")
223
+ )
224
+ return ()
225
+
226
+ elements: list[Requirement | DependencyGroupInclude] = []
227
+ for item in raw_group:
228
+ if isinstance(item, str):
229
+ # packaging.requirements.Requirement parsing ensures that this is a
230
+ # valid PEP 508 Dependency Specifier
231
+ # raises InvalidRequirement on failure
232
+ elements.append(Requirement(item))
233
+ elif isinstance(item, Mapping):
234
+ if tuple(item.keys()) != ("include-group",):
235
+ errors.error(
236
+ InvalidDependencyGroupObject(
237
+ f"Invalid dependency group item: {item!r}"
238
+ )
239
+ )
240
+ else:
241
+ include_group = item["include-group"]
242
+ elements.append(DependencyGroupInclude(include_group=include_group))
243
+ else:
244
+ errors.error(TypeError(f"Invalid dependency group item: {item!r}"))
245
+
246
+ self._parsed_groups[group] = tuple(elements)
247
+ return self._parsed_groups[group]
248
+
249
+
250
+ # --------------------
251
+ # Functional Interface
252
+ # --------------------
253
+
254
+
255
+ def resolve_dependency_groups(
256
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], /, *groups: str
257
+ ) -> tuple[str, ...]:
258
+ """
259
+ Resolve a dependency group to a tuple of requirements, as strings.
260
+
261
+ :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table
262
+ from ``pyproject.toml``
263
+ :param groups: the name of the group(s) to resolve
264
+ """
265
+ resolver = DependencyGroupResolver(dependency_groups)
266
+ return tuple(str(r) for group in groups for r in resolver.resolve(group))
267
+
268
+
269
+ # ----------------
270
+ # internal helpers
271
+ # ----------------
272
+
273
+
274
+ _NORMALIZE_PATTERN = re.compile(r"[-_.]+")
275
+
276
+
277
+ def _normalize_name(name: str) -> str:
278
+ return _NORMALIZE_PATTERN.sub("-", name).lower()
279
+
280
+
281
+ def _normalize_group_names(
282
+ dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]],
283
+ errors: _ErrorCollector,
284
+ ) -> dict[str, Sequence[str | Mapping[str, str]]]:
285
+ original_names: dict[str, list[str]] = {}
286
+ normalized_groups: dict[str, Sequence[str | Mapping[str, str]]] = {}
287
+
288
+ for group_name, value in dependency_groups.items():
289
+ normed_group_name = _normalize_name(group_name)
290
+ original_names.setdefault(normed_group_name, []).append(group_name)
291
+ normalized_groups[normed_group_name] = value
292
+
293
+ for normed_name, names in original_names.items():
294
+ if len(names) > 1:
295
+ errors.error(
296
+ DuplicateGroupNames(
297
+ "Duplicate dependency group names: "
298
+ f"{normed_name} ({', '.join(names)})"
299
+ )
300
+ )
301
+
302
+ return normalized_groups
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/pylock.py ADDED
@@ -0,0 +1,905 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import logging
5
+ import re
6
+ from collections.abc import Mapping, Sequence
7
+ from dataclasses import dataclass
8
+ from datetime import datetime
9
+ from typing import (
10
+ TYPE_CHECKING,
11
+ Any,
12
+ Callable,
13
+ Protocol,
14
+ TypeVar,
15
+ cast,
16
+ )
17
+ from urllib.parse import urlparse
18
+
19
+ from .markers import Environment, Marker, default_environment
20
+ from .specifiers import SpecifierSet
21
+ from .tags import create_compatible_tags_selector, sys_tags
22
+ from .utils import (
23
+ NormalizedName,
24
+ is_normalized_name,
25
+ parse_sdist_filename,
26
+ parse_wheel_filename,
27
+ )
28
+ from .version import Version
29
+
30
+ if TYPE_CHECKING: # pragma: no cover
31
+ from collections.abc import Collection, Iterator
32
+ from pathlib import Path
33
+
34
+ from typing_extensions import Self
35
+
36
+ from .tags import Tag
37
+
38
+ _logger = logging.getLogger(__name__)
39
+
40
+ __all__ = [
41
+ "Package",
42
+ "PackageArchive",
43
+ "PackageDirectory",
44
+ "PackageSdist",
45
+ "PackageVcs",
46
+ "PackageWheel",
47
+ "Pylock",
48
+ "PylockUnsupportedVersionError",
49
+ "PylockValidationError",
50
+ "is_valid_pylock_path",
51
+ ]
52
+
53
+
54
+ def __dir__() -> list[str]:
55
+ return __all__
56
+
57
+
58
+ _T = TypeVar("_T")
59
+ _T2 = TypeVar("_T2")
60
+
61
+
62
+ class _FromMappingProtocol(Protocol): # pragma: no cover
63
+ @classmethod
64
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
65
+
66
+
67
+ _FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
68
+
69
+
70
+ _PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$")
71
+
72
+
73
+ def is_valid_pylock_path(path: Path) -> bool:
74
+ """Check if the given path is a valid pylock file path."""
75
+ return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name))
76
+
77
+
78
+ def _toml_key(key: str) -> str:
79
+ return key.replace("_", "-")
80
+
81
+
82
+ def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401
83
+ if isinstance(value, (Version, Marker, SpecifierSet)):
84
+ return str(value)
85
+ if isinstance(value, Sequence) and key == "environments":
86
+ return [str(v) for v in value]
87
+ return value
88
+
89
+
90
+ def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
91
+ return {
92
+ _toml_key(key): _toml_value(key, value)
93
+ for key, value in data
94
+ if value is not None
95
+ }
96
+
97
+
98
+ def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
99
+ """Get a value from the dictionary and verify it's the expected type."""
100
+ if (value := d.get(key)) is None:
101
+ return None
102
+ if not isinstance(value, expected_type):
103
+ raise PylockValidationError(
104
+ f"Unexpected type {type(value).__name__} "
105
+ f"(expected {expected_type.__name__})",
106
+ context=key,
107
+ )
108
+ return value
109
+
110
+
111
+ def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
112
+ """Get a required value from the dictionary and verify it's the expected type."""
113
+ if (value := _get(d, expected_type, key)) is None:
114
+ raise _PylockRequiredKeyError(key)
115
+ return value
116
+
117
+
118
+ def _get_sequence(
119
+ d: Mapping[str, Any], expected_item_type: type[_T], key: str
120
+ ) -> Sequence[_T] | None:
121
+ """Get a list value from the dictionary and verify it's the expected items type."""
122
+ if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract]
123
+ return None
124
+ if isinstance(value, (str, bytes)):
125
+ # special case: str and bytes are Sequences, but we want to reject it
126
+ raise PylockValidationError(
127
+ f"Unexpected type {type(value).__name__} (expected Sequence)",
128
+ context=key,
129
+ )
130
+ for i, item in enumerate(value):
131
+ if not isinstance(item, expected_item_type):
132
+ raise PylockValidationError(
133
+ f"Unexpected type {type(item).__name__} "
134
+ f"(expected {expected_item_type.__name__})",
135
+ context=f"{key}[{i}]",
136
+ )
137
+ return value
138
+
139
+
140
+ def _get_as(
141
+ d: Mapping[str, Any],
142
+ expected_type: type[_T],
143
+ target_type: Callable[[_T], _T2],
144
+ key: str,
145
+ ) -> _T2 | None:
146
+ """Get a value from the dictionary, verify it's the expected type,
147
+ and convert to the target type.
148
+
149
+ This assumes the target_type constructor accepts the value.
150
+ """
151
+ if (value := _get(d, expected_type, key)) is None:
152
+ return None
153
+ try:
154
+ return target_type(value)
155
+ except Exception as e:
156
+ raise PylockValidationError(e, context=key) from e
157
+
158
+
159
+ def _get_required_as(
160
+ d: Mapping[str, Any],
161
+ expected_type: type[_T],
162
+ target_type: Callable[[_T], _T2],
163
+ key: str,
164
+ ) -> _T2:
165
+ """Get a required value from the dict, verify it's the expected type,
166
+ and convert to the target type."""
167
+ if (value := _get_as(d, expected_type, target_type, key)) is None:
168
+ raise _PylockRequiredKeyError(key)
169
+ return value
170
+
171
+
172
+ def _get_sequence_as(
173
+ d: Mapping[str, Any],
174
+ expected_item_type: type[_T],
175
+ target_item_type: Callable[[_T], _T2],
176
+ key: str,
177
+ ) -> list[_T2] | None:
178
+ """Get list value from dictionary and verify expected items type."""
179
+ if (value := _get_sequence(d, expected_item_type, key)) is None:
180
+ return None
181
+ result = []
182
+ try:
183
+ for item in value:
184
+ typed_item = target_item_type(item)
185
+ result.append(typed_item)
186
+ except Exception as e:
187
+ raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
188
+ return result
189
+
190
+
191
+ def _get_object(
192
+ d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
193
+ ) -> _FromMappingProtocolT | None:
194
+ """Get a dictionary value from the dictionary and convert it to a dataclass."""
195
+ if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract]
196
+ return None
197
+ try:
198
+ return target_type._from_dict(value)
199
+ except Exception as e:
200
+ raise PylockValidationError(e, context=key) from e
201
+
202
+
203
+ def _get_sequence_of_objects(
204
+ d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
205
+ ) -> list[_FromMappingProtocolT] | None:
206
+ """Get a list value from the dictionary and convert its items to a dataclass."""
207
+ if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract]
208
+ return None
209
+ result: list[_FromMappingProtocolT] = []
210
+ try:
211
+ for item in value:
212
+ typed_item = target_item_type._from_dict(item)
213
+ result.append(typed_item)
214
+ except Exception as e:
215
+ raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
216
+ return result
217
+
218
+
219
+ def _get_required_sequence_of_objects(
220
+ d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
221
+ ) -> Sequence[_FromMappingProtocolT]:
222
+ """Get a required list value from the dictionary and convert its items to a
223
+ dataclass."""
224
+ if (result := _get_sequence_of_objects(d, target_item_type, key)) is None:
225
+ raise _PylockRequiredKeyError(key)
226
+ return result
227
+
228
+
229
+ def _validate_normalized_name(name: str) -> NormalizedName:
230
+ """Validate that a string is a NormalizedName."""
231
+ if not is_normalized_name(name):
232
+ raise PylockValidationError(f"Name {name!r} is not normalized")
233
+ return NormalizedName(name)
234
+
235
+
236
+ def _validate_path_url(path: str | None, url: str | None) -> None:
237
+ if not path and not url:
238
+ raise PylockValidationError("path or url must be provided")
239
+
240
+
241
+ def _path_name(path: str | None) -> str | None:
242
+ if not path:
243
+ return None
244
+ # If the path is relative it MAY use POSIX-style path separators explicitly
245
+ # for portability
246
+ if "/" in path:
247
+ return path.rsplit("/", 1)[-1]
248
+ elif "\\" in path:
249
+ return path.rsplit("\\", 1)[-1]
250
+ else:
251
+ return path
252
+
253
+
254
+ def _url_name(url: str | None) -> str | None:
255
+ if not url:
256
+ return None
257
+ url_path = urlparse(url).path
258
+ return url_path.rsplit("/", 1)[-1]
259
+
260
+
261
+ def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]:
262
+ if not hashes:
263
+ raise PylockValidationError("At least one hash must be provided")
264
+ if not all(isinstance(hash_val, str) for hash_val in hashes.values()):
265
+ raise PylockValidationError("Hash values must be strings")
266
+ return hashes
267
+
268
+
269
+ class PylockValidationError(Exception):
270
+ """Raised when when input data is not spec-compliant."""
271
+
272
+ context: str | None = None
273
+ message: str
274
+
275
+ def __init__(
276
+ self,
277
+ cause: str | Exception,
278
+ *,
279
+ context: str | None = None,
280
+ ) -> None:
281
+ if isinstance(cause, PylockValidationError):
282
+ if cause.context:
283
+ self.context = (
284
+ f"{context}.{cause.context}" if context else cause.context
285
+ )
286
+ else:
287
+ self.context = context
288
+ self.message = cause.message
289
+ else:
290
+ self.context = context
291
+ self.message = str(cause)
292
+
293
+ def __str__(self) -> str:
294
+ if self.context:
295
+ return f"{self.message} in {self.context!r}"
296
+ return self.message
297
+
298
+
299
+ class _PylockRequiredKeyError(PylockValidationError):
300
+ def __init__(self, key: str) -> None:
301
+ super().__init__("Missing required value", context=key)
302
+
303
+
304
+ class PylockUnsupportedVersionError(PylockValidationError):
305
+ """Raised when encountering an unsupported `lock_version`."""
306
+
307
+
308
+ class PylockSelectError(Exception):
309
+ """Base exception for errors raised by :meth:`Pylock.select`."""
310
+
311
+
312
+ @dataclass(frozen=True, init=False)
313
+ class PackageVcs:
314
+ type: str
315
+ url: str | None = None
316
+ path: str | None = None
317
+ requested_revision: str | None = None
318
+ commit_id: str # type: ignore[misc]
319
+ subdirectory: str | None = None
320
+
321
+ def __init__(
322
+ self,
323
+ *,
324
+ type: str,
325
+ url: str | None = None,
326
+ path: str | None = None,
327
+ requested_revision: str | None = None,
328
+ commit_id: str,
329
+ subdirectory: str | None = None,
330
+ ) -> None:
331
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
332
+ object.__setattr__(self, "type", type)
333
+ object.__setattr__(self, "url", url)
334
+ object.__setattr__(self, "path", path)
335
+ object.__setattr__(self, "requested_revision", requested_revision)
336
+ object.__setattr__(self, "commit_id", commit_id)
337
+ object.__setattr__(self, "subdirectory", subdirectory)
338
+
339
+ @classmethod
340
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
341
+ package_vcs = cls(
342
+ type=_get_required(d, str, "type"),
343
+ url=_get(d, str, "url"),
344
+ path=_get(d, str, "path"),
345
+ requested_revision=_get(d, str, "requested-revision"),
346
+ commit_id=_get_required(d, str, "commit-id"),
347
+ subdirectory=_get(d, str, "subdirectory"),
348
+ )
349
+ _validate_path_url(package_vcs.path, package_vcs.url)
350
+ return package_vcs
351
+
352
+
353
+ @dataclass(frozen=True, init=False)
354
+ class PackageDirectory:
355
+ path: str
356
+ editable: bool | None = None
357
+ subdirectory: str | None = None
358
+
359
+ def __init__(
360
+ self,
361
+ *,
362
+ path: str,
363
+ editable: bool | None = None,
364
+ subdirectory: str | None = None,
365
+ ) -> None:
366
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
367
+ object.__setattr__(self, "path", path)
368
+ object.__setattr__(self, "editable", editable)
369
+ object.__setattr__(self, "subdirectory", subdirectory)
370
+
371
+ @classmethod
372
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
373
+ return cls(
374
+ path=_get_required(d, str, "path"),
375
+ editable=_get(d, bool, "editable"),
376
+ subdirectory=_get(d, str, "subdirectory"),
377
+ )
378
+
379
+
380
+ @dataclass(frozen=True, init=False)
381
+ class PackageArchive:
382
+ url: str | None = None
383
+ path: str | None = None
384
+ size: int | None = None
385
+ upload_time: datetime | None = None
386
+ hashes: Mapping[str, str] # type: ignore[misc]
387
+ subdirectory: str | None = None
388
+
389
+ def __init__(
390
+ self,
391
+ *,
392
+ url: str | None = None,
393
+ path: str | None = None,
394
+ size: int | None = None,
395
+ upload_time: datetime | None = None,
396
+ hashes: Mapping[str, str],
397
+ subdirectory: str | None = None,
398
+ ) -> None:
399
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
400
+ object.__setattr__(self, "url", url)
401
+ object.__setattr__(self, "path", path)
402
+ object.__setattr__(self, "size", size)
403
+ object.__setattr__(self, "upload_time", upload_time)
404
+ object.__setattr__(self, "hashes", hashes)
405
+ object.__setattr__(self, "subdirectory", subdirectory)
406
+
407
+ @classmethod
408
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
409
+ package_archive = cls(
410
+ url=_get(d, str, "url"),
411
+ path=_get(d, str, "path"),
412
+ size=_get(d, int, "size"),
413
+ upload_time=_get(d, datetime, "upload-time"),
414
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
415
+ subdirectory=_get(d, str, "subdirectory"),
416
+ )
417
+ _validate_path_url(package_archive.path, package_archive.url)
418
+ return package_archive
419
+
420
+
421
+ @dataclass(frozen=True, init=False)
422
+ class PackageSdist:
423
+ name: str | None = None
424
+ upload_time: datetime | None = None
425
+ url: str | None = None
426
+ path: str | None = None
427
+ size: int | None = None
428
+ hashes: Mapping[str, str] # type: ignore[misc]
429
+
430
+ def __init__(
431
+ self,
432
+ *,
433
+ name: str | None = None,
434
+ upload_time: datetime | None = None,
435
+ url: str | None = None,
436
+ path: str | None = None,
437
+ size: int | None = None,
438
+ hashes: Mapping[str, str],
439
+ ) -> None:
440
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
441
+ object.__setattr__(self, "name", name)
442
+ object.__setattr__(self, "upload_time", upload_time)
443
+ object.__setattr__(self, "url", url)
444
+ object.__setattr__(self, "path", path)
445
+ object.__setattr__(self, "size", size)
446
+ object.__setattr__(self, "hashes", hashes)
447
+
448
+ @classmethod
449
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
450
+ package_sdist = cls(
451
+ name=_get(d, str, "name"),
452
+ upload_time=_get(d, datetime, "upload-time"),
453
+ url=_get(d, str, "url"),
454
+ path=_get(d, str, "path"),
455
+ size=_get(d, int, "size"),
456
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
457
+ )
458
+ _validate_path_url(package_sdist.path, package_sdist.url)
459
+ return package_sdist
460
+
461
+ @property
462
+ def filename(self) -> str:
463
+ """Get the filename of the sdist."""
464
+ filename = self.name or _path_name(self.path) or _url_name(self.url)
465
+ if not filename:
466
+ raise PylockValidationError("Cannot determine sdist filename")
467
+ return filename
468
+
469
+
470
+ @dataclass(frozen=True, init=False)
471
+ class PackageWheel:
472
+ name: str | None = None
473
+ upload_time: datetime | None = None
474
+ url: str | None = None
475
+ path: str | None = None
476
+ size: int | None = None
477
+ hashes: Mapping[str, str] # type: ignore[misc]
478
+
479
+ def __init__(
480
+ self,
481
+ *,
482
+ name: str | None = None,
483
+ upload_time: datetime | None = None,
484
+ url: str | None = None,
485
+ path: str | None = None,
486
+ size: int | None = None,
487
+ hashes: Mapping[str, str],
488
+ ) -> None:
489
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
490
+ object.__setattr__(self, "name", name)
491
+ object.__setattr__(self, "upload_time", upload_time)
492
+ object.__setattr__(self, "url", url)
493
+ object.__setattr__(self, "path", path)
494
+ object.__setattr__(self, "size", size)
495
+ object.__setattr__(self, "hashes", hashes)
496
+
497
+ @classmethod
498
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
499
+ package_wheel = cls(
500
+ name=_get(d, str, "name"),
501
+ upload_time=_get(d, datetime, "upload-time"),
502
+ url=_get(d, str, "url"),
503
+ path=_get(d, str, "path"),
504
+ size=_get(d, int, "size"),
505
+ hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
506
+ )
507
+ _validate_path_url(package_wheel.path, package_wheel.url)
508
+ return package_wheel
509
+
510
+ @property
511
+ def filename(self) -> str:
512
+ """Get the filename of the wheel."""
513
+ filename = self.name or _path_name(self.path) or _url_name(self.url)
514
+ if not filename:
515
+ raise PylockValidationError("Cannot determine wheel filename")
516
+ return filename
517
+
518
+
519
+ @dataclass(frozen=True, init=False)
520
+ class Package:
521
+ name: NormalizedName
522
+ version: Version | None = None
523
+ marker: Marker | None = None
524
+ requires_python: SpecifierSet | None = None
525
+ dependencies: Sequence[Mapping[str, Any]] | None = None
526
+ vcs: PackageVcs | None = None
527
+ directory: PackageDirectory | None = None
528
+ archive: PackageArchive | None = None
529
+ index: str | None = None
530
+ sdist: PackageSdist | None = None
531
+ wheels: Sequence[PackageWheel] | None = None
532
+ attestation_identities: Sequence[Mapping[str, Any]] | None = None
533
+ tool: Mapping[str, Any] | None = None
534
+
535
+ def __init__(
536
+ self,
537
+ *,
538
+ name: NormalizedName,
539
+ version: Version | None = None,
540
+ marker: Marker | None = None,
541
+ requires_python: SpecifierSet | None = None,
542
+ dependencies: Sequence[Mapping[str, Any]] | None = None,
543
+ vcs: PackageVcs | None = None,
544
+ directory: PackageDirectory | None = None,
545
+ archive: PackageArchive | None = None,
546
+ index: str | None = None,
547
+ sdist: PackageSdist | None = None,
548
+ wheels: Sequence[PackageWheel] | None = None,
549
+ attestation_identities: Sequence[Mapping[str, Any]] | None = None,
550
+ tool: Mapping[str, Any] | None = None,
551
+ ) -> None:
552
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
553
+ object.__setattr__(self, "name", name)
554
+ object.__setattr__(self, "version", version)
555
+ object.__setattr__(self, "marker", marker)
556
+ object.__setattr__(self, "requires_python", requires_python)
557
+ object.__setattr__(self, "dependencies", dependencies)
558
+ object.__setattr__(self, "vcs", vcs)
559
+ object.__setattr__(self, "directory", directory)
560
+ object.__setattr__(self, "archive", archive)
561
+ object.__setattr__(self, "index", index)
562
+ object.__setattr__(self, "sdist", sdist)
563
+ object.__setattr__(self, "wheels", wheels)
564
+ object.__setattr__(self, "attestation_identities", attestation_identities)
565
+ object.__setattr__(self, "tool", tool)
566
+
567
+ @classmethod
568
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
569
+ package = cls(
570
+ name=_get_required_as(d, str, _validate_normalized_name, "name"),
571
+ version=_get_as(d, str, Version, "version"),
572
+ requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
573
+ dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract]
574
+ marker=_get_as(d, str, Marker, "marker"),
575
+ vcs=_get_object(d, PackageVcs, "vcs"),
576
+ directory=_get_object(d, PackageDirectory, "directory"),
577
+ archive=_get_object(d, PackageArchive, "archive"),
578
+ index=_get(d, str, "index"),
579
+ sdist=_get_object(d, PackageSdist, "sdist"),
580
+ wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"),
581
+ attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract]
582
+ tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
583
+ )
584
+ distributions = bool(package.sdist) + len(package.wheels or [])
585
+ direct_urls = (
586
+ bool(package.vcs) + bool(package.directory) + bool(package.archive)
587
+ )
588
+ if distributions > 0 and direct_urls > 0:
589
+ raise PylockValidationError(
590
+ "None of vcs, directory, archive must be set if sdist or wheels are set"
591
+ )
592
+ if distributions == 0 and direct_urls != 1:
593
+ raise PylockValidationError(
594
+ "Exactly one of vcs, directory, archive must be set "
595
+ "if sdist and wheels are not set"
596
+ )
597
+ for i, wheel in enumerate(package.wheels or []):
598
+ try:
599
+ (name, version, _, _) = parse_wheel_filename(wheel.filename)
600
+ except Exception as e:
601
+ raise PylockValidationError(
602
+ f"Invalid wheel filename {wheel.filename!r}",
603
+ context=f"wheels[{i}]",
604
+ ) from e
605
+ if name != package.name:
606
+ raise PylockValidationError(
607
+ f"Name in {wheel.filename!r} is not consistent with "
608
+ f"package name {package.name!r}",
609
+ context=f"wheels[{i}]",
610
+ )
611
+ if package.version and version != package.version:
612
+ raise PylockValidationError(
613
+ f"Version in {wheel.filename!r} is not consistent with "
614
+ f"package version {str(package.version)!r}",
615
+ context=f"wheels[{i}]",
616
+ )
617
+ if package.sdist:
618
+ try:
619
+ name, version = parse_sdist_filename(package.sdist.filename)
620
+ except Exception as e:
621
+ raise PylockValidationError(
622
+ f"Invalid sdist filename {package.sdist.filename!r}",
623
+ context="sdist",
624
+ ) from e
625
+ if name != package.name:
626
+ raise PylockValidationError(
627
+ f"Name in {package.sdist.filename!r} is not consistent with "
628
+ f"package name {package.name!r}",
629
+ context="sdist",
630
+ )
631
+ if package.version and version != package.version:
632
+ raise PylockValidationError(
633
+ f"Version in {package.sdist.filename!r} is not consistent with "
634
+ f"package version {str(package.version)!r}",
635
+ context="sdist",
636
+ )
637
+ try:
638
+ for i, attestation_identity in enumerate( # noqa: B007
639
+ package.attestation_identities or []
640
+ ):
641
+ _get_required(attestation_identity, str, "kind")
642
+ except Exception as e:
643
+ raise PylockValidationError(
644
+ e, context=f"attestation-identities[{i}]"
645
+ ) from e
646
+ return package
647
+
648
+ @property
649
+ def is_direct(self) -> bool:
650
+ return not (self.sdist or self.wheels)
651
+
652
+
653
+ @dataclass(frozen=True, init=False)
654
+ class Pylock:
655
+ """A class representing a pylock file."""
656
+
657
+ lock_version: Version
658
+ environments: Sequence[Marker] | None = None
659
+ requires_python: SpecifierSet | None = None
660
+ extras: Sequence[NormalizedName] | None = None
661
+ dependency_groups: Sequence[str] | None = None
662
+ default_groups: Sequence[str] | None = None
663
+ created_by: str # type: ignore[misc]
664
+ packages: Sequence[Package] # type: ignore[misc]
665
+ tool: Mapping[str, Any] | None = None
666
+
667
+ def __init__(
668
+ self,
669
+ *,
670
+ lock_version: Version,
671
+ environments: Sequence[Marker] | None = None,
672
+ requires_python: SpecifierSet | None = None,
673
+ extras: Sequence[NormalizedName] | None = None,
674
+ dependency_groups: Sequence[str] | None = None,
675
+ default_groups: Sequence[str] | None = None,
676
+ created_by: str,
677
+ packages: Sequence[Package],
678
+ tool: Mapping[str, Any] | None = None,
679
+ ) -> None:
680
+ # In Python 3.10+ make dataclass kw_only=True and remove __init__
681
+ object.__setattr__(self, "lock_version", lock_version)
682
+ object.__setattr__(self, "environments", environments)
683
+ object.__setattr__(self, "requires_python", requires_python)
684
+ object.__setattr__(self, "extras", extras)
685
+ object.__setattr__(self, "dependency_groups", dependency_groups)
686
+ object.__setattr__(self, "default_groups", default_groups)
687
+ object.__setattr__(self, "created_by", created_by)
688
+ object.__setattr__(self, "packages", packages)
689
+ object.__setattr__(self, "tool", tool)
690
+
691
+ @classmethod
692
+ def _from_dict(cls, d: Mapping[str, Any]) -> Self:
693
+ pylock = cls(
694
+ lock_version=_get_required_as(d, str, Version, "lock-version"),
695
+ environments=_get_sequence_as(d, str, Marker, "environments"),
696
+ extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"),
697
+ dependency_groups=_get_sequence(d, str, "dependency-groups"),
698
+ default_groups=_get_sequence(d, str, "default-groups"),
699
+ created_by=_get_required(d, str, "created-by"),
700
+ requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
701
+ packages=_get_required_sequence_of_objects(d, Package, "packages"),
702
+ tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
703
+ )
704
+ if not Version("1") <= pylock.lock_version < Version("2"):
705
+ raise PylockUnsupportedVersionError(
706
+ f"pylock version {pylock.lock_version} is not supported"
707
+ )
708
+ if pylock.lock_version > Version("1.0"):
709
+ _logger.warning(
710
+ "pylock minor version %s is not supported", pylock.lock_version
711
+ )
712
+ return pylock
713
+
714
+ @classmethod
715
+ def from_dict(cls, d: Mapping[str, Any], /) -> Self:
716
+ """Create and validate a Pylock instance from a TOML dictionary.
717
+
718
+ Raises :class:`PylockValidationError` if the input data is not
719
+ spec-compliant.
720
+ """
721
+ return cls._from_dict(d)
722
+
723
+ def to_dict(self) -> Mapping[str, Any]:
724
+ """Convert the Pylock instance to a TOML dictionary."""
725
+ return dataclasses.asdict(self, dict_factory=_toml_dict_factory)
726
+
727
+ def validate(self) -> None:
728
+ """Validate the Pylock instance against the specification.
729
+
730
+ Raises :class:`PylockValidationError` otherwise."""
731
+ self.from_dict(self.to_dict())
732
+
733
+ def select(
734
+ self,
735
+ *,
736
+ environment: Environment | None = None,
737
+ tags: Sequence[Tag] | None = None,
738
+ extras: Collection[str] | None = None,
739
+ dependency_groups: Collection[str] | None = None,
740
+ ) -> Iterator[
741
+ tuple[
742
+ Package,
743
+ PackageVcs
744
+ | PackageDirectory
745
+ | PackageArchive
746
+ | PackageWheel
747
+ | PackageSdist,
748
+ ]
749
+ ]:
750
+ """Select what to install from the lock file.
751
+
752
+ The *environment* and *tags* parameters represent the environment being
753
+ selected for. If unspecified, ``packaging.markers.default_environment()`` and
754
+ ``packaging.tags.sys_tags()`` are used.
755
+
756
+ The *extras* parameter represents the extras to install.
757
+
758
+ The *dependency_groups* parameter represents the groups to install. If
759
+ unspecified, the default groups are used.
760
+
761
+ This method must be used on valid Pylock instances (i.e. one obtained
762
+ from :meth:`Pylock.from_dict` or if constructed manually, after calling
763
+ :meth:`Pylock.validate`).
764
+ """
765
+ compatible_tags_selector = create_compatible_tags_selector(tags or sys_tags())
766
+
767
+ # #. Gather the extras and dependency groups to install and set ``extras`` and
768
+ # ``dependency_groups`` for marker evaluation, respectively.
769
+ #
770
+ # #. ``extras`` SHOULD be set to the empty set by default.
771
+ # #. ``dependency_groups`` SHOULD be the set created from
772
+ # :ref:`pylock-default-groups` by default.
773
+ env = cast(
774
+ "dict[str, str | frozenset[str]]",
775
+ dict(
776
+ environment or {}, # Marker.evaluate will fill-up
777
+ extras=frozenset(extras or []),
778
+ dependency_groups=frozenset(
779
+ (self.default_groups or [])
780
+ if dependency_groups is None # to allow selecting no group
781
+ else dependency_groups
782
+ ),
783
+ ),
784
+ )
785
+ env_python_full_version = (
786
+ environment["python_full_version"]
787
+ if environment
788
+ else default_environment()["python_full_version"]
789
+ )
790
+
791
+ # #. Check if the metadata version specified by :ref:`pylock-lock-version` is
792
+ # supported; an error or warning MUST be raised as appropriate.
793
+ # Covered by lock.validate() which is a precondition for this method.
794
+
795
+ # #. If :ref:`pylock-requires-python` is specified, check that the environment
796
+ # being installed for meets the requirement; an error MUST be raised if it is
797
+ # not met.
798
+ if self.requires_python and not self.requires_python.contains(
799
+ env_python_full_version,
800
+ ):
801
+ raise PylockSelectError(
802
+ f"python_full_version {env_python_full_version!r} "
803
+ f"in provided environment does not satisfy the Python version "
804
+ f"requirement {str(self.requires_python)!r}"
805
+ )
806
+
807
+ # #. If :ref:`pylock-environments` is specified, check that at least one of the
808
+ # environment marker expressions is satisfied; an error MUST be raised if no
809
+ # expression is satisfied.
810
+ if self.environments:
811
+ for env_marker in self.environments:
812
+ if env_marker.evaluate(
813
+ cast("dict[str, str]", environment or {}), context="requirement"
814
+ ):
815
+ break
816
+ else:
817
+ raise PylockSelectError(
818
+ "Provided environment does not satisfy any of the "
819
+ "environments specified in the lock file"
820
+ )
821
+
822
+ # #. For each package listed in :ref:`pylock-packages`:
823
+ selected_packages_by_name: dict[str, tuple[int, Package]] = {}
824
+ for package_index, package in enumerate(self.packages):
825
+ # #. If :ref:`pylock-packages-marker` is specified, check if it is
826
+ # satisfied;if it isn't, skip to the next package.
827
+ if package.marker and not package.marker.evaluate(env, context="lock_file"):
828
+ continue
829
+
830
+ # #. If :ref:`pylock-packages-requires-python` is specified, check if it is
831
+ # satisfied; an error MUST be raised if it isn't.
832
+ if package.requires_python and not package.requires_python.contains(
833
+ env_python_full_version,
834
+ ):
835
+ raise PylockSelectError(
836
+ f"python_full_version {env_python_full_version!r} "
837
+ f"in provided environment does not satisfy the Python version "
838
+ f"requirement {str(package.requires_python)!r} for package "
839
+ f"{package.name!r} at packages[{package_index}]"
840
+ )
841
+
842
+ # #. Check that no other conflicting instance of the package has been slated
843
+ # to be installed; an error about the ambiguity MUST be raised otherwise.
844
+ if package.name in selected_packages_by_name:
845
+ raise PylockSelectError(
846
+ f"Multiple packages with the name {package.name!r} are "
847
+ f"selected at packages[{package_index}] and "
848
+ f"packages[{selected_packages_by_name[package.name][0]}]"
849
+ )
850
+
851
+ # #. Check that the source of the package is specified appropriately (i.e.
852
+ # there are no conflicting sources in the package entry);
853
+ # an error MUST be raised if any issues are found.
854
+ # Covered by lock.validate() which is a precondition for this method.
855
+
856
+ # #. Add the package to the set of packages to install.
857
+ selected_packages_by_name[package.name] = (package_index, package)
858
+
859
+ # #. For each package to be installed:
860
+ for package_index, package in selected_packages_by_name.values():
861
+ # - If :ref:`pylock-packages-vcs` is set:
862
+ if package.vcs is not None:
863
+ yield package, package.vcs
864
+
865
+ # - Else if :ref:`pylock-packages-directory` is set:
866
+ elif package.directory is not None:
867
+ yield package, package.directory
868
+
869
+ # - Else if :ref:`pylock-packages-archive` is set:
870
+ elif package.archive is not None:
871
+ yield package, package.archive
872
+
873
+ # - Else if there are entries for :ref:`pylock-packages-wheels`:
874
+ elif package.wheels:
875
+ # #. Look for the appropriate wheel file based on
876
+ # :ref:`pylock-packages-wheels-name`; if one is not found then move
877
+ # on to :ref:`pylock-packages-sdist` or an error MUST be raised about
878
+ # a lack of source for the project.
879
+ best_wheel = next(
880
+ compatible_tags_selector(
881
+ (wheel, parse_wheel_filename(wheel.filename)[-1])
882
+ for wheel in package.wheels
883
+ ),
884
+ None,
885
+ )
886
+ if best_wheel:
887
+ yield package, best_wheel
888
+ elif package.sdist is not None:
889
+ yield package, package.sdist
890
+ else:
891
+ raise PylockSelectError(
892
+ f"No wheel found matching the provided tags "
893
+ f"for package {package.name!r} "
894
+ f"at packages[{package_index}], "
895
+ f"and no sdist available as a fallback"
896
+ )
897
+
898
+ # - Else if no :ref:`pylock-packages-wheels` file is found or
899
+ # :ref:`pylock-packages-sdist` is solely set:
900
+ elif package.sdist is not None:
901
+ yield package, package.sdist
902
+
903
+ else:
904
+ # Covered by lock.validate() which is a precondition for this method.
905
+ raise NotImplementedError # pragma: no cover
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/packaging/utils.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file is dual licensed under the terms of the Apache License, Version
2
+ # 2.0, and the BSD License. See the LICENSE file in the root of this repository
3
+ # for complete details.
4
+
5
+ from __future__ import annotations
6
+
7
+ import re
8
+ from typing import NewType, Tuple, Union, cast
9
+
10
+ from .tags import Tag, UnsortedTagsError, parse_tag
11
+ from .version import InvalidVersion, Version, _TrimmedRelease
12
+
13
+ __all__ = [
14
+ "BuildTag",
15
+ "InvalidName",
16
+ "InvalidSdistFilename",
17
+ "InvalidWheelFilename",
18
+ "NormalizedName",
19
+ "canonicalize_name",
20
+ "canonicalize_version",
21
+ "is_normalized_name",
22
+ "parse_sdist_filename",
23
+ "parse_wheel_filename",
24
+ ]
25
+
26
+
27
+ def __dir__() -> list[str]:
28
+ return __all__
29
+
30
+
31
+ BuildTag = Union[Tuple[()], Tuple[int, str]]
32
+
33
+ NormalizedName = NewType("NormalizedName", str)
34
+ """
35
+ A :class:`typing.NewType` of :class:`str`, representing a normalized name.
36
+ """
37
+
38
+
39
+ class InvalidName(ValueError):
40
+ """
41
+ An invalid distribution name; users should refer to the packaging user guide.
42
+ """
43
+
44
+
45
+ class InvalidWheelFilename(ValueError):
46
+ """
47
+ An invalid wheel filename was found, users should refer to PEP 427.
48
+ """
49
+
50
+
51
+ class InvalidSdistFilename(ValueError):
52
+ """
53
+ An invalid sdist filename was found, users should refer to the packaging user guide.
54
+ """
55
+
56
+
57
+ # Core metadata spec for `Name`
58
+ _validate_regex = re.compile(
59
+ r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII
60
+ )
61
+ _normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]", re.ASCII)
62
+ # PEP 427: The build number must start with a digit.
63
+ _build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII)
64
+
65
+
66
+ def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:
67
+ """
68
+ This function takes a valid Python package or extra name, and returns the
69
+ normalized form of it.
70
+
71
+ The return type is typed as :class:`NormalizedName`. This allows type
72
+ checkers to help require that a string has passed through this function
73
+ before use.
74
+
75
+ If **validate** is true, then the function will check if **name** is a valid
76
+ distribution name before normalizing.
77
+
78
+ :param str name: The name to normalize.
79
+ :param bool validate: Check whether the name is a valid distribution name.
80
+ :raises InvalidName: If **validate** is true and the name is not an
81
+ acceptable distribution name.
82
+
83
+ >>> from packaging.utils import canonicalize_name
84
+ >>> canonicalize_name("Django")
85
+ 'django'
86
+ >>> canonicalize_name("oslo.concurrency")
87
+ 'oslo-concurrency'
88
+ >>> canonicalize_name("requests")
89
+ 'requests'
90
+ """
91
+ if validate and not _validate_regex.fullmatch(name):
92
+ raise InvalidName(f"name is invalid: {name!r}")
93
+ # Ensure all ``.`` and ``_`` are ``-``
94
+ # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503
95
+ # Much faster than re, and even faster than str.translate
96
+ value = name.lower().replace("_", "-").replace(".", "-")
97
+ # Condense repeats (faster than regex)
98
+ while "--" in value:
99
+ value = value.replace("--", "-")
100
+ return cast("NormalizedName", value)
101
+
102
+
103
+ def is_normalized_name(name: str) -> bool:
104
+ """
105
+ Check if a name is already normalized (i.e. :func:`canonicalize_name` would
106
+ roundtrip to the same value).
107
+
108
+ :param str name: The name to check.
109
+
110
+ >>> from packaging.utils import is_normalized_name
111
+ >>> is_normalized_name("requests")
112
+ True
113
+ >>> is_normalized_name("Django")
114
+ False
115
+ """
116
+ return _normalized_regex.fullmatch(name) is not None
117
+
118
+
119
+ def canonicalize_version(
120
+ version: Version | str, *, strip_trailing_zero: bool = True
121
+ ) -> str:
122
+ """Return a canonical form of a version as a string.
123
+
124
+ This function takes a string representing a package version (or a
125
+ :class:`~packaging.version.Version` instance), and returns the
126
+ normalized form of it. By default, it strips trailing zeros from
127
+ the release segment.
128
+
129
+ >>> from packaging.utils import canonicalize_version
130
+ >>> canonicalize_version('1.0.1')
131
+ '1.0.1'
132
+
133
+ Per PEP 625, versions may have multiple canonical forms, differing
134
+ only by trailing zeros.
135
+
136
+ >>> canonicalize_version('1.0.0')
137
+ '1'
138
+ >>> canonicalize_version('1.0.0', strip_trailing_zero=False)
139
+ '1.0.0'
140
+
141
+ Invalid versions are returned unaltered.
142
+
143
+ >>> canonicalize_version('foo bar baz')
144
+ 'foo bar baz'
145
+
146
+ >>> canonicalize_version('1.4.0.0.0')
147
+ '1.4'
148
+ """
149
+ if isinstance(version, str):
150
+ try:
151
+ version = Version(version)
152
+ except InvalidVersion:
153
+ return str(version)
154
+ return str(_TrimmedRelease(version) if strip_trailing_zero else version)
155
+
156
+
157
+ def parse_wheel_filename(
158
+ filename: str,
159
+ *,
160
+ validate_order: bool = False,
161
+ ) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
162
+ """
163
+ This function takes the filename of a wheel file, and parses it,
164
+ returning a tuple of name, version, build number, and tags.
165
+
166
+ The name part of the tuple is normalized and typed as
167
+ :class:`NormalizedName`. The version portion is an instance of
168
+ :class:`~packaging.version.Version`. The build number is ``()`` if
169
+ there is no build number in the wheel filename, otherwise a
170
+ two-item tuple of an integer for the leading digits and
171
+ a string for the rest of the build number. The tags portion is a
172
+ frozen set of :class:`~packaging.tags.Tag` instances (as the tag
173
+ string format allows multiple tags to be combined into a single
174
+ string).
175
+
176
+ If **validate_order** is true, compressed tag set components are
177
+ checked to be in sorted order as required by PEP 425.
178
+
179
+ :param str filename: The name of the wheel file.
180
+ :param bool validate_order: Check whether compressed tag set components
181
+ are in sorted order.
182
+ :raises InvalidWheelFilename: If the filename in question
183
+ does not follow the :ref:`wheel specification
184
+ <pypug:binary-distribution-format>`.
185
+
186
+ >>> from packaging.utils import parse_wheel_filename
187
+ >>> from packaging.tags import Tag
188
+ >>> from packaging.version import Version
189
+ >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl")
190
+ >>> name
191
+ 'foo'
192
+ >>> ver == Version('1.0')
193
+ True
194
+ >>> tags == {Tag("py3", "none", "any")}
195
+ True
196
+ >>> not build
197
+ True
198
+
199
+ .. versionadded:: 26.1
200
+ The *validate_order* parameter.
201
+ """
202
+ if not filename.endswith(".whl"):
203
+ raise InvalidWheelFilename(
204
+ f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
205
+ )
206
+
207
+ filename = filename[:-4]
208
+ dashes = filename.count("-")
209
+ if dashes not in (4, 5):
210
+ raise InvalidWheelFilename(
211
+ f"Invalid wheel filename (wrong number of parts): {filename!r}"
212
+ )
213
+
214
+ parts = filename.split("-", dashes - 2)
215
+ name_part = parts[0]
216
+ # See PEP 427 for the rules on escaping the project name.
217
+ if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
218
+ raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
219
+ name = canonicalize_name(name_part)
220
+
221
+ try:
222
+ version = Version(parts[1])
223
+ except InvalidVersion as e:
224
+ raise InvalidWheelFilename(
225
+ f"Invalid wheel filename (invalid version): {filename!r}"
226
+ ) from e
227
+
228
+ if dashes == 5:
229
+ build_part = parts[2]
230
+ build_match = _build_tag_regex.match(build_part)
231
+ if build_match is None:
232
+ raise InvalidWheelFilename(
233
+ f"Invalid build number: {build_part} in {filename!r}"
234
+ )
235
+ build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2)))
236
+ else:
237
+ build = ()
238
+ tag_str = parts[-1]
239
+ try:
240
+ tags = parse_tag(tag_str, validate_order=validate_order)
241
+ except UnsortedTagsError:
242
+ raise InvalidWheelFilename(
243
+ f"Invalid wheel filename (compressed tag set components must be in "
244
+ f"sorted order per PEP 425): {filename!r}"
245
+ ) from None
246
+ return (name, version, build, tags)
247
+
248
+
249
+ def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
250
+ """
251
+ This function takes the filename of a sdist file (as specified
252
+ in the `Source distribution format`_ documentation), and parses
253
+ it, returning a tuple of the normalized name and version as
254
+ represented by an instance of :class:`~packaging.version.Version`.
255
+
256
+ :param str filename: The name of the sdist file.
257
+ :raises InvalidSdistFilename: If the filename does not end
258
+ with an sdist extension (``.zip`` or ``.tar.gz``), or if it does not
259
+ contain a dash separating the name and the version of the distribution.
260
+
261
+ >>> from packaging.utils import parse_sdist_filename
262
+ >>> from packaging.version import Version
263
+ >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz")
264
+ >>> name
265
+ 'foo'
266
+ >>> ver == Version('1.0')
267
+ True
268
+
269
+ .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name
270
+ """
271
+ if filename.endswith(".tar.gz"):
272
+ file_stem = filename[: -len(".tar.gz")]
273
+ elif filename.endswith(".zip"):
274
+ file_stem = filename[: -len(".zip")]
275
+ else:
276
+ raise InvalidSdistFilename(
277
+ f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
278
+ f" {filename!r}"
279
+ )
280
+
281
+ # We are requiring a PEP 440 version, which cannot contain dashes,
282
+ # so we split on the last dash.
283
+ name_part, sep, version_part = file_stem.rpartition("-")
284
+ if not sep:
285
+ raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")
286
+
287
+ name = canonicalize_name(name_part)
288
+
289
+ try:
290
+ version = Version(version_part)
291
+ except InvalidVersion as e:
292
+ raise InvalidSdistFilename(
293
+ f"Invalid sdist filename (invalid version): {filename!r}"
294
+ ) from e
295
+
296
+ return (name, version)
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/cohere_asr/feature_extraction_cohere_asr.py ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import numpy as np
16
+ import torch
17
+
18
+ from ...feature_extraction_sequence_utils import SequenceFeatureExtractor
19
+ from ...feature_extraction_utils import BatchFeature
20
+ from ...utils import TensorType, is_librosa_available, logging
21
+ from ...utils.import_utils import requires
22
+
23
+
24
+ if is_librosa_available():
25
+ import librosa
26
+
27
+
28
+ EPSILON = 1e-5
29
+ LOG_ZERO_GUARD_VALUE = 2**-24
30
+
31
+
32
+ logger = logging.get_logger(__name__)
33
+
34
+
35
+ @requires(backends=("torch", "librosa"))
36
+ class CohereAsrFeatureExtractor(SequenceFeatureExtractor):
37
+ r"""
38
+ Constructs a CohereAsr feature extractor.
39
+
40
+ This feature extractor inherits from [`~feature_extraction_sequence_utils.SequenceFeatureExtractor`] which contains
41
+ most of the main methods. Users should refer to this superclass for more information regarding those methods.
42
+
43
+ This class extracts mel-filter bank features from raw speech using a custom numpy implementation of the `Short Time
44
+ Fourier Transform` which should match pytorch's `torch.stft` equivalent.
45
+
46
+ Args:
47
+ feature_size (`int`, *optional*, defaults to 128):
48
+ The feature dimension of the extracted features.
49
+ sampling_rate (`int`, *optional*, defaults to 16000):
50
+ The sampling rate at which the audio files should be digitalized expressed in hertz (Hz).
51
+ hop_length (`int`, *optional*, defaults to 160):
52
+ Length of the overlapping windows for the STFT used to obtain the Mel Frequency coefficients.
53
+ n_fft (`int`, *optional*, defaults to 512):
54
+ Size of the Fourier transform.
55
+ win_length (`int`, *optional*, defaults to 400):
56
+ The window length for the STFT computation.
57
+ preemphasis (`float`, *optional*, defaults to 0.97):
58
+ A preemphasis filter coefficient. 0.0 means no preemphasis filter.
59
+ padding_value (`float`, *optional*, defaults to 0.0):
60
+ Padding value used to pad the audio. Should correspond to silences.
61
+ dither (`float`, *optional*, defaults to 1e-05):
62
+ Amount of deterministic dither noise to add before feature extraction. Each sample is seeded by its
63
+ valid waveform length so that dither is batch-composition invariant. Set to 0.0 to disable.
64
+ max_audio_clip_s (`float`, *optional*, defaults to 35.0):
65
+ Maximum duration in seconds for a single audio chunk. Audio longer than
66
+ `max_audio_clip_s - overlap_chunk_second` is split at energy-based boundaries.
67
+ overlap_chunk_second (`float`, *optional*, defaults to 5.0):
68
+ Size in seconds of the boundary search window used when splitting long audio. This is not actual
69
+ overlap between chunks — it defines how far back from the chunk boundary to search for a quiet
70
+ split point.
71
+ min_energy_window_samples (`int`, *optional*, defaults to 1600):
72
+ Size in samples of the sliding window used to find the quietest point when splitting audio chunks.
73
+ """
74
+
75
+ model_input_names = ["input_features", "attention_mask"]
76
+
77
+ def __init__(
78
+ self,
79
+ feature_size=128,
80
+ sampling_rate=16000,
81
+ hop_length=160,
82
+ n_fft=512,
83
+ win_length=400,
84
+ preemphasis=0.97,
85
+ padding_value=0.0,
86
+ dither=1e-5,
87
+ max_audio_clip_s=35.0,
88
+ overlap_chunk_second=5.0,
89
+ min_energy_window_samples=1600,
90
+ **kwargs,
91
+ ):
92
+ super().__init__(feature_size=feature_size, sampling_rate=sampling_rate, padding_value=padding_value, **kwargs)
93
+
94
+ self.hop_length = hop_length
95
+ self.n_fft = n_fft
96
+ self.win_length = win_length
97
+ self.preemphasis = preemphasis
98
+ self.dither = dither
99
+ self.max_audio_clip_s = max_audio_clip_s
100
+ self.overlap_chunk_second = overlap_chunk_second
101
+ self.min_energy_window_samples = min_energy_window_samples
102
+
103
+ # TODO: @eustlb, for now we use librosa to compute the mel filters
104
+ # indeed mel_filter_bank uses np.float64 (while librosa uses np.float32), giving numerical differences
105
+ mel_filters = librosa.filters.mel(
106
+ sr=sampling_rate, n_fft=n_fft, n_mels=feature_size, fmin=0.0, fmax=sampling_rate / 2, norm="slaney"
107
+ )
108
+ self.mel_filters = torch.from_numpy(mel_filters).to(torch.float32)
109
+
110
+ def _find_split_point_energy(self, waveform: torch.Tensor, start_idx: int, end_idx: int) -> int:
111
+ segment = waveform[start_idx:end_idx]
112
+ if segment.shape[0] <= self.min_energy_window_samples:
113
+ return (start_idx + end_idx) // 2
114
+
115
+ min_energy = float("inf")
116
+ quietest_idx = start_idx
117
+ upper = segment.shape[0] - self.min_energy_window_samples
118
+ for i in range(0, upper, self.min_energy_window_samples):
119
+ window = segment[i : i + self.min_energy_window_samples]
120
+ energy = torch.sqrt(torch.mean(window * window)).item()
121
+ if energy < min_energy:
122
+ min_energy = energy
123
+ quietest_idx = start_idx + i
124
+ return quietest_idx
125
+
126
+ def _split_audio_chunks_energy(self, waveform: torch.Tensor) -> list[torch.Tensor]:
127
+ chunk_size = max(1, int(round(self.max_audio_clip_s * self.sampling_rate)))
128
+ boundary_context_size = max(1, int(round(self.overlap_chunk_second * self.sampling_rate)))
129
+ total_samples = waveform.shape[0]
130
+
131
+ if total_samples <= chunk_size:
132
+ return [waveform]
133
+
134
+ chunks_meta: list[tuple[int, int]] = []
135
+ idx = 0
136
+ while idx < total_samples:
137
+ if idx + chunk_size >= total_samples:
138
+ chunks_meta.append((idx, total_samples))
139
+ break
140
+
141
+ search_start = max(idx, idx + chunk_size - boundary_context_size)
142
+ search_end = min(idx + chunk_size, total_samples)
143
+ if search_end <= search_start:
144
+ split_point = idx + chunk_size
145
+ else:
146
+ split_point = self._find_split_point_energy(waveform, search_start, search_end)
147
+
148
+ split_point = max(idx + 1, min(split_point, total_samples))
149
+ chunks_meta.append((idx, split_point))
150
+ idx = split_point
151
+
152
+ return [waveform[start:end] for start, end in chunks_meta if end > start]
153
+
154
+ def _apply_dither(self, waveform: torch.Tensor, audio_lengths: torch.Tensor) -> torch.Tensor:
155
+ if self.dither <= 0:
156
+ return waveform
157
+ generator = torch.Generator(device=waveform.device)
158
+ for i in range(waveform.shape[0]):
159
+ valid_samples = min(int(audio_lengths[i].item()), waveform.shape[1])
160
+ if valid_samples <= 0:
161
+ continue
162
+ generator.manual_seed(valid_samples)
163
+ noise = torch.randn(valid_samples, dtype=waveform.dtype, device=waveform.device, generator=generator)
164
+ waveform[i, :valid_samples] += self.dither * noise
165
+ return waveform
166
+
167
+ def _torch_extract_fbank_features(self, waveform, device="cpu"):
168
+ # spectrogram
169
+ window = torch.hann_window(self.win_length, periodic=False, device=device)
170
+ stft = torch.stft(
171
+ waveform,
172
+ self.n_fft,
173
+ hop_length=self.hop_length,
174
+ win_length=self.win_length,
175
+ window=window,
176
+ return_complex=True,
177
+ pad_mode="constant",
178
+ )
179
+ # Let's match original implementation
180
+ magnitudes = torch.view_as_real(stft)
181
+ magnitudes = torch.sqrt(magnitudes.pow(2).sum(-1))
182
+ magnitudes = magnitudes.pow(2)
183
+
184
+ # log mel spectrogram
185
+ mel_filters = self.mel_filters.to(device)
186
+ mel_spec = mel_filters @ magnitudes
187
+ mel_spec = torch.log(mel_spec + LOG_ZERO_GUARD_VALUE)
188
+
189
+ # (batch_size, num_mel_filters, num_frames) -> (batch_size, num_frames, num_mel_filters)
190
+ mel_spec = mel_spec.permute(0, 2, 1)
191
+
192
+ return mel_spec
193
+
194
+ def __call__(
195
+ self,
196
+ raw_speech: np.ndarray | list[float] | list[np.ndarray] | list[list[float]],
197
+ truncation: bool = False,
198
+ pad_to_multiple_of: int | None = None,
199
+ return_tensors: str | TensorType | None = None,
200
+ return_attention_mask: bool | None = None,
201
+ padding: str | None = "longest",
202
+ max_length: int | None = None,
203
+ sampling_rate: int | None = None,
204
+ do_normalize: bool | None = None,
205
+ device: str | None = "cpu",
206
+ return_token_timestamps: bool | None = None,
207
+ **kwargs,
208
+ ) -> BatchFeature:
209
+ """
210
+ Main method to featurize and prepare for the model one or several sequence(s). Implementation uses PyTorch for
211
+ the STFT computation if available, otherwise a slower NumPy based one.
212
+
213
+ Args:
214
+ raw_speech (`np.ndarray`, `list[float]`, `list[np.ndarray]`, `list[list[float]]`):
215
+ The sequence or batch of sequences to be padded. Each sequence can be a numpy array, a list of float
216
+ values, a list of numpy arrays or a list of list of float values. Must be mono channel audio, not
217
+ stereo, i.e. single float per timestep.
218
+ truncation (`bool`, *optional*, default to `True`):
219
+ Activates truncation to cut input sequences longer than *max_length* to *max_length*.
220
+ pad_to_multiple_of (`int`, *optional*, defaults to None):
221
+ If set will pad the sequence to a multiple of the provided value.
222
+
223
+ This is especially useful to enable the use of Tensor Cores on NVIDIA hardware with compute capability
224
+ `>= 7.5` (Volta), or on TPUs which benefit from having sequence lengths be a multiple of 128.
225
+ return_attention_mask (`bool`, *optional*):
226
+ Whether to return the attention mask. If left to the default, will return the attention mask according
227
+ to the specific feature_extractor's default.
228
+
229
+ [What are attention masks?](../glossary#attention-mask)
230
+
231
+ <Tip>
232
+
233
+ For CohereAsr models, `attention_mask` should always be passed for batched inference, to avoid subtle
234
+ bugs.
235
+
236
+ </Tip>
237
+
238
+ return_tensors (`str` or [`~utils.TensorType`], *optional*):
239
+ If set, will return tensors instead of list of python integers. Acceptable values are:
240
+
241
+ - `'tf'`: Return TensorFlow `tf.constant` objects.
242
+ - `'pt'`: Return PyTorch `torch.Tensor` objects.
243
+ - `'np'`: Return Numpy `np.ndarray` objects.
244
+ sampling_rate (`int`, *optional*):
245
+ The sampling rate at which the `raw_speech` input was sampled. It is strongly recommended to pass
246
+ `sampling_rate` at the forward call to prevent silent errors and allow automatic speech recognition
247
+ pipeline.
248
+ padding_value (`float`, *optional*, defaults to 0.0):
249
+ The value that is used to fill the padding values / vectors.
250
+ do_normalize (`bool`, *optional*, defaults to `False`):
251
+ Whether or not to zero-mean unit-variance normalize the input. Normalizing can help to significantly
252
+ improve the performance of the model.
253
+ device (`str`, *optional*, defaults to `'cpu'`):
254
+ Specifies the device for computation of the log-mel spectrogram of audio signals in the
255
+ `_torch_extract_fbank_features` method. (e.g., "cpu", "cuda")
256
+ return_token_timestamps (`bool`, *optional*, defaults to `None`):
257
+ Deprecated. Use `return_attention_mask` instead from which the number of frames can be inferred.
258
+
259
+ Whether or not to return the number of frames of the input raw_speech.
260
+ These num_frames can be used by the model to compute word level timestamps.
261
+ """
262
+ if sampling_rate is not None:
263
+ if sampling_rate != self.sampling_rate:
264
+ raise ValueError(
265
+ f"The model corresponding to this feature extractor: {self.__class__.__name__} was trained using a"
266
+ f" sampling rate of {self.sampling_rate}. Please make sure that the provided `raw_speech` input"
267
+ f" was sampled with {self.sampling_rate} and not {sampling_rate}."
268
+ )
269
+ else:
270
+ logger.warning(
271
+ f"It is strongly recommended to pass the `sampling_rate` argument to `{self.__class__.__name__}()`. "
272
+ "Failing to do so can result in silent errors that might be hard to debug."
273
+ )
274
+
275
+ # Convert to torch tensor
276
+ if isinstance(raw_speech, np.ndarray):
277
+ raw_speech = torch.tensor(raw_speech)
278
+ elif isinstance(raw_speech, (list, tuple)) and isinstance(raw_speech[0], np.ndarray):
279
+ raw_speech = [torch.tensor(speech) for speech in raw_speech]
280
+
281
+ is_batched_torch = isinstance(raw_speech, torch.Tensor) and len(raw_speech.shape) > 1
282
+ if is_batched_torch and len(raw_speech.shape) > 2:
283
+ logger.warning(
284
+ f"Only mono-channel audio is supported for input to {self.__class__.__name__}. "
285
+ "We will take the mean of the channels to convert to mono."
286
+ )
287
+ raw_speech = raw_speech.mean(-1)
288
+
289
+ is_batched_sequence = isinstance(raw_speech, (list, tuple))
290
+ if is_batched_sequence:
291
+ for speech in raw_speech:
292
+ if len(speech.shape) > 1:
293
+ logger.warning(
294
+ f"Only mono-channel audio is supported for input to {self.__class__.__name__}. "
295
+ "We will take the mean of the channels to convert to mono."
296
+ )
297
+ speech = speech.mean(-1)
298
+
299
+ if is_batched_torch or is_batched_sequence:
300
+ raw_speech = [speech.to(torch.float32) for speech in raw_speech]
301
+ else:
302
+ raw_speech = [raw_speech.to(torch.float32)]
303
+
304
+ # Chunk long audio at energy-based boundaries
305
+ fast_path_threshold_s = max(0.0, self.max_audio_clip_s - self.overlap_chunk_second)
306
+ audio_chunk_index: list[tuple[int, int | None]] = []
307
+ chunked_speech: list[torch.Tensor] = []
308
+ for sample_idx, speech in enumerate(raw_speech):
309
+ duration_s = speech.shape[0] / self.sampling_rate
310
+ if duration_s <= fast_path_threshold_s:
311
+ chunked_speech.append(speech)
312
+ audio_chunk_index.append((sample_idx, None))
313
+ else:
314
+ chunks = self._split_audio_chunks_energy(speech)
315
+ for chunk_idx, chunk in enumerate(chunks):
316
+ chunked_speech.append(chunk)
317
+ audio_chunk_index.append((sample_idx, chunk_idx))
318
+
319
+ raw_speech = [speech[:, None] for speech in chunked_speech]
320
+
321
+ audio_lengths = [len(speech) for speech in raw_speech]
322
+ batched_speech = BatchFeature({"input_features": raw_speech, "audio_lengths": audio_lengths})
323
+
324
+ padded_inputs = self.pad(
325
+ batched_speech,
326
+ padding=padding,
327
+ max_length=max_length,
328
+ truncation=truncation,
329
+ pad_to_multiple_of=pad_to_multiple_of,
330
+ return_tensors="pt",
331
+ )
332
+ input_features = padded_inputs.input_features.squeeze(-1)
333
+
334
+ # dithering
335
+ input_features = self._apply_dither(input_features, padded_inputs.audio_lengths)
336
+
337
+ # preemphasis
338
+ if self.preemphasis is not None:
339
+ timemask = torch.arange(input_features.shape[1], device=input_features.device).unsqueeze(
340
+ 0
341
+ ) < padded_inputs.audio_lengths.unsqueeze(1)
342
+ input_features = torch.cat(
343
+ [input_features[:, :1], input_features[:, 1:] - self.preemphasis * input_features[:, :-1]], dim=1
344
+ )
345
+ input_features = input_features.masked_fill(~timemask, 0.0)
346
+
347
+ input_features = self._torch_extract_fbank_features(input_features, device)
348
+ features_lengths = torch.floor_divide(
349
+ padded_inputs.audio_lengths + self.n_fft // 2 * 2 - self.n_fft, self.hop_length
350
+ )
351
+ attention_mask = torch.arange(input_features.shape[1], device=device)[None, :] < features_lengths[:, None]
352
+
353
+ # normalize mel features, ignoring padding
354
+ mask = attention_mask.unsqueeze(-1)
355
+ input_features_masked = input_features * mask
356
+ mean = input_features_masked.sum(dim=1) / features_lengths.unsqueeze(-1)
357
+ mean = mean.unsqueeze(1)
358
+ variance = ((input_features_masked - mean) ** 2 * mask).sum(dim=1) / (features_lengths - 1).unsqueeze(-1)
359
+ std = torch.sqrt(variance).unsqueeze(1)
360
+ input_features = (input_features - mean) / (std + EPSILON)
361
+ input_features *= mask
362
+
363
+ result = BatchFeature(
364
+ data={
365
+ "input_features": input_features,
366
+ "attention_mask": attention_mask,
367
+ },
368
+ tensor_type=return_tensors,
369
+ )
370
+ result["audio_chunk_index"] = audio_chunk_index
371
+ return result
372
+
373
+
374
+ __all__ = ["CohereAsrFeatureExtractor"]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/cohere_asr/processing_cohere_asr.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2026 The HuggingFace Inc. team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from ...audio_utils import AudioInput, make_list_of_audio
16
+ from ...processing_utils import ProcessingKwargs, ProcessorMixin, Unpack
17
+ from ...tokenization_utils_base import PreTokenizedInput, TextInput
18
+ from ...utils import auto_docstring, is_torch_available, logging
19
+ from ...utils.import_utils import requires
20
+
21
+
22
+ if is_torch_available():
23
+ import torch
24
+
25
+
26
+ LANGUAGES = {"ar", "de", "el", "en", "es", "fr", "it", "ja", "ko", "nl", "pl", "pt", "vi", "zh"}
27
+ _NO_SPACE_LANGS = {"ja", "zh"}
28
+
29
+
30
+ logger = logging.get_logger(__name__)
31
+
32
+
33
+ class CohereAsrProcessorKwargs(ProcessingKwargs, total=False):
34
+ _defaults = {
35
+ "audio_kwargs": {
36
+ "sampling_rate": 16000,
37
+ "padding": "longest",
38
+ "return_attention_mask": True,
39
+ },
40
+ "text_kwargs": {
41
+ "padding": True,
42
+ "padding_side": "right",
43
+ "add_special_tokens": False,
44
+ },
45
+ "common_kwargs": {"return_tensors": "pt"},
46
+ }
47
+
48
+
49
+ @auto_docstring
50
+ @requires(backends=("torch",))
51
+ class CohereAsrProcessor(ProcessorMixin):
52
+ def __init__(self, feature_extractor, tokenizer):
53
+ super().__init__(feature_extractor, tokenizer)
54
+
55
+ def get_decoder_prompt_ids(self, language: str, punctuation: bool = True) -> list[int]:
56
+ """Build the decoder prompt token IDs for the given language and punctuation settings."""
57
+ if language not in LANGUAGES:
58
+ raise ValueError(
59
+ f"Unsupported language: {language!r}. Supported languages: {', '.join(sorted(LANGUAGES))}."
60
+ )
61
+ pnc_token = "<|pnc|>" if punctuation else "<|nopnc|>"
62
+ tokens = [
63
+ "▁",
64
+ "<|startofcontext|>",
65
+ "<|startoftranscript|>",
66
+ "<|emo:undefined|>",
67
+ f"<|{language}|>",
68
+ f"<|{language}|>",
69
+ pnc_token,
70
+ "<|noitn|>",
71
+ "<|notimestamp|>",
72
+ "<|nodiarize|>",
73
+ ]
74
+ return self.tokenizer.convert_tokens_to_ids(tokens)
75
+
76
+ @auto_docstring
77
+ def __call__(
78
+ self,
79
+ audio: AudioInput,
80
+ language: str,
81
+ text: TextInput | PreTokenizedInput | list[TextInput] | list[PreTokenizedInput] | None = None,
82
+ punctuation: bool = True,
83
+ sampling_rate: int | None = None,
84
+ **kwargs: Unpack[CohereAsrProcessorKwargs],
85
+ ):
86
+ r"""
87
+ language (`str`):
88
+ Language code (e.g. `"en"`, `"es"`, `"fr"`) used to build the decoder prompt. The processor
89
+ constructs the full decoder prompt and returns `decoder_input_ids` alongside the audio features.
90
+ punctuation (`bool`, defaults to `True`):
91
+ Whether to enable punctuation in the decoder prompt.
92
+ sampling_rate (`int`, *optional*):
93
+ The sampling rate of the input audio in Hz. This should match the sampling rate expected by the feature
94
+ extractor (defaults to 16000 Hz). If provided, it will be validated against the processor's expected
95
+ sampling rate, and an error will be raised if they don't match. If not provided, a warning will be
96
+ issued and the default sampling rate will be assumed.
97
+ """
98
+ audio = make_list_of_audio(audio)
99
+
100
+ output_kwargs = self._merge_kwargs(
101
+ CohereAsrProcessorKwargs,
102
+ tokenizer_init_kwargs=self.tokenizer.init_kwargs,
103
+ **kwargs,
104
+ )
105
+
106
+ if sampling_rate is None:
107
+ logger.warning_once(
108
+ f"You've provided audio without specifying the sampling rate. It will be assumed to be {output_kwargs['audio_kwargs']['sampling_rate']}, which can result in silent errors."
109
+ )
110
+ elif sampling_rate != output_kwargs["audio_kwargs"]["sampling_rate"]:
111
+ raise ValueError(
112
+ f"The sampling rate of the audio ({sampling_rate}) does not match the sampling rate of the processor ({output_kwargs['audio_kwargs']['sampling_rate']}). Please provide resampled the audio to the expected sampling rate."
113
+ )
114
+
115
+ inputs = self.feature_extractor(audio, **output_kwargs["audio_kwargs"])
116
+
117
+ prompt_ids = self.get_decoder_prompt_ids(language=language, punctuation=punctuation)
118
+ batch_size = inputs["input_features"].shape[0]
119
+ inputs["decoder_input_ids"] = torch.tensor([prompt_ids] * batch_size, dtype=torch.long)
120
+
121
+ if text is not None:
122
+ encodings = self.tokenizer(text, **output_kwargs["text_kwargs"])
123
+ inputs["labels"] = encodings["input_ids"]
124
+
125
+ return inputs
126
+
127
+ def decode(self, *args, audio_chunk_index=None, language=None, **kwargs):
128
+ texts = self.tokenizer.decode(*args, **kwargs)
129
+ if audio_chunk_index is None:
130
+ return texts
131
+ if language is None:
132
+ raise ValueError("`language` must be provided when `audio_chunk_index` is given.")
133
+ separator = "" if language in _NO_SPACE_LANGS else " "
134
+ return self._reassemble_chunk_texts(texts, audio_chunk_index, separator)
135
+
136
+ @staticmethod
137
+ def _reassemble_chunk_texts(
138
+ texts: list[str],
139
+ audio_chunk_index: list[tuple[int, int | None]],
140
+ separator: str = " ",
141
+ ) -> list[str]:
142
+ """Reassemble per-chunk transcription texts back into per-sample strings.
143
+
144
+ When audio inputs are longer than the feature extractor's `max_audio_clip_s`, they are split into
145
+ overlapping chunks before being fed to the model. This means a single original audio sample can
146
+ produce multiple decoded text segments. This method reverses that chunking: it groups the decoded
147
+ texts by their original sample index using `chunk_map`, orders the chunks, and joins them
148
+ with `separator` to reconstruct one transcription string per input sample.
149
+
150
+ Args:
151
+ texts: Decoded text strings, one per model output (i.e. one per chunk).
152
+ audio_chunk_index: List of `(sample_idx, chunk_idx)` tuples that map each entry in
153
+ `texts` back to its original sample and chunk position. A `chunk_idx` of `None`
154
+ indicates the sample was not chunked.
155
+ separator: String used to join chunks belonging to the same sample. Defaults to a
156
+ space; callers pass an empty string for languages that don't use spaces between
157
+ words (e.g. Chinese, Japanese).
158
+
159
+ Returns:
160
+ A list of reassembled transcription strings, one per original input sample.
161
+ """
162
+ max_sample_idx = max(sample_idx for sample_idx, _ in audio_chunk_index)
163
+ outputs = [""] * (max_sample_idx + 1)
164
+ chunked = {}
165
+
166
+ for (sample_idx, chunk_idx), text in zip(audio_chunk_index, texts):
167
+ if chunk_idx is None:
168
+ outputs[sample_idx] = text
169
+ else:
170
+ if sample_idx not in chunked:
171
+ chunked[sample_idx] = []
172
+ chunked[sample_idx].append((chunk_idx, text))
173
+
174
+ for sample_idx, chunk_items in chunked.items():
175
+ chunk_items.sort(key=lambda item: item[0])
176
+ non_empty = [t for _, t in chunk_items if t and t.strip()]
177
+ parts = [non_empty[0].rstrip()] + [t.strip() for t in non_empty[1:]]
178
+ outputs[sample_idx] = separator.join(parts)
179
+
180
+ return outputs
181
+
182
+ @property
183
+ def model_input_names(self):
184
+ feature_extractor_input_names = self.feature_extractor.model_input_names
185
+ return feature_extractor_input_names + ["labels"]
186
+
187
+
188
+ __all__ = ["CohereAsrProcessor"]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/chunk_utils.py ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import logging
15
+ import math
16
+ from collections.abc import Callable, Iterable, Sequence
17
+ from functools import partial
18
+ from typing import Any
19
+
20
+ import torch
21
+
22
+ from .tensor_utils import tensor_tree_map, tree_map
23
+
24
+
25
+ def _fetch_dims(tree: dict | list | tuple | torch.Tensor) -> list[tuple[int, ...]]:
26
+ shapes = []
27
+ if isinstance(tree, dict):
28
+ for v in tree.values():
29
+ shapes.extend(_fetch_dims(v))
30
+ elif isinstance(tree, (list, tuple)):
31
+ for t in tree:
32
+ shapes.extend(_fetch_dims(t))
33
+ elif isinstance(tree, torch.Tensor):
34
+ shapes.append(tree.shape)
35
+ else:
36
+ raise TypeError("Not supported")
37
+
38
+ return shapes
39
+
40
+
41
+ @torch.jit.ignore
42
+ def _flat_idx_to_idx(flat_idx: int, dims: tuple[int, ...]) -> tuple[int, ...]:
43
+ idx = []
44
+ for d in reversed(dims):
45
+ idx.append(flat_idx % d)
46
+ flat_idx = flat_idx // d
47
+
48
+ return tuple(reversed(idx))
49
+
50
+
51
+ @torch.jit.ignore
52
+ def _get_minimal_slice_set(
53
+ start: Sequence[int],
54
+ end: Sequence[int],
55
+ dims: Sequence[int],
56
+ start_edges: Sequence[bool] | None = None,
57
+ end_edges: Sequence[bool] | None = None,
58
+ ) -> list[tuple[slice, ...]]:
59
+ """
60
+ Produces an ordered sequence of tensor slices that, when used in sequence on a tensor with shape dims, yields
61
+ tensors that contain every leaf in the contiguous range [start, end]. Care is taken to yield a short sequence of
62
+ slices, and perhaps even the shortest possible (I'm pretty sure it's the latter).
63
+
64
+ end is INCLUSIVE.
65
+ """
66
+
67
+ # start_edges and end_edges both indicate whether, starting from any given
68
+ # dimension, the start/end index is at the top/bottom edge of the
69
+ # corresponding tensor, modeled as a tree
70
+ def reduce_edge_list(l: list[bool]) -> None:
71
+ tally = True
72
+ for i in range(len(l)):
73
+ reversed_idx = -1 * (i + 1)
74
+ l[reversed_idx] &= tally
75
+ tally = l[reversed_idx]
76
+
77
+ if start_edges is None:
78
+ start_edges = [s == 0 for s in start]
79
+ reduce_edge_list(start_edges)
80
+ if end_edges is None:
81
+ end_edges = [e == (d - 1) for e, d in zip(end, dims)]
82
+ reduce_edge_list(end_edges)
83
+
84
+ # Base cases. Either start/end are empty and we're done, or the final,
85
+ # one-dimensional tensor can be simply sliced
86
+ if len(start) == 0:
87
+ return [()]
88
+ elif len(start) == 1:
89
+ return [(slice(start[0], end[0] + 1),)]
90
+
91
+ slices: list[tuple[slice, ...]] = []
92
+ path_list: list[slice] = []
93
+
94
+ # Dimensions common to start and end can be selected directly
95
+ for s, e in zip(start, end):
96
+ if s == e:
97
+ path_list.append(slice(s, s + 1))
98
+ else:
99
+ break
100
+
101
+ path: tuple[slice, ...] = tuple(path_list)
102
+ divergence_idx = len(path)
103
+
104
+ # start == end, and we're done
105
+ if divergence_idx == len(dims):
106
+ return [path]
107
+
108
+ def upper() -> tuple[tuple[slice, ...], ...]:
109
+ assert start_edges is not None
110
+ assert end_edges is not None
111
+
112
+ sdi = start[divergence_idx]
113
+ return tuple(
114
+ path + (slice(sdi, sdi + 1),) + s
115
+ for s in _get_minimal_slice_set(
116
+ start[divergence_idx + 1 :],
117
+ [d - 1 for d in dims[divergence_idx + 1 :]],
118
+ dims[divergence_idx + 1 :],
119
+ start_edges=start_edges[divergence_idx + 1 :],
120
+ end_edges=[True for _ in end_edges[divergence_idx + 1 :]],
121
+ )
122
+ )
123
+
124
+ def lower() -> tuple[tuple[slice, ...], ...]:
125
+ assert start_edges is not None
126
+ assert end_edges is not None
127
+
128
+ edi = end[divergence_idx]
129
+ return tuple(
130
+ path + (slice(edi, edi + 1),) + s
131
+ for s in _get_minimal_slice_set(
132
+ [0 for _ in start[divergence_idx + 1 :]],
133
+ end[divergence_idx + 1 :],
134
+ dims[divergence_idx + 1 :],
135
+ start_edges=[True for _ in start_edges[divergence_idx + 1 :]],
136
+ end_edges=end_edges[divergence_idx + 1 :],
137
+ )
138
+ )
139
+
140
+ # If both start and end are at the edges of the subtree rooted at
141
+ # divergence_idx, we can just select the whole subtree at once
142
+ if start_edges[divergence_idx] and end_edges[divergence_idx]:
143
+ slices.append(path + (slice(start[divergence_idx], end[divergence_idx] + 1),))
144
+ # If just start is at the edge, we can grab almost all of the subtree,
145
+ # treating only the ragged bottom edge as an edge case
146
+ elif start_edges[divergence_idx]:
147
+ slices.append(path + (slice(start[divergence_idx], end[divergence_idx]),))
148
+ slices.extend(lower())
149
+ # Analogous to the previous case, but the top is ragged this time
150
+ elif end_edges[divergence_idx]:
151
+ slices.extend(upper())
152
+ slices.append(path + (slice(start[divergence_idx] + 1, end[divergence_idx] + 1),))
153
+ # If both sides of the range are ragged, we need to handle both sides
154
+ # separately. If there's contiguous meat in between them, we can index it
155
+ # in one big chunk
156
+ else:
157
+ slices.extend(upper())
158
+ middle_ground = end[divergence_idx] - start[divergence_idx]
159
+ if middle_ground > 1:
160
+ slices.append(path + (slice(start[divergence_idx] + 1, end[divergence_idx]),))
161
+ slices.extend(lower())
162
+
163
+ return slices
164
+
165
+
166
+ @torch.jit.ignore
167
+ def _chunk_slice(t: torch.Tensor, flat_start: int, flat_end: int, no_batch_dims: int) -> torch.Tensor:
168
+ """
169
+ Equivalent to
170
+
171
+ t.reshape((-1,) + t.shape[no_batch_dims:])[flat_start:flat_end]
172
+
173
+ but without the need for the initial reshape call, which can be memory-intensive in certain situations. The only
174
+ reshape operations in this function are performed on sub-tensors that scale with (flat_end - flat_start), the chunk
175
+ size.
176
+ """
177
+
178
+ batch_dims = t.shape[:no_batch_dims]
179
+ start_idx = list(_flat_idx_to_idx(flat_start, batch_dims))
180
+ # _get_minimal_slice_set is inclusive
181
+ end_idx = list(_flat_idx_to_idx(flat_end - 1, batch_dims))
182
+
183
+ # Get an ordered list of slices to perform
184
+ slices = _get_minimal_slice_set(
185
+ start_idx,
186
+ end_idx,
187
+ batch_dims,
188
+ )
189
+
190
+ sliced_tensors = [t[s] for s in slices]
191
+
192
+ return torch.cat([s.view((-1,) + t.shape[no_batch_dims:]) for s in sliced_tensors])
193
+
194
+
195
+ def chunk_layer(
196
+ layer: Callable,
197
+ inputs: dict[str, Any],
198
+ chunk_size: int,
199
+ no_batch_dims: int,
200
+ low_mem: bool = False,
201
+ _out: Any = None,
202
+ _add_into_out: bool = False,
203
+ ) -> Any:
204
+ """
205
+ Implements the "chunking" procedure described in section 1.11.8.
206
+
207
+ Layer outputs and inputs are assumed to be simple "pytrees," consisting only of (arbitrarily nested) lists, tuples,
208
+ and dicts with torch.Tensor leaves.
209
+
210
+ Args:
211
+ layer:
212
+ The layer to be applied chunk-wise
213
+ inputs:
214
+ A (non-nested) dictionary of keyworded inputs. All leaves must be tensors and must share the same batch
215
+ dimensions.
216
+ chunk_size:
217
+ The number of sub-batches per chunk. If multiple batch dimensions are specified, a "sub-batch" is defined
218
+ as a single indexing of all batch dimensions simultaneously (s.t. the number of sub-batches is the product
219
+ of the batch dimensions).
220
+ no_batch_dims:
221
+ How many of the initial dimensions of each input tensor can be considered batch dimensions.
222
+ low_mem:
223
+ Avoids flattening potentially large input tensors. Unnecessary in most cases, and is ever so slightly
224
+ slower than the default setting.
225
+ Returns:
226
+ The reassembled output of the layer on the inputs.
227
+ """
228
+ if not (len(inputs) > 0):
229
+ raise ValueError("Must provide at least one input")
230
+
231
+ initial_dims = [shape[:no_batch_dims] for shape in _fetch_dims(inputs)]
232
+ orig_batch_dims = tuple(max(s) for s in zip(*initial_dims))
233
+
234
+ def _prep_inputs(t: torch.Tensor) -> torch.Tensor:
235
+ if not low_mem:
236
+ if sum(t.shape[:no_batch_dims]) != no_batch_dims:
237
+ t = t.expand(orig_batch_dims + t.shape[no_batch_dims:])
238
+ t = t.reshape(-1, *t.shape[no_batch_dims:])
239
+ else:
240
+ t = t.expand(orig_batch_dims + t.shape[no_batch_dims:])
241
+ return t
242
+
243
+ prepped_inputs: dict[str, Any] = tensor_tree_map(_prep_inputs, inputs)
244
+ prepped_outputs = None
245
+ if _out is not None:
246
+ prepped_outputs = tensor_tree_map(lambda t: t.view([-1] + list(t.shape[no_batch_dims:])), _out)
247
+
248
+ flat_batch_dim = 1
249
+ for d in orig_batch_dims:
250
+ flat_batch_dim *= d
251
+
252
+ no_chunks = flat_batch_dim // chunk_size + (flat_batch_dim % chunk_size != 0)
253
+
254
+ def _select_chunk(t: torch.Tensor) -> torch.Tensor:
255
+ return t[i : i + chunk_size] if t.shape[0] != 1 else t
256
+
257
+ i = 0
258
+ out = prepped_outputs
259
+ for _ in range(no_chunks):
260
+ # Chunk the input
261
+ if not low_mem:
262
+ select_chunk = _select_chunk
263
+ else:
264
+ select_chunk = partial(
265
+ _chunk_slice,
266
+ flat_start=i,
267
+ flat_end=min(flat_batch_dim, i + chunk_size),
268
+ no_batch_dims=len(orig_batch_dims),
269
+ )
270
+
271
+ chunks: dict[str, Any] = tensor_tree_map(select_chunk, prepped_inputs)
272
+
273
+ # Run the layer on the chunk
274
+ output_chunk = layer(**chunks)
275
+
276
+ # Allocate space for the output
277
+ if out is None:
278
+ out = tensor_tree_map(lambda t: t.new_zeros((flat_batch_dim,) + t.shape[1:]), output_chunk)
279
+
280
+ # Put the chunk in its pre-allocated space
281
+ if isinstance(output_chunk, dict):
282
+
283
+ def assign(d1: dict, d2: dict) -> None:
284
+ for k, v in d1.items():
285
+ if isinstance(v, dict):
286
+ assign(v, d2[k])
287
+ else:
288
+ if _add_into_out:
289
+ v[i : i + chunk_size] += d2[k]
290
+ else:
291
+ v[i : i + chunk_size] = d2[k]
292
+
293
+ assign(out, output_chunk)
294
+ elif isinstance(output_chunk, tuple):
295
+ for x1, x2 in zip(out, output_chunk):
296
+ if _add_into_out:
297
+ x1[i : i + chunk_size] += x2
298
+ else:
299
+ x1[i : i + chunk_size] = x2
300
+ elif isinstance(output_chunk, torch.Tensor):
301
+ if _add_into_out:
302
+ out[i : i + chunk_size] += output_chunk
303
+ else:
304
+ out[i : i + chunk_size] = output_chunk
305
+ else:
306
+ raise TypeError("Not supported")
307
+
308
+ i += chunk_size
309
+
310
+ out = tensor_tree_map(lambda t: t.view(orig_batch_dims + t.shape[1:]), out)
311
+
312
+ return out
313
+
314
+
315
+ class ChunkSizeTuner:
316
+ def __init__(
317
+ self,
318
+ # Heuristically, runtimes for most of the modules in the network
319
+ # plateau earlier than this on all GPUs I've run the model on.
320
+ max_chunk_size: int = 512,
321
+ ):
322
+ self.max_chunk_size = max_chunk_size
323
+ self.cached_chunk_size: int | None = None
324
+ self.cached_arg_data: tuple | None = None
325
+
326
+ def _determine_favorable_chunk_size(self, fn: Callable, args: tuple, min_chunk_size: int) -> int:
327
+ logging.info("Tuning chunk size...")
328
+
329
+ if min_chunk_size >= self.max_chunk_size:
330
+ return min_chunk_size
331
+
332
+ candidates: list[int] = [2**l for l in range(int(math.log2(self.max_chunk_size)) + 1)]
333
+ candidates = [c for c in candidates if c > min_chunk_size]
334
+ candidates = [min_chunk_size] + candidates
335
+ candidates[-1] += 4
336
+
337
+ def test_chunk_size(chunk_size: int) -> bool:
338
+ try:
339
+ with torch.no_grad():
340
+ fn(*args, chunk_size=chunk_size)
341
+ return True
342
+ except RuntimeError:
343
+ return False
344
+
345
+ min_viable_chunk_size_index = 0
346
+ i = len(candidates) - 1
347
+ while i > min_viable_chunk_size_index:
348
+ viable = test_chunk_size(candidates[i])
349
+ if not viable:
350
+ i = (min_viable_chunk_size_index + i) // 2
351
+ else:
352
+ min_viable_chunk_size_index = i
353
+ i = (i + len(candidates) - 1) // 2
354
+
355
+ return candidates[min_viable_chunk_size_index]
356
+
357
+ def _compare_arg_caches(self, ac1: Iterable, ac2: Iterable) -> bool:
358
+ consistent = True
359
+ for a1, a2 in zip(ac1, ac2):
360
+ assert type(ac1) is type(ac2)
361
+ if isinstance(ac1, (list, tuple)):
362
+ consistent &= self._compare_arg_caches(a1, a2)
363
+ elif isinstance(ac1, dict):
364
+ a1_items = [v for _, v in sorted(a1.items(), key=lambda x: x[0])]
365
+ a2_items = [v for _, v in sorted(a2.items(), key=lambda x: x[0])]
366
+ consistent &= self._compare_arg_caches(a1_items, a2_items)
367
+ else:
368
+ consistent &= a1 == a2
369
+
370
+ return consistent
371
+
372
+ def tune_chunk_size(
373
+ self,
374
+ representative_fn: Callable,
375
+ args: tuple,
376
+ min_chunk_size: int,
377
+ ) -> int:
378
+ consistent = True
379
+ arg_data: tuple = tree_map(lambda a: a.shape if isinstance(a, torch.Tensor) else a, args, object)
380
+ if self.cached_arg_data is not None:
381
+ # If args have changed shape/value, we need to re-tune
382
+ assert len(self.cached_arg_data) == len(arg_data)
383
+ consistent = self._compare_arg_caches(self.cached_arg_data, arg_data)
384
+ else:
385
+ # Otherwise, we can reuse the precomputed value
386
+ consistent = False
387
+
388
+ if not consistent:
389
+ self.cached_chunk_size = self._determine_favorable_chunk_size(
390
+ representative_fn,
391
+ args,
392
+ min_chunk_size,
393
+ )
394
+ self.cached_arg_data = arg_data
395
+
396
+ assert self.cached_chunk_size is not None
397
+
398
+ return self.cached_chunk_size
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/loss.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ import torch
18
+
19
+
20
+ def _calculate_bin_centers(boundaries: torch.Tensor) -> torch.Tensor:
21
+ step = boundaries[1] - boundaries[0]
22
+ bin_centers = boundaries + step / 2
23
+ bin_centers = torch.cat([bin_centers, (bin_centers[-1] + step).unsqueeze(-1)], dim=0)
24
+ return bin_centers
25
+
26
+
27
+ def _calculate_expected_aligned_error(
28
+ alignment_confidence_breaks: torch.Tensor,
29
+ aligned_distance_error_probs: torch.Tensor,
30
+ ) -> tuple[torch.Tensor, torch.Tensor]:
31
+ bin_centers = _calculate_bin_centers(alignment_confidence_breaks)
32
+ return (
33
+ torch.sum(aligned_distance_error_probs * bin_centers, dim=-1),
34
+ bin_centers[-1],
35
+ )
36
+
37
+
38
+ def compute_predicted_aligned_error(
39
+ logits: torch.Tensor,
40
+ max_bin: int = 31,
41
+ no_bins: int = 64,
42
+ **kwargs,
43
+ ) -> dict[str, torch.Tensor]:
44
+ """Computes aligned confidence metrics from logits.
45
+
46
+ Args:
47
+ logits: [*, num_res, num_res, num_bins] the logits output from
48
+ PredictedAlignedErrorHead.
49
+ max_bin: Maximum bin value
50
+ no_bins: Number of bins
51
+ Returns:
52
+ aligned_confidence_probs: [*, num_res, num_res, num_bins] the predicted
53
+ aligned error probabilities over bins for each residue pair.
54
+ predicted_aligned_error: [*, num_res, num_res] the expected aligned distance
55
+ error for each pair of residues.
56
+ max_predicted_aligned_error: [*] the maximum predicted error possible.
57
+ """
58
+ boundaries = torch.linspace(0, max_bin, steps=(no_bins - 1), device=logits.device)
59
+
60
+ aligned_confidence_probs = torch.nn.functional.softmax(logits, dim=-1)
61
+ predicted_aligned_error, max_predicted_aligned_error = _calculate_expected_aligned_error(
62
+ alignment_confidence_breaks=boundaries,
63
+ aligned_distance_error_probs=aligned_confidence_probs,
64
+ )
65
+
66
+ return {
67
+ "aligned_confidence_probs": aligned_confidence_probs,
68
+ "predicted_aligned_error": predicted_aligned_error,
69
+ "max_predicted_aligned_error": max_predicted_aligned_error,
70
+ }
71
+
72
+
73
+ def compute_tm(
74
+ logits: torch.Tensor,
75
+ residue_weights: torch.Tensor | None = None,
76
+ max_bin: int = 31,
77
+ no_bins: int = 64,
78
+ eps: float = 1e-8,
79
+ **kwargs,
80
+ ) -> torch.Tensor:
81
+ if residue_weights is None:
82
+ residue_weights = logits.new_ones(logits.shape[-2])
83
+
84
+ boundaries = torch.linspace(0, max_bin, steps=(no_bins - 1), device=logits.device)
85
+
86
+ bin_centers = _calculate_bin_centers(boundaries)
87
+ torch.sum(residue_weights)
88
+ n = logits.shape[-2]
89
+ clipped_n = max(n, 19)
90
+
91
+ d0 = 1.24 * (clipped_n - 15) ** (1.0 / 3) - 1.8
92
+
93
+ probs = torch.nn.functional.softmax(logits, dim=-1)
94
+
95
+ tm_per_bin = 1.0 / (1 + (bin_centers**2) / (d0**2))
96
+ predicted_tm_term = torch.sum(probs * tm_per_bin, dim=-1)
97
+
98
+ normed_residue_mask = residue_weights / (eps + residue_weights.sum())
99
+ per_alignment = torch.sum(predicted_tm_term * normed_residue_mask, dim=-1)
100
+
101
+ weighted = per_alignment * residue_weights
102
+
103
+ argmax = (weighted == torch.max(weighted)).nonzero()[0]
104
+ return per_alignment[tuple(argmax)]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/protein.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Protein data type."""
17
+
18
+ import dataclasses
19
+ import re
20
+ import string
21
+ from collections.abc import Iterator, Mapping, Sequence
22
+ from typing import Any
23
+
24
+ import numpy as np
25
+
26
+ from . import residue_constants
27
+
28
+
29
+ FeatureDict = Mapping[str, np.ndarray]
30
+ ModelOutput = Mapping[str, Any] # Is a nested dict.
31
+ PICO_TO_ANGSTROM = 0.01
32
+
33
+
34
+ @dataclasses.dataclass(frozen=True)
35
+ class Protein:
36
+ """Protein structure representation."""
37
+
38
+ # Cartesian coordinates of atoms in angstroms. The atom types correspond to
39
+ # residue_constants.atom_types, i.e. the first three are N, CA, CB.
40
+ atom_positions: np.ndarray # [num_res, num_atom_type, 3]
41
+
42
+ # Amino-acid type for each residue represented as an integer between 0 and
43
+ # 20, where 20 is 'X'.
44
+ aatype: np.ndarray # [num_res]
45
+
46
+ # Binary float mask to indicate presence of a particular atom. 1.0 if an atom
47
+ # is present and 0.0 if not. This should be used for loss masking.
48
+ atom_mask: np.ndarray # [num_res, num_atom_type]
49
+
50
+ # Residue index as used in PDB. It is not necessarily continuous or 0-indexed.
51
+ residue_index: np.ndarray # [num_res]
52
+
53
+ # B-factors, or temperature factors, of each residue (in sq. angstroms units),
54
+ # representing the displacement of the residue from its ground truth mean
55
+ # value.
56
+ b_factors: np.ndarray # [num_res, num_atom_type]
57
+
58
+ # Chain indices for multi-chain predictions
59
+ chain_index: np.ndarray | None = None
60
+
61
+ # Optional remark about the protein. Included as a comment in output PDB
62
+ # files
63
+ remark: str | None = None
64
+
65
+ # Templates used to generate this protein (prediction-only)
66
+ parents: Sequence[str] | None = None
67
+
68
+ # Chain corresponding to each parent
69
+ parents_chain_index: Sequence[int] | None = None
70
+
71
+
72
+ def from_proteinnet_string(proteinnet_str: str) -> Protein:
73
+ tag_re = r"(\[[A-Z]+\]\n)"
74
+ tags: list[str] = [tag.strip() for tag in re.split(tag_re, proteinnet_str) if len(tag) > 0]
75
+ groups: Iterator[tuple[str, list[str]]] = zip(tags[0::2], [l.split("\n") for l in tags[1::2]])
76
+
77
+ atoms: list[str] = ["N", "CA", "C"]
78
+ aatype = None
79
+ atom_positions = None
80
+ atom_mask = None
81
+ for g in groups:
82
+ if g[0] == "[PRIMARY]":
83
+ seq = g[1][0].strip()
84
+ # Replace unknown residues with "X" (strings are immutable, so convert to list first)
85
+ seq = [char if char in residue_constants.restypes else "X" for char in seq]
86
+ aatype = np.array(
87
+ [residue_constants.restype_order.get(res_symbol, residue_constants.restype_num) for res_symbol in seq]
88
+ )
89
+ elif g[0] == "[TERTIARY]":
90
+ tertiary: list[list[float]] = []
91
+ for axis in range(3):
92
+ tertiary.append(list(map(float, g[1][axis].split())))
93
+ tertiary_np = np.array(tertiary)
94
+ atom_positions = np.zeros((len(tertiary[0]) // 3, residue_constants.atom_type_num, 3)).astype(np.float32)
95
+ for i, atom in enumerate(atoms):
96
+ atom_positions[:, residue_constants.atom_order[atom], :] = np.transpose(tertiary_np[:, i::3])
97
+ atom_positions *= PICO_TO_ANGSTROM
98
+ elif g[0] == "[MASK]":
99
+ mask = np.array(list(map({"-": 0, "+": 1}.get, g[1][0].strip())))
100
+ atom_mask = np.zeros(
101
+ (
102
+ len(mask),
103
+ residue_constants.atom_type_num,
104
+ )
105
+ ).astype(np.float32)
106
+ for i, atom in enumerate(atoms):
107
+ atom_mask[:, residue_constants.atom_order[atom]] = 1
108
+ atom_mask *= mask[..., None]
109
+
110
+ assert aatype is not None
111
+
112
+ return Protein(
113
+ atom_positions=atom_positions,
114
+ atom_mask=atom_mask,
115
+ aatype=aatype,
116
+ residue_index=np.arange(len(aatype)),
117
+ b_factors=None,
118
+ )
119
+
120
+
121
+ def get_pdb_headers(prot: Protein, chain_id: int = 0) -> list[str]:
122
+ pdb_headers: list[str] = []
123
+
124
+ remark = prot.remark
125
+ if remark is not None:
126
+ pdb_headers.append(f"REMARK {remark}")
127
+
128
+ parents = prot.parents
129
+ parents_chain_index = prot.parents_chain_index
130
+ if parents is not None and parents_chain_index is not None:
131
+ parents = [p for i, p in zip(parents_chain_index, parents) if i == chain_id]
132
+
133
+ if parents is None or len(parents) == 0:
134
+ parents = ["N/A"]
135
+
136
+ pdb_headers.append(f"PARENT {' '.join(parents)}")
137
+
138
+ return pdb_headers
139
+
140
+
141
+ def add_pdb_headers(prot: Protein, pdb_str: str) -> str:
142
+ """Add pdb headers to an existing PDB string. Useful during multi-chain
143
+ recycling
144
+ """
145
+ out_pdb_lines: list[str] = []
146
+ lines = pdb_str.split("\n")
147
+
148
+ remark = prot.remark
149
+ if remark is not None:
150
+ out_pdb_lines.append(f"REMARK {remark}")
151
+
152
+ parents_per_chain: list[list[str]]
153
+ if prot.parents is not None and len(prot.parents) > 0:
154
+ parents_per_chain = []
155
+ if prot.parents_chain_index is not None:
156
+ parent_dict: dict[str, list[str]] = {}
157
+ for p, i in zip(prot.parents, prot.parents_chain_index):
158
+ parent_dict.setdefault(str(i), [])
159
+ parent_dict[str(i)].append(p)
160
+
161
+ max_idx = max(int(chain_idx) for chain_idx in parent_dict)
162
+ for i in range(max_idx + 1):
163
+ chain_parents = parent_dict.get(str(i), ["N/A"])
164
+ parents_per_chain.append(chain_parents)
165
+ else:
166
+ parents_per_chain.append(list(prot.parents))
167
+ else:
168
+ parents_per_chain = [["N/A"]]
169
+
170
+ def make_parent_line(p: Sequence[str]) -> str:
171
+ return f"PARENT {' '.join(p)}"
172
+
173
+ out_pdb_lines.append(make_parent_line(parents_per_chain[0]))
174
+
175
+ chain_counter = 0
176
+ for i, l in enumerate(lines):
177
+ if "PARENT" not in l and "REMARK" not in l:
178
+ out_pdb_lines.append(l)
179
+ if "TER" in l and "END" not in lines[i + 1]:
180
+ chain_counter += 1
181
+ if not chain_counter >= len(parents_per_chain):
182
+ chain_parents = parents_per_chain[chain_counter]
183
+ else:
184
+ chain_parents = ["N/A"]
185
+
186
+ out_pdb_lines.append(make_parent_line(chain_parents))
187
+
188
+ return "\n".join(out_pdb_lines)
189
+
190
+
191
+ def to_pdb(prot: Protein) -> str:
192
+ """Converts a `Protein` instance to a PDB string.
193
+
194
+ Args:
195
+ prot: The protein to convert to PDB.
196
+
197
+ Returns:
198
+ PDB string.
199
+ """
200
+ restypes = residue_constants.restypes + ["X"]
201
+
202
+ def res_1to3(r: int) -> str:
203
+ return residue_constants.restype_1to3.get(restypes[r], "UNK")
204
+
205
+ atom_types = residue_constants.atom_types
206
+
207
+ pdb_lines: list[str] = []
208
+
209
+ atom_mask = prot.atom_mask
210
+ aatype = prot.aatype
211
+ atom_positions = prot.atom_positions
212
+ residue_index = prot.residue_index.astype(np.int32)
213
+ b_factors = prot.b_factors
214
+ chain_index = prot.chain_index
215
+
216
+ if np.any(aatype > residue_constants.restype_num):
217
+ raise ValueError("Invalid aatypes.")
218
+
219
+ headers = get_pdb_headers(prot)
220
+ if len(headers) > 0:
221
+ pdb_lines.extend(headers)
222
+
223
+ n = aatype.shape[0]
224
+ atom_index = 1
225
+ prev_chain_index = 0
226
+ chain_tags = string.ascii_uppercase
227
+ chain_tag = None
228
+ # Add all atom sites.
229
+ for i in range(n):
230
+ res_name_3 = res_1to3(aatype[i])
231
+ for atom_name, pos, mask, b_factor in zip(atom_types, atom_positions[i], atom_mask[i], b_factors[i]):
232
+ if mask < 0.5:
233
+ continue
234
+
235
+ record_type = "ATOM"
236
+ name = atom_name if len(atom_name) == 4 else f" {atom_name}"
237
+ alt_loc = ""
238
+ insertion_code = ""
239
+ occupancy = 1.00
240
+ element = atom_name[0] # Protein supports only C, N, O, S, this works.
241
+ charge = ""
242
+
243
+ chain_tag = "A"
244
+ if chain_index is not None:
245
+ chain_tag = chain_tags[chain_index[i]]
246
+
247
+ # PDB is a columnar format, every space matters here!
248
+ atom_line = (
249
+ f"{record_type:<6}{atom_index:>5} {name:<4}{alt_loc:>1}"
250
+ f"{res_name_3:>3} {chain_tag:>1}"
251
+ f"{residue_index[i]:>4}{insertion_code:>1} "
252
+ f"{pos[0]:>8.3f}{pos[1]:>8.3f}{pos[2]:>8.3f}"
253
+ f"{occupancy:>6.2f}{b_factor:>6.2f} "
254
+ f"{element:>2}{charge:>2}"
255
+ )
256
+ pdb_lines.append(atom_line)
257
+ atom_index += 1
258
+
259
+ should_terminate = i == n - 1
260
+ if chain_index is not None:
261
+ if i != n - 1 and chain_index[i + 1] != prev_chain_index:
262
+ should_terminate = True
263
+ prev_chain_index = chain_index[i + 1]
264
+
265
+ if should_terminate:
266
+ # Close the chain.
267
+ chain_end = "TER"
268
+ chain_termination_line = (
269
+ f"{chain_end:<6}{atom_index:>5} {res_1to3(aatype[i]):>3} {chain_tag:>1}{residue_index[i]:>4}"
270
+ )
271
+ pdb_lines.append(chain_termination_line)
272
+ atom_index += 1
273
+
274
+ if i != n - 1:
275
+ # "prev" is a misnomer here. This happens at the beginning of
276
+ # each new chain.
277
+ pdb_lines.extend(get_pdb_headers(prot, prev_chain_index))
278
+
279
+ pdb_lines.append("END")
280
+ pdb_lines.append("")
281
+ return "\n".join(pdb_lines)
282
+
283
+
284
+ def ideal_atom_mask(prot: Protein) -> np.ndarray:
285
+ """Computes an ideal atom mask.
286
+
287
+ `Protein.atom_mask` typically is defined according to the atoms that are reported in the PDB. This function
288
+ computes a mask according to heavy atoms that should be present in the given sequence of amino acids.
289
+
290
+ Args:
291
+ prot: `Protein` whose fields are `numpy.ndarray` objects.
292
+
293
+ Returns:
294
+ An ideal atom mask.
295
+ """
296
+ return residue_constants.STANDARD_ATOM_MASK[prot.aatype]
297
+
298
+
299
+ def from_prediction(
300
+ features: FeatureDict,
301
+ result: ModelOutput,
302
+ b_factors: np.ndarray | None = None,
303
+ chain_index: np.ndarray | None = None,
304
+ remark: str | None = None,
305
+ parents: Sequence[str] | None = None,
306
+ parents_chain_index: Sequence[int] | None = None,
307
+ ) -> Protein:
308
+ """Assembles a protein from a prediction.
309
+
310
+ Args:
311
+ features: Dictionary holding model inputs.
312
+ result: Dictionary holding model outputs.
313
+ b_factors: (Optional) B-factors to use for the protein.
314
+ chain_index: (Optional) Chain indices for multi-chain predictions
315
+ remark: (Optional) Remark about the prediction
316
+ parents: (Optional) List of template names
317
+ Returns:
318
+ A protein instance.
319
+ """
320
+ return Protein(
321
+ aatype=features["aatype"],
322
+ atom_positions=result["final_atom_positions"],
323
+ atom_mask=result["final_atom_mask"],
324
+ residue_index=features["residue_index"] + 1,
325
+ b_factors=b_factors if b_factors is not None else np.zeros_like(result["final_atom_mask"]),
326
+ chain_index=chain_index,
327
+ remark=remark,
328
+ parents=parents,
329
+ parents_chain_index=parents_chain_index,
330
+ )
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/residue_constants.py ADDED
@@ -0,0 +1,979 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Constants used in AlphaFold."""
17
+
18
+ import collections
19
+ import copy
20
+ import functools
21
+ from collections.abc import Mapping, Sequence
22
+ from importlib import resources
23
+
24
+ import numpy as np
25
+
26
+
27
+ # Internal import (35fd).
28
+
29
+
30
+ # Distance from one CA to next CA [trans configuration: omega = 180].
31
+ ca_ca = 3.80209737096
32
+
33
+ # Format: The list for each AA type contains chi1, chi2, chi3, chi4 in
34
+ # this order (or a relevant subset from chi1 onwards). ALA and GLY don't have
35
+ # chi angles so their chi angle lists are empty.
36
+ chi_angles_atoms: dict[str, list[list[str]]] = {
37
+ "ALA": [],
38
+ # Chi5 in arginine is always 0 +- 5 degrees, so ignore it.
39
+ "ARG": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"], ["CB", "CG", "CD", "NE"], ["CG", "CD", "NE", "CZ"]],
40
+ "ASN": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "OD1"]],
41
+ "ASP": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "OD1"]],
42
+ "CYS": [["N", "CA", "CB", "SG"]],
43
+ "GLN": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"], ["CB", "CG", "CD", "OE1"]],
44
+ "GLU": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"], ["CB", "CG", "CD", "OE1"]],
45
+ "GLY": [],
46
+ "HIS": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "ND1"]],
47
+ "ILE": [["N", "CA", "CB", "CG1"], ["CA", "CB", "CG1", "CD1"]],
48
+ "LEU": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]],
49
+ "LYS": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"], ["CB", "CG", "CD", "CE"], ["CG", "CD", "CE", "NZ"]],
50
+ "MET": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "SD"], ["CB", "CG", "SD", "CE"]],
51
+ "PHE": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]],
52
+ "PRO": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD"]],
53
+ "SER": [["N", "CA", "CB", "OG"]],
54
+ "THR": [["N", "CA", "CB", "OG1"]],
55
+ "TRP": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]],
56
+ "TYR": [["N", "CA", "CB", "CG"], ["CA", "CB", "CG", "CD1"]],
57
+ "VAL": [["N", "CA", "CB", "CG1"]],
58
+ }
59
+
60
+ # If chi angles given in fixed-length array, this matrix determines how to mask
61
+ # them for each AA type. The order is as per restype_order (see below).
62
+ chi_angles_mask: list[list[float]] = [
63
+ [0.0, 0.0, 0.0, 0.0], # ALA
64
+ [1.0, 1.0, 1.0, 1.0], # ARG
65
+ [1.0, 1.0, 0.0, 0.0], # ASN
66
+ [1.0, 1.0, 0.0, 0.0], # ASP
67
+ [1.0, 0.0, 0.0, 0.0], # CYS
68
+ [1.0, 1.0, 1.0, 0.0], # GLN
69
+ [1.0, 1.0, 1.0, 0.0], # GLU
70
+ [0.0, 0.0, 0.0, 0.0], # GLY
71
+ [1.0, 1.0, 0.0, 0.0], # HIS
72
+ [1.0, 1.0, 0.0, 0.0], # ILE
73
+ [1.0, 1.0, 0.0, 0.0], # LEU
74
+ [1.0, 1.0, 1.0, 1.0], # LYS
75
+ [1.0, 1.0, 1.0, 0.0], # MET
76
+ [1.0, 1.0, 0.0, 0.0], # PHE
77
+ [1.0, 1.0, 0.0, 0.0], # PRO
78
+ [1.0, 0.0, 0.0, 0.0], # SER
79
+ [1.0, 0.0, 0.0, 0.0], # THR
80
+ [1.0, 1.0, 0.0, 0.0], # TRP
81
+ [1.0, 1.0, 0.0, 0.0], # TYR
82
+ [1.0, 0.0, 0.0, 0.0], # VAL
83
+ ]
84
+
85
+ # The following chi angles are pi periodic: they can be rotated by a multiple
86
+ # of pi without affecting the structure.
87
+ chi_pi_periodic: list[list[float]] = [
88
+ [0.0, 0.0, 0.0, 0.0], # ALA
89
+ [0.0, 0.0, 0.0, 0.0], # ARG
90
+ [0.0, 0.0, 0.0, 0.0], # ASN
91
+ [0.0, 1.0, 0.0, 0.0], # ASP
92
+ [0.0, 0.0, 0.0, 0.0], # CYS
93
+ [0.0, 0.0, 0.0, 0.0], # GLN
94
+ [0.0, 0.0, 1.0, 0.0], # GLU
95
+ [0.0, 0.0, 0.0, 0.0], # GLY
96
+ [0.0, 0.0, 0.0, 0.0], # HIS
97
+ [0.0, 0.0, 0.0, 0.0], # ILE
98
+ [0.0, 0.0, 0.0, 0.0], # LEU
99
+ [0.0, 0.0, 0.0, 0.0], # LYS
100
+ [0.0, 0.0, 0.0, 0.0], # MET
101
+ [0.0, 1.0, 0.0, 0.0], # PHE
102
+ [0.0, 0.0, 0.0, 0.0], # PRO
103
+ [0.0, 0.0, 0.0, 0.0], # SER
104
+ [0.0, 0.0, 0.0, 0.0], # THR
105
+ [0.0, 0.0, 0.0, 0.0], # TRP
106
+ [0.0, 1.0, 0.0, 0.0], # TYR
107
+ [0.0, 0.0, 0.0, 0.0], # VAL
108
+ [0.0, 0.0, 0.0, 0.0], # UNK
109
+ ]
110
+
111
+ # Atoms positions relative to the 8 rigid groups, defined by the pre-omega, phi,
112
+ # psi and chi angles:
113
+ # 0: 'backbone group',
114
+ # 1: 'pre-omega-group', (empty)
115
+ # 2: 'phi-group', (currently empty, because it defines only hydrogens)
116
+ # 3: 'psi-group',
117
+ # 4,5,6,7: 'chi1,2,3,4-group'
118
+ # The atom positions are relative to the axis-end-atom of the corresponding
119
+ # rotation axis. The x-axis is in direction of the rotation axis, and the y-axis
120
+ # is defined such that the dihedral-angle-definiting atom (the last entry in
121
+ # chi_angles_atoms above) is in the xy-plane (with a positive y-coordinate).
122
+ # format: [atomname, group_idx, rel_position]
123
+ rigid_group_atom_positions: dict[str, list[tuple[str, int, tuple[float, float, float]]]] = {
124
+ "ALA": [
125
+ ("N", 0, (-0.525, 1.363, 0.000)),
126
+ ("CA", 0, (0.000, 0.000, 0.000)),
127
+ ("C", 0, (1.526, -0.000, -0.000)),
128
+ ("CB", 0, (-0.529, -0.774, -1.205)),
129
+ ("O", 3, (0.627, 1.062, 0.000)),
130
+ ],
131
+ "ARG": [
132
+ ("N", 0, (-0.524, 1.362, -0.000)),
133
+ ("CA", 0, (0.000, 0.000, 0.000)),
134
+ ("C", 0, (1.525, -0.000, -0.000)),
135
+ ("CB", 0, (-0.524, -0.778, -1.209)),
136
+ ("O", 3, (0.626, 1.062, 0.000)),
137
+ ("CG", 4, (0.616, 1.390, -0.000)),
138
+ ("CD", 5, (0.564, 1.414, 0.000)),
139
+ ("NE", 6, (0.539, 1.357, -0.000)),
140
+ ("NH1", 7, (0.206, 2.301, 0.000)),
141
+ ("NH2", 7, (2.078, 0.978, -0.000)),
142
+ ("CZ", 7, (0.758, 1.093, -0.000)),
143
+ ],
144
+ "ASN": [
145
+ ("N", 0, (-0.536, 1.357, 0.000)),
146
+ ("CA", 0, (0.000, 0.000, 0.000)),
147
+ ("C", 0, (1.526, -0.000, -0.000)),
148
+ ("CB", 0, (-0.531, -0.787, -1.200)),
149
+ ("O", 3, (0.625, 1.062, 0.000)),
150
+ ("CG", 4, (0.584, 1.399, 0.000)),
151
+ ("ND2", 5, (0.593, -1.188, 0.001)),
152
+ ("OD1", 5, (0.633, 1.059, 0.000)),
153
+ ],
154
+ "ASP": [
155
+ ("N", 0, (-0.525, 1.362, -0.000)),
156
+ ("CA", 0, (0.000, 0.000, 0.000)),
157
+ ("C", 0, (1.527, 0.000, -0.000)),
158
+ ("CB", 0, (-0.526, -0.778, -1.208)),
159
+ ("O", 3, (0.626, 1.062, -0.000)),
160
+ ("CG", 4, (0.593, 1.398, -0.000)),
161
+ ("OD1", 5, (0.610, 1.091, 0.000)),
162
+ ("OD2", 5, (0.592, -1.101, -0.003)),
163
+ ],
164
+ "CYS": [
165
+ ("N", 0, (-0.522, 1.362, -0.000)),
166
+ ("CA", 0, (0.000, 0.000, 0.000)),
167
+ ("C", 0, (1.524, 0.000, 0.000)),
168
+ ("CB", 0, (-0.519, -0.773, -1.212)),
169
+ ("O", 3, (0.625, 1.062, -0.000)),
170
+ ("SG", 4, (0.728, 1.653, 0.000)),
171
+ ],
172
+ "GLN": [
173
+ ("N", 0, (-0.526, 1.361, -0.000)),
174
+ ("CA", 0, (0.000, 0.000, 0.000)),
175
+ ("C", 0, (1.526, 0.000, 0.000)),
176
+ ("CB", 0, (-0.525, -0.779, -1.207)),
177
+ ("O", 3, (0.626, 1.062, -0.000)),
178
+ ("CG", 4, (0.615, 1.393, 0.000)),
179
+ ("CD", 5, (0.587, 1.399, -0.000)),
180
+ ("NE2", 6, (0.593, -1.189, -0.001)),
181
+ ("OE1", 6, (0.634, 1.060, 0.000)),
182
+ ],
183
+ "GLU": [
184
+ ("N", 0, (-0.528, 1.361, 0.000)),
185
+ ("CA", 0, (0.000, 0.000, 0.000)),
186
+ ("C", 0, (1.526, -0.000, -0.000)),
187
+ ("CB", 0, (-0.526, -0.781, -1.207)),
188
+ ("O", 3, (0.626, 1.062, 0.000)),
189
+ ("CG", 4, (0.615, 1.392, 0.000)),
190
+ ("CD", 5, (0.600, 1.397, 0.000)),
191
+ ("OE1", 6, (0.607, 1.095, -0.000)),
192
+ ("OE2", 6, (0.589, -1.104, -0.001)),
193
+ ],
194
+ "GLY": [
195
+ ("N", 0, (-0.572, 1.337, 0.000)),
196
+ ("CA", 0, (0.000, 0.000, 0.000)),
197
+ ("C", 0, (1.517, -0.000, -0.000)),
198
+ ("O", 3, (0.626, 1.062, -0.000)),
199
+ ],
200
+ "HIS": [
201
+ ("N", 0, (-0.527, 1.360, 0.000)),
202
+ ("CA", 0, (0.000, 0.000, 0.000)),
203
+ ("C", 0, (1.525, 0.000, 0.000)),
204
+ ("CB", 0, (-0.525, -0.778, -1.208)),
205
+ ("O", 3, (0.625, 1.063, 0.000)),
206
+ ("CG", 4, (0.600, 1.370, -0.000)),
207
+ ("CD2", 5, (0.889, -1.021, 0.003)),
208
+ ("ND1", 5, (0.744, 1.160, -0.000)),
209
+ ("CE1", 5, (2.030, 0.851, 0.002)),
210
+ ("NE2", 5, (2.145, -0.466, 0.004)),
211
+ ],
212
+ "ILE": [
213
+ ("N", 0, (-0.493, 1.373, -0.000)),
214
+ ("CA", 0, (0.000, 0.000, 0.000)),
215
+ ("C", 0, (1.527, -0.000, -0.000)),
216
+ ("CB", 0, (-0.536, -0.793, -1.213)),
217
+ ("O", 3, (0.627, 1.062, -0.000)),
218
+ ("CG1", 4, (0.534, 1.437, -0.000)),
219
+ ("CG2", 4, (0.540, -0.785, -1.199)),
220
+ ("CD1", 5, (0.619, 1.391, 0.000)),
221
+ ],
222
+ "LEU": [
223
+ ("N", 0, (-0.520, 1.363, 0.000)),
224
+ ("CA", 0, (0.000, 0.000, 0.000)),
225
+ ("C", 0, (1.525, -0.000, -0.000)),
226
+ ("CB", 0, (-0.522, -0.773, -1.214)),
227
+ ("O", 3, (0.625, 1.063, -0.000)),
228
+ ("CG", 4, (0.678, 1.371, 0.000)),
229
+ ("CD1", 5, (0.530, 1.430, -0.000)),
230
+ ("CD2", 5, (0.535, -0.774, 1.200)),
231
+ ],
232
+ "LYS": [
233
+ ("N", 0, (-0.526, 1.362, -0.000)),
234
+ ("CA", 0, (0.000, 0.000, 0.000)),
235
+ ("C", 0, (1.526, 0.000, 0.000)),
236
+ ("CB", 0, (-0.524, -0.778, -1.208)),
237
+ ("O", 3, (0.626, 1.062, -0.000)),
238
+ ("CG", 4, (0.619, 1.390, 0.000)),
239
+ ("CD", 5, (0.559, 1.417, 0.000)),
240
+ ("CE", 6, (0.560, 1.416, 0.000)),
241
+ ("NZ", 7, (0.554, 1.387, 0.000)),
242
+ ],
243
+ "MET": [
244
+ ("N", 0, (-0.521, 1.364, -0.000)),
245
+ ("CA", 0, (0.000, 0.000, 0.000)),
246
+ ("C", 0, (1.525, 0.000, 0.000)),
247
+ ("CB", 0, (-0.523, -0.776, -1.210)),
248
+ ("O", 3, (0.625, 1.062, -0.000)),
249
+ ("CG", 4, (0.613, 1.391, -0.000)),
250
+ ("SD", 5, (0.703, 1.695, 0.000)),
251
+ ("CE", 6, (0.320, 1.786, -0.000)),
252
+ ],
253
+ "PHE": [
254
+ ("N", 0, (-0.518, 1.363, 0.000)),
255
+ ("CA", 0, (0.000, 0.000, 0.000)),
256
+ ("C", 0, (1.524, 0.000, -0.000)),
257
+ ("CB", 0, (-0.525, -0.776, -1.212)),
258
+ ("O", 3, (0.626, 1.062, -0.000)),
259
+ ("CG", 4, (0.607, 1.377, 0.000)),
260
+ ("CD1", 5, (0.709, 1.195, -0.000)),
261
+ ("CD2", 5, (0.706, -1.196, 0.000)),
262
+ ("CE1", 5, (2.102, 1.198, -0.000)),
263
+ ("CE2", 5, (2.098, -1.201, -0.000)),
264
+ ("CZ", 5, (2.794, -0.003, -0.001)),
265
+ ],
266
+ "PRO": [
267
+ ("N", 0, (-0.566, 1.351, -0.000)),
268
+ ("CA", 0, (0.000, 0.000, 0.000)),
269
+ ("C", 0, (1.527, -0.000, 0.000)),
270
+ ("CB", 0, (-0.546, -0.611, -1.293)),
271
+ ("O", 3, (0.621, 1.066, 0.000)),
272
+ ("CG", 4, (0.382, 1.445, 0.0)),
273
+ # ('CD', 5, (0.427, 1.440, 0.0)),
274
+ ("CD", 5, (0.477, 1.424, 0.0)), # manually made angle 2 degrees larger
275
+ ],
276
+ "SER": [
277
+ ("N", 0, (-0.529, 1.360, -0.000)),
278
+ ("CA", 0, (0.000, 0.000, 0.000)),
279
+ ("C", 0, (1.525, -0.000, -0.000)),
280
+ ("CB", 0, (-0.518, -0.777, -1.211)),
281
+ ("O", 3, (0.626, 1.062, -0.000)),
282
+ ("OG", 4, (0.503, 1.325, 0.000)),
283
+ ],
284
+ "THR": [
285
+ ("N", 0, (-0.517, 1.364, 0.000)),
286
+ ("CA", 0, (0.000, 0.000, 0.000)),
287
+ ("C", 0, (1.526, 0.000, -0.000)),
288
+ ("CB", 0, (-0.516, -0.793, -1.215)),
289
+ ("O", 3, (0.626, 1.062, 0.000)),
290
+ ("CG2", 4, (0.550, -0.718, -1.228)),
291
+ ("OG1", 4, (0.472, 1.353, 0.000)),
292
+ ],
293
+ "TRP": [
294
+ ("N", 0, (-0.521, 1.363, 0.000)),
295
+ ("CA", 0, (0.000, 0.000, 0.000)),
296
+ ("C", 0, (1.525, -0.000, 0.000)),
297
+ ("CB", 0, (-0.523, -0.776, -1.212)),
298
+ ("O", 3, (0.627, 1.062, 0.000)),
299
+ ("CG", 4, (0.609, 1.370, -0.000)),
300
+ ("CD1", 5, (0.824, 1.091, 0.000)),
301
+ ("CD2", 5, (0.854, -1.148, -0.005)),
302
+ ("CE2", 5, (2.186, -0.678, -0.007)),
303
+ ("CE3", 5, (0.622, -2.530, -0.007)),
304
+ ("NE1", 5, (2.140, 0.690, -0.004)),
305
+ ("CH2", 5, (3.028, -2.890, -0.013)),
306
+ ("CZ2", 5, (3.283, -1.543, -0.011)),
307
+ ("CZ3", 5, (1.715, -3.389, -0.011)),
308
+ ],
309
+ "TYR": [
310
+ ("N", 0, (-0.522, 1.362, 0.000)),
311
+ ("CA", 0, (0.000, 0.000, 0.000)),
312
+ ("C", 0, (1.524, -0.000, -0.000)),
313
+ ("CB", 0, (-0.522, -0.776, -1.213)),
314
+ ("O", 3, (0.627, 1.062, -0.000)),
315
+ ("CG", 4, (0.607, 1.382, -0.000)),
316
+ ("CD1", 5, (0.716, 1.195, -0.000)),
317
+ ("CD2", 5, (0.713, -1.194, -0.001)),
318
+ ("CE1", 5, (2.107, 1.200, -0.002)),
319
+ ("CE2", 5, (2.104, -1.201, -0.003)),
320
+ ("OH", 5, (4.168, -0.002, -0.005)),
321
+ ("CZ", 5, (2.791, -0.001, -0.003)),
322
+ ],
323
+ "VAL": [
324
+ ("N", 0, (-0.494, 1.373, -0.000)),
325
+ ("CA", 0, (0.000, 0.000, 0.000)),
326
+ ("C", 0, (1.527, -0.000, -0.000)),
327
+ ("CB", 0, (-0.533, -0.795, -1.213)),
328
+ ("O", 3, (0.627, 1.062, -0.000)),
329
+ ("CG1", 4, (0.540, 1.429, -0.000)),
330
+ ("CG2", 4, (0.533, -0.776, 1.203)),
331
+ ],
332
+ }
333
+
334
+ # A list of atoms (excluding hydrogen) for each AA type. PDB naming convention.
335
+ residue_atoms: dict[str, list[str]] = {
336
+ "ALA": ["C", "CA", "CB", "N", "O"],
337
+ "ARG": ["C", "CA", "CB", "CG", "CD", "CZ", "N", "NE", "O", "NH1", "NH2"],
338
+ "ASP": ["C", "CA", "CB", "CG", "N", "O", "OD1", "OD2"],
339
+ "ASN": ["C", "CA", "CB", "CG", "N", "ND2", "O", "OD1"],
340
+ "CYS": ["C", "CA", "CB", "N", "O", "SG"],
341
+ "GLU": ["C", "CA", "CB", "CG", "CD", "N", "O", "OE1", "OE2"],
342
+ "GLN": ["C", "CA", "CB", "CG", "CD", "N", "NE2", "O", "OE1"],
343
+ "GLY": ["C", "CA", "N", "O"],
344
+ "HIS": ["C", "CA", "CB", "CG", "CD2", "CE1", "N", "ND1", "NE2", "O"],
345
+ "ILE": ["C", "CA", "CB", "CG1", "CG2", "CD1", "N", "O"],
346
+ "LEU": ["C", "CA", "CB", "CG", "CD1", "CD2", "N", "O"],
347
+ "LYS": ["C", "CA", "CB", "CG", "CD", "CE", "N", "NZ", "O"],
348
+ "MET": ["C", "CA", "CB", "CG", "CE", "N", "O", "SD"],
349
+ "PHE": ["C", "CA", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "N", "O"],
350
+ "PRO": ["C", "CA", "CB", "CG", "CD", "N", "O"],
351
+ "SER": ["C", "CA", "CB", "N", "O", "OG"],
352
+ "THR": ["C", "CA", "CB", "CG2", "N", "O", "OG1"],
353
+ "TRP": ["C", "CA", "CB", "CG", "CD1", "CD2", "CE2", "CE3", "CZ2", "CZ3", "CH2", "N", "NE1", "O"],
354
+ "TYR": ["C", "CA", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "N", "O", "OH"],
355
+ "VAL": ["C", "CA", "CB", "CG1", "CG2", "N", "O"],
356
+ }
357
+
358
+ # Naming swaps for ambiguous atom names.
359
+ # Due to symmetries in the amino acids the naming of atoms is ambiguous in
360
+ # 4 of the 20 amino acids.
361
+ # (The LDDT paper lists 7 amino acids as ambiguous, but the naming ambiguities
362
+ # in LEU, VAL and ARG can be resolved by using the 3d constellations of
363
+ # the 'ambiguous' atoms and their neighbours)
364
+ # TODO: ^ interpret this
365
+ residue_atom_renaming_swaps: dict[str, dict[str, str]] = {
366
+ "ASP": {"OD1": "OD2"},
367
+ "GLU": {"OE1": "OE2"},
368
+ "PHE": {"CD1": "CD2", "CE1": "CE2"},
369
+ "TYR": {"CD1": "CD2", "CE1": "CE2"},
370
+ }
371
+
372
+ # Van der Waals radii [Angstroem] of the atoms (from Wikipedia)
373
+ van_der_waals_radius: dict[str, float] = {
374
+ "C": 1.7,
375
+ "N": 1.55,
376
+ "O": 1.52,
377
+ "S": 1.8,
378
+ }
379
+
380
+ Bond = collections.namedtuple("Bond", ["atom1_name", "atom2_name", "length", "stddev"])
381
+ BondAngle = collections.namedtuple(
382
+ "BondAngle",
383
+ ["atom1_name", "atom2_name", "atom3name", "angle_rad", "stddev"],
384
+ )
385
+
386
+
387
+ def map_structure_with_atom_order(in_list: list, first_call: bool = True) -> list:
388
+ # Maps strings in a nested list structure to their corresponding index in atom_order
389
+ if first_call:
390
+ in_list = copy.deepcopy(in_list)
391
+ for i in range(len(in_list)):
392
+ if isinstance(in_list[i], list):
393
+ in_list[i] = map_structure_with_atom_order(in_list[i], first_call=False)
394
+ elif isinstance(in_list[i], str):
395
+ in_list[i] = atom_order[in_list[i]]
396
+ else:
397
+ raise TypeError("Unexpected type when mapping nested lists!")
398
+ return in_list
399
+
400
+
401
+ @functools.cache
402
+ def load_stereo_chemical_props() -> tuple[
403
+ Mapping[str, list[Bond]],
404
+ Mapping[str, list[Bond]],
405
+ Mapping[str, list[BondAngle]],
406
+ ]:
407
+ """Load stereo_chemical_props.txt into a nice structure.
408
+
409
+ Load literature values for bond lengths and bond angles and translate bond angles into the length of the opposite
410
+ edge of the triangle ("residue_virtual_bonds").
411
+
412
+ Returns:
413
+ residue_bonds: dict that maps resname --> list of Bond tuples residue_virtual_bonds: dict that maps resname -->
414
+ list of Bond tuples residue_bond_angles: dict that maps resname --> list of BondAngle tuples
415
+ """
416
+ # TODO: this file should be downloaded in a setup script
417
+ stereo_chemical_props = resources.read_text("openfold.resources", "stereo_chemical_props.txt")
418
+
419
+ lines_iter = iter(stereo_chemical_props.splitlines())
420
+ # Load bond lengths.
421
+ residue_bonds: dict[str, list[Bond]] = {}
422
+ next(lines_iter) # Skip header line.
423
+ for line in lines_iter:
424
+ if line.strip() == "-":
425
+ break
426
+ bond, resname, bond_length, stddev = line.split()
427
+ atom1, atom2 = bond.split("-")
428
+ if resname not in residue_bonds:
429
+ residue_bonds[resname] = []
430
+ residue_bonds[resname].append(Bond(atom1, atom2, float(bond_length), float(stddev)))
431
+ residue_bonds["UNK"] = []
432
+
433
+ # Load bond angles.
434
+ residue_bond_angles: dict[str, list[BondAngle]] = {}
435
+ next(lines_iter) # Skip empty line.
436
+ next(lines_iter) # Skip header line.
437
+ for line in lines_iter:
438
+ if line.strip() == "-":
439
+ break
440
+ bond, resname, angle_degree, stddev_degree = line.split()
441
+ atom1, atom2, atom3 = bond.split("-")
442
+ if resname not in residue_bond_angles:
443
+ residue_bond_angles[resname] = []
444
+ residue_bond_angles[resname].append(
445
+ BondAngle(
446
+ atom1,
447
+ atom2,
448
+ atom3,
449
+ float(angle_degree) / 180.0 * np.pi,
450
+ float(stddev_degree) / 180.0 * np.pi,
451
+ )
452
+ )
453
+ residue_bond_angles["UNK"] = []
454
+
455
+ def make_bond_key(atom1_name: str, atom2_name: str) -> str:
456
+ """Unique key to lookup bonds."""
457
+ return "-".join(sorted([atom1_name, atom2_name]))
458
+
459
+ # Translate bond angles into distances ("virtual bonds").
460
+ residue_virtual_bonds: dict[str, list[Bond]] = {}
461
+ for resname, bond_angles in residue_bond_angles.items():
462
+ # Create a fast lookup dict for bond lengths.
463
+ bond_cache: dict[str, Bond] = {}
464
+ for b in residue_bonds[resname]:
465
+ bond_cache[make_bond_key(b.atom1_name, b.atom2_name)] = b
466
+ residue_virtual_bonds[resname] = []
467
+ for ba in bond_angles:
468
+ bond1 = bond_cache[make_bond_key(ba.atom1_name, ba.atom2_name)]
469
+ bond2 = bond_cache[make_bond_key(ba.atom2_name, ba.atom3name)]
470
+
471
+ # Compute distance between atom1 and atom3 using the law of cosines
472
+ # c^2 = a^2 + b^2 - 2ab*cos(gamma).
473
+ gamma = ba.angle_rad
474
+ length = np.sqrt(bond1.length**2 + bond2.length**2 - 2 * bond1.length * bond2.length * np.cos(gamma))
475
+
476
+ # Propagation of uncertainty assuming uncorrelated errors.
477
+ dl_outer = 0.5 / length
478
+ dl_dgamma = (2 * bond1.length * bond2.length * np.sin(gamma)) * dl_outer
479
+ dl_db1 = (2 * bond1.length - 2 * bond2.length * np.cos(gamma)) * dl_outer
480
+ dl_db2 = (2 * bond2.length - 2 * bond1.length * np.cos(gamma)) * dl_outer
481
+ stddev = np.sqrt(
482
+ (dl_dgamma * ba.stddev) ** 2 + (dl_db1 * bond1.stddev) ** 2 + (dl_db2 * bond2.stddev) ** 2
483
+ )
484
+ residue_virtual_bonds[resname].append(Bond(ba.atom1_name, ba.atom3name, length, stddev))
485
+
486
+ return (residue_bonds, residue_virtual_bonds, residue_bond_angles)
487
+
488
+
489
+ # Between-residue bond lengths for general bonds (first element) and for Proline
490
+ # (second element).
491
+ between_res_bond_length_c_n: tuple[float, float] = (1.329, 1.341)
492
+ between_res_bond_length_stddev_c_n: tuple[float, float] = (0.014, 0.016)
493
+
494
+ # Between-residue cos_angles.
495
+ between_res_cos_angles_c_n_ca: tuple[float, float] = (-0.5203, 0.0353) # degrees: 121.352 +- 2.315
496
+ between_res_cos_angles_ca_c_n: tuple[float, float] = (-0.4473, 0.0311) # degrees: 116.568 +- 1.995
497
+
498
+ # This mapping is used when we need to store atom data in a format that requires
499
+ # fixed atom data size for every residue (e.g. a numpy array).
500
+ atom_types: list[str] = [
501
+ "N",
502
+ "CA",
503
+ "C",
504
+ "CB",
505
+ "O",
506
+ "CG",
507
+ "CG1",
508
+ "CG2",
509
+ "OG",
510
+ "OG1",
511
+ "SG",
512
+ "CD",
513
+ "CD1",
514
+ "CD2",
515
+ "ND1",
516
+ "ND2",
517
+ "OD1",
518
+ "OD2",
519
+ "SD",
520
+ "CE",
521
+ "CE1",
522
+ "CE2",
523
+ "CE3",
524
+ "NE",
525
+ "NE1",
526
+ "NE2",
527
+ "OE1",
528
+ "OE2",
529
+ "CH2",
530
+ "NH1",
531
+ "NH2",
532
+ "OH",
533
+ "CZ",
534
+ "CZ2",
535
+ "CZ3",
536
+ "NZ",
537
+ "OXT",
538
+ ]
539
+ atom_order: dict[str, int] = {atom_type: i for i, atom_type in enumerate(atom_types)}
540
+ atom_type_num = len(atom_types) # := 37.
541
+
542
+ # A compact atom encoding with 14 columns
543
+ # pylint: disable=line-too-long
544
+ restype_name_to_atom14_names: dict[str, list[str]] = {
545
+ "ALA": ["N", "CA", "C", "O", "CB", "", "", "", "", "", "", "", "", ""],
546
+ "ARG": ["N", "CA", "C", "O", "CB", "CG", "CD", "NE", "CZ", "NH1", "NH2", "", "", ""],
547
+ "ASN": ["N", "CA", "C", "O", "CB", "CG", "OD1", "ND2", "", "", "", "", "", ""],
548
+ "ASP": ["N", "CA", "C", "O", "CB", "CG", "OD1", "OD2", "", "", "", "", "", ""],
549
+ "CYS": ["N", "CA", "C", "O", "CB", "SG", "", "", "", "", "", "", "", ""],
550
+ "GLN": ["N", "CA", "C", "O", "CB", "CG", "CD", "OE1", "NE2", "", "", "", "", ""],
551
+ "GLU": ["N", "CA", "C", "O", "CB", "CG", "CD", "OE1", "OE2", "", "", "", "", ""],
552
+ "GLY": ["N", "CA", "C", "O", "", "", "", "", "", "", "", "", "", ""],
553
+ "HIS": ["N", "CA", "C", "O", "CB", "CG", "ND1", "CD2", "CE1", "NE2", "", "", "", ""],
554
+ "ILE": ["N", "CA", "C", "O", "CB", "CG1", "CG2", "CD1", "", "", "", "", "", ""],
555
+ "LEU": ["N", "CA", "C", "O", "CB", "CG", "CD1", "CD2", "", "", "", "", "", ""],
556
+ "LYS": ["N", "CA", "C", "O", "CB", "CG", "CD", "CE", "NZ", "", "", "", "", ""],
557
+ "MET": ["N", "CA", "C", "O", "CB", "CG", "SD", "CE", "", "", "", "", "", ""],
558
+ "PHE": ["N", "CA", "C", "O", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "", "", ""],
559
+ "PRO": ["N", "CA", "C", "O", "CB", "CG", "CD", "", "", "", "", "", "", ""],
560
+ "SER": ["N", "CA", "C", "O", "CB", "OG", "", "", "", "", "", "", "", ""],
561
+ "THR": ["N", "CA", "C", "O", "CB", "OG1", "CG2", "", "", "", "", "", "", ""],
562
+ "TRP": ["N", "CA", "C", "O", "CB", "CG", "CD1", "CD2", "NE1", "CE2", "CE3", "CZ2", "CZ3", "CH2"],
563
+ "TYR": ["N", "CA", "C", "O", "CB", "CG", "CD1", "CD2", "CE1", "CE2", "CZ", "OH", "", ""],
564
+ "VAL": ["N", "CA", "C", "O", "CB", "CG1", "CG2", "", "", "", "", "", "", ""],
565
+ "UNK": ["", "", "", "", "", "", "", "", "", "", "", "", "", ""],
566
+ }
567
+ # pylint: enable=line-too-long
568
+
569
+
570
+ # This is the standard residue order when coding AA type as a number.
571
+ # Reproduce it by taking 3-letter AA codes and sorting them alphabetically.
572
+ restypes: list[str] = [
573
+ "A",
574
+ "R",
575
+ "N",
576
+ "D",
577
+ "C",
578
+ "Q",
579
+ "E",
580
+ "G",
581
+ "H",
582
+ "I",
583
+ "L",
584
+ "K",
585
+ "M",
586
+ "F",
587
+ "P",
588
+ "S",
589
+ "T",
590
+ "W",
591
+ "Y",
592
+ "V",
593
+ ]
594
+ restype_order: dict[str, int] = {restype: i for i, restype in enumerate(restypes)}
595
+ restype_num = len(restypes) # := 20.
596
+ unk_restype_index = restype_num # Catch-all index for unknown restypes.
597
+
598
+ restypes_with_x: list[str] = restypes + ["X"]
599
+ restype_order_with_x: dict[str, int] = {restype: i for i, restype in enumerate(restypes_with_x)}
600
+
601
+
602
+ def sequence_to_onehot(sequence: str, mapping: Mapping[str, int], map_unknown_to_x: bool = False) -> np.ndarray:
603
+ """Maps the given sequence into a one-hot encoded matrix.
604
+
605
+ Args:
606
+ sequence: An amino acid sequence.
607
+ mapping: A dictionary mapping amino acids to integers.
608
+ map_unknown_to_x: If True, any amino acid that is not in the mapping will be
609
+ mapped to the unknown amino acid 'X'. If the mapping doesn't contain amino acid 'X', an error will be thrown.
610
+ If False, any amino acid not in the mapping will throw an error.
611
+
612
+ Returns:
613
+ A numpy array of shape (seq_len, num_unique_aas) with one-hot encoding of the sequence.
614
+
615
+ Raises:
616
+ ValueError: If the mapping doesn't contain values from 0 to
617
+ num_unique_aas - 1 without any gaps.
618
+ """
619
+ num_entries = max(mapping.values()) + 1
620
+
621
+ if sorted(set(mapping.values())) != list(range(num_entries)):
622
+ raise ValueError(
623
+ "The mapping must have values from 0 to num_unique_aas-1 without any gaps. Got: %s"
624
+ % sorted(mapping.values())
625
+ )
626
+
627
+ one_hot_arr = np.zeros((len(sequence), num_entries), dtype=np.int32)
628
+
629
+ for aa_index, aa_type in enumerate(sequence):
630
+ if map_unknown_to_x:
631
+ if aa_type.isalpha() and aa_type.isupper():
632
+ aa_id = mapping.get(aa_type, mapping["X"])
633
+ else:
634
+ raise ValueError(f"Invalid character in the sequence: {aa_type}")
635
+ else:
636
+ aa_id = mapping[aa_type]
637
+ one_hot_arr[aa_index, aa_id] = 1
638
+
639
+ return one_hot_arr
640
+
641
+
642
+ restype_1to3: dict[str, str] = {
643
+ "A": "ALA",
644
+ "R": "ARG",
645
+ "N": "ASN",
646
+ "D": "ASP",
647
+ "C": "CYS",
648
+ "Q": "GLN",
649
+ "E": "GLU",
650
+ "G": "GLY",
651
+ "H": "HIS",
652
+ "I": "ILE",
653
+ "L": "LEU",
654
+ "K": "LYS",
655
+ "M": "MET",
656
+ "F": "PHE",
657
+ "P": "PRO",
658
+ "S": "SER",
659
+ "T": "THR",
660
+ "W": "TRP",
661
+ "Y": "TYR",
662
+ "V": "VAL",
663
+ }
664
+
665
+
666
+ # NB: restype_3to1 differs from Bio.PDB.protein_letters_3to1 by being a simple
667
+ # 1-to-1 mapping of 3 letter names to one letter names. The latter contains
668
+ # many more, and less common, three letter names as keys and maps many of these
669
+ # to the same one letter name (including 'X' and 'U' which we don't use here).
670
+ restype_3to1: dict[str, str] = {v: k for k, v in restype_1to3.items()}
671
+
672
+ # Define a restype name for all unknown residues.
673
+ unk_restype = "UNK"
674
+
675
+ resnames: list[str] = [restype_1to3[r] for r in restypes] + [unk_restype]
676
+ resname_to_idx: dict[str, int] = {resname: i for i, resname in enumerate(resnames)}
677
+
678
+
679
+ # The mapping here uses hhblits convention, so that B is mapped to D, J and O
680
+ # are mapped to X, U is mapped to C, and Z is mapped to E. Other than that the
681
+ # remaining 20 amino acids are kept in alphabetical order.
682
+ # There are 2 non-amino acid codes, X (representing any amino acid) and
683
+ # "-" representing a missing amino acid in an alignment. The id for these
684
+ # codes is put at the end (20 and 21) so that they can easily be ignored if
685
+ # desired.
686
+ HHBLITS_AA_TO_ID: dict[str, int] = {
687
+ "A": 0,
688
+ "B": 2,
689
+ "C": 1,
690
+ "D": 2,
691
+ "E": 3,
692
+ "F": 4,
693
+ "G": 5,
694
+ "H": 6,
695
+ "I": 7,
696
+ "J": 20,
697
+ "K": 8,
698
+ "L": 9,
699
+ "M": 10,
700
+ "N": 11,
701
+ "O": 20,
702
+ "P": 12,
703
+ "Q": 13,
704
+ "R": 14,
705
+ "S": 15,
706
+ "T": 16,
707
+ "U": 1,
708
+ "V": 17,
709
+ "W": 18,
710
+ "X": 20,
711
+ "Y": 19,
712
+ "Z": 3,
713
+ "-": 21,
714
+ }
715
+
716
+ # Partial inversion of HHBLITS_AA_TO_ID.
717
+ ID_TO_HHBLITS_AA: dict[int, str] = {
718
+ 0: "A",
719
+ 1: "C", # Also U.
720
+ 2: "D", # Also B.
721
+ 3: "E", # Also Z.
722
+ 4: "F",
723
+ 5: "G",
724
+ 6: "H",
725
+ 7: "I",
726
+ 8: "K",
727
+ 9: "L",
728
+ 10: "M",
729
+ 11: "N",
730
+ 12: "P",
731
+ 13: "Q",
732
+ 14: "R",
733
+ 15: "S",
734
+ 16: "T",
735
+ 17: "V",
736
+ 18: "W",
737
+ 19: "Y",
738
+ 20: "X", # Includes J and O.
739
+ 21: "-",
740
+ }
741
+
742
+ restypes_with_x_and_gap: list[str] = restypes + ["X", "-"]
743
+ MAP_HHBLITS_AATYPE_TO_OUR_AATYPE: tuple[int, ...] = tuple(
744
+ restypes_with_x_and_gap.index(ID_TO_HHBLITS_AA[i]) for i in range(len(restypes_with_x_and_gap))
745
+ )
746
+
747
+
748
+ def _make_standard_atom_mask() -> np.ndarray:
749
+ """Returns [num_res_types, num_atom_types] mask array."""
750
+ # +1 to account for unknown (all 0s).
751
+ mask = np.zeros([restype_num + 1, atom_type_num], dtype=np.int32)
752
+ for restype, restype_letter in enumerate(restypes):
753
+ restype_name = restype_1to3[restype_letter]
754
+ atom_names = residue_atoms[restype_name]
755
+ for atom_name in atom_names:
756
+ atom_type = atom_order[atom_name]
757
+ mask[restype, atom_type] = 1
758
+ return mask
759
+
760
+
761
+ STANDARD_ATOM_MASK = _make_standard_atom_mask()
762
+
763
+
764
+ # A one hot representation for the first and second atoms defining the axis
765
+ # of rotation for each chi-angle in each residue.
766
+ def chi_angle_atom(atom_index: int) -> np.ndarray:
767
+ """Define chi-angle rigid groups via one-hot representations."""
768
+ chi_angles_index = {}
769
+ one_hots = []
770
+
771
+ for k, v in chi_angles_atoms.items():
772
+ indices = [atom_types.index(s[atom_index]) for s in v]
773
+ indices.extend([-1] * (4 - len(indices)))
774
+ chi_angles_index[k] = indices
775
+
776
+ for r in restypes:
777
+ res3 = restype_1to3[r]
778
+ one_hot = np.eye(atom_type_num)[chi_angles_index[res3]]
779
+ one_hots.append(one_hot)
780
+
781
+ one_hots.append(np.zeros([4, atom_type_num])) # Add zeros for residue `X`.
782
+ one_hot = np.stack(one_hots, axis=0)
783
+ one_hot = np.transpose(one_hot, [0, 2, 1])
784
+
785
+ return one_hot
786
+
787
+
788
+ chi_atom_1_one_hot = chi_angle_atom(1)
789
+ chi_atom_2_one_hot = chi_angle_atom(2)
790
+
791
+ # An array like chi_angles_atoms but using indices rather than names.
792
+ chi_angles_atom_indices_list: list[list[list[str]]] = [chi_angles_atoms[restype_1to3[r]] for r in restypes]
793
+ chi_angles_atom_indices_ours: list = map_structure_with_atom_order(chi_angles_atom_indices_list)
794
+ chi_angles_atom_indices = np.array(
795
+ [chi_atoms + ([[0, 0, 0, 0]] * (4 - len(chi_atoms))) for chi_atoms in chi_angles_atom_indices_list]
796
+ )
797
+
798
+ # Mapping from (res_name, atom_name) pairs to the atom's chi group index
799
+ # and atom index within that group.
800
+ chi_groups_for_atom: dict[tuple[str, str], list[tuple[int, int]]] = collections.defaultdict(list)
801
+ for res_name, chi_angle_atoms_for_res in chi_angles_atoms.items():
802
+ for chi_group_i, chi_group in enumerate(chi_angle_atoms_for_res):
803
+ for atom_i, atom in enumerate(chi_group):
804
+ chi_groups_for_atom[(res_name, atom)].append((chi_group_i, atom_i))
805
+ chi_groups_for_atom = dict(chi_groups_for_atom)
806
+
807
+
808
+ def _make_rigid_transformation_4x4(ex: np.ndarray, ey: np.ndarray, translation: np.ndarray) -> np.ndarray:
809
+ """Create a rigid 4x4 transformation matrix from two axes and transl."""
810
+ # Normalize ex.
811
+ ex_normalized = ex / np.linalg.norm(ex)
812
+
813
+ # make ey perpendicular to ex
814
+ ey_normalized = ey - np.dot(ey, ex_normalized) * ex_normalized
815
+ ey_normalized /= np.linalg.norm(ey_normalized)
816
+
817
+ # compute ez as cross product
818
+ eznorm = np.cross(ex_normalized, ey_normalized)
819
+ m = np.stack([ex_normalized, ey_normalized, eznorm, translation]).transpose()
820
+ m = np.concatenate([m, [[0.0, 0.0, 0.0, 1.0]]], axis=0)
821
+ return m
822
+
823
+
824
+ # create an array with (restype, atomtype) --> rigid_group_idx
825
+ # and an array with (restype, atomtype, coord) for the atom positions
826
+ # and compute affine transformation matrices (4,4) from one rigid group to the
827
+ # previous group
828
+ restype_atom37_to_rigid_group = np.zeros([21, 37], dtype=int)
829
+ restype_atom37_mask = np.zeros([21, 37], dtype=np.float32)
830
+ restype_atom37_rigid_group_positions = np.zeros([21, 37, 3], dtype=np.float32)
831
+ restype_atom14_to_rigid_group = np.zeros([21, 14], dtype=int)
832
+ restype_atom14_mask = np.zeros([21, 14], dtype=np.float32)
833
+ restype_atom14_rigid_group_positions = np.zeros([21, 14, 3], dtype=np.float32)
834
+ restype_rigid_group_default_frame = np.zeros([21, 8, 4, 4], dtype=np.float32)
835
+
836
+
837
+ def _make_rigid_group_constants() -> None:
838
+ """Fill the arrays above."""
839
+ for restype, restype_letter in enumerate(restypes):
840
+ resname = restype_1to3[restype_letter]
841
+ for atomname, group_idx, atom_position in rigid_group_atom_positions[resname]:
842
+ atomtype = atom_order[atomname]
843
+ restype_atom37_to_rigid_group[restype, atomtype] = group_idx
844
+ restype_atom37_mask[restype, atomtype] = 1
845
+ restype_atom37_rigid_group_positions[restype, atomtype, :] = atom_position
846
+
847
+ atom14idx = restype_name_to_atom14_names[resname].index(atomname)
848
+ restype_atom14_to_rigid_group[restype, atom14idx] = group_idx
849
+ restype_atom14_mask[restype, atom14idx] = 1
850
+ restype_atom14_rigid_group_positions[restype, atom14idx, :] = atom_position
851
+
852
+ for restype, restype_letter in enumerate(restypes):
853
+ resname = restype_1to3[restype_letter]
854
+ atom_positions: dict[str, np.ndarray] = {
855
+ name: np.array(pos) for name, _, pos in rigid_group_atom_positions[resname]
856
+ }
857
+
858
+ # backbone to backbone is the identity transform
859
+ restype_rigid_group_default_frame[restype, 0, :, :] = np.eye(4)
860
+
861
+ # pre-omega-frame to backbone (currently dummy identity matrix)
862
+ restype_rigid_group_default_frame[restype, 1, :, :] = np.eye(4)
863
+
864
+ # phi-frame to backbone
865
+ mat = _make_rigid_transformation_4x4(
866
+ ex=atom_positions["N"] - atom_positions["CA"],
867
+ ey=np.array([1.0, 0.0, 0.0]),
868
+ translation=atom_positions["N"],
869
+ )
870
+ restype_rigid_group_default_frame[restype, 2, :, :] = mat
871
+
872
+ # psi-frame to backbone
873
+ mat = _make_rigid_transformation_4x4(
874
+ ex=atom_positions["C"] - atom_positions["CA"],
875
+ ey=atom_positions["CA"] - atom_positions["N"],
876
+ translation=atom_positions["C"],
877
+ )
878
+ restype_rigid_group_default_frame[restype, 3, :, :] = mat
879
+
880
+ # chi1-frame to backbone
881
+ if chi_angles_mask[restype][0]:
882
+ base_atom_names = chi_angles_atoms[resname][0]
883
+ base_atom_positions = [atom_positions[name] for name in base_atom_names]
884
+ mat = _make_rigid_transformation_4x4(
885
+ ex=base_atom_positions[2] - base_atom_positions[1],
886
+ ey=base_atom_positions[0] - base_atom_positions[1],
887
+ translation=base_atom_positions[2],
888
+ )
889
+ restype_rigid_group_default_frame[restype, 4, :, :] = mat
890
+
891
+ # chi2-frame to chi1-frame
892
+ # chi3-frame to chi2-frame
893
+ # chi4-frame to chi3-frame
894
+ # luckily all rotation axes for the next frame start at (0,0,0) of the
895
+ # previous frame
896
+ for chi_idx in range(1, 4):
897
+ if chi_angles_mask[restype][chi_idx]:
898
+ axis_end_atom_name = chi_angles_atoms[resname][chi_idx][2]
899
+ axis_end_atom_position = atom_positions[axis_end_atom_name]
900
+ mat = _make_rigid_transformation_4x4(
901
+ ex=axis_end_atom_position,
902
+ ey=np.array([-1.0, 0.0, 0.0]),
903
+ translation=axis_end_atom_position,
904
+ )
905
+ restype_rigid_group_default_frame[restype, 4 + chi_idx, :, :] = mat
906
+
907
+
908
+ _make_rigid_group_constants()
909
+
910
+
911
+ def make_atom14_dists_bounds(
912
+ overlap_tolerance: float = 1.5,
913
+ bond_length_tolerance_factor: int = 15,
914
+ ) -> dict[str, np.ndarray]:
915
+ """compute upper and lower bounds for bonds to assess violations."""
916
+ restype_atom14_bond_lower_bound = np.zeros([21, 14, 14], np.float32)
917
+ restype_atom14_bond_upper_bound = np.zeros([21, 14, 14], np.float32)
918
+ restype_atom14_bond_stddev = np.zeros([21, 14, 14], np.float32)
919
+ residue_bonds, residue_virtual_bonds, _ = load_stereo_chemical_props()
920
+ for restype, restype_letter in enumerate(restypes):
921
+ resname = restype_1to3[restype_letter]
922
+ atom_list = restype_name_to_atom14_names[resname]
923
+
924
+ # create lower and upper bounds for clashes
925
+ for atom1_idx, atom1_name in enumerate(atom_list):
926
+ if not atom1_name:
927
+ continue
928
+ atom1_radius = van_der_waals_radius[atom1_name[0]]
929
+ for atom2_idx, atom2_name in enumerate(atom_list):
930
+ if (not atom2_name) or atom1_idx == atom2_idx:
931
+ continue
932
+ atom2_radius = van_der_waals_radius[atom2_name[0]]
933
+ lower = atom1_radius + atom2_radius - overlap_tolerance
934
+ upper = 1e10
935
+ restype_atom14_bond_lower_bound[restype, atom1_idx, atom2_idx] = lower
936
+ restype_atom14_bond_lower_bound[restype, atom2_idx, atom1_idx] = lower
937
+ restype_atom14_bond_upper_bound[restype, atom1_idx, atom2_idx] = upper
938
+ restype_atom14_bond_upper_bound[restype, atom2_idx, atom1_idx] = upper
939
+
940
+ # overwrite lower and upper bounds for bonds and angles
941
+ for b in residue_bonds[resname] + residue_virtual_bonds[resname]:
942
+ atom1_idx = atom_list.index(b.atom1_name)
943
+ atom2_idx = atom_list.index(b.atom2_name)
944
+ lower = b.length - bond_length_tolerance_factor * b.stddev
945
+ upper = b.length + bond_length_tolerance_factor * b.stddev
946
+ restype_atom14_bond_lower_bound[restype, atom1_idx, atom2_idx] = lower
947
+ restype_atom14_bond_lower_bound[restype, atom2_idx, atom1_idx] = lower
948
+ restype_atom14_bond_upper_bound[restype, atom1_idx, atom2_idx] = upper
949
+ restype_atom14_bond_upper_bound[restype, atom2_idx, atom1_idx] = upper
950
+ restype_atom14_bond_stddev[restype, atom1_idx, atom2_idx] = b.stddev
951
+ restype_atom14_bond_stddev[restype, atom2_idx, atom1_idx] = b.stddev
952
+ return {
953
+ "lower_bound": restype_atom14_bond_lower_bound, # shape (21,14,14)
954
+ "upper_bound": restype_atom14_bond_upper_bound, # shape (21,14,14)
955
+ "stddev": restype_atom14_bond_stddev, # shape (21,14,14)
956
+ }
957
+
958
+
959
+ restype_atom14_ambiguous_atoms = np.zeros((21, 14), dtype=np.float32)
960
+ restype_atom14_ambiguous_atoms_swap_idx: np.ndarray = np.tile(np.arange(14, dtype=int), (21, 1))
961
+
962
+
963
+ def _make_atom14_ambiguity_feats() -> None:
964
+ for res, pairs in residue_atom_renaming_swaps.items():
965
+ res_idx = restype_order[restype_3to1[res]]
966
+ for atom1, atom2 in pairs.items():
967
+ atom1_idx = restype_name_to_atom14_names[res].index(atom1)
968
+ atom2_idx = restype_name_to_atom14_names[res].index(atom2)
969
+ restype_atom14_ambiguous_atoms[res_idx, atom1_idx] = 1
970
+ restype_atom14_ambiguous_atoms[res_idx, atom2_idx] = 1
971
+ restype_atom14_ambiguous_atoms_swap_idx[res_idx, atom1_idx] = atom2_idx
972
+ restype_atom14_ambiguous_atoms_swap_idx[res_idx, atom2_idx] = atom1_idx
973
+
974
+
975
+ _make_atom14_ambiguity_feats()
976
+
977
+
978
+ def aatype_to_str_sequence(aatype: Sequence[int]) -> str:
979
+ return "".join([restypes_with_x[aatype[i]] for i in range(len(aatype))])
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/esm/openfold_utils/rigid_utils.py ADDED
@@ -0,0 +1,1243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from __future__ import annotations
17
+
18
+ from collections.abc import Callable, Sequence
19
+ from functools import cache
20
+ from typing import Any
21
+
22
+ import numpy as np
23
+ import torch
24
+
25
+
26
+ def rot_matmul(a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
27
+ """
28
+ Performs matrix multiplication of two rotation matrix tensors. Written out by hand to avoid AMP downcasting.
29
+
30
+ Args:
31
+ a: [*, 3, 3] left multiplicand
32
+ b: [*, 3, 3] right multiplicand
33
+ Returns:
34
+ The product ab
35
+ """
36
+
37
+ def row_mul(i: int) -> torch.Tensor:
38
+ return torch.stack(
39
+ [
40
+ a[..., i, 0] * b[..., 0, 0] + a[..., i, 1] * b[..., 1, 0] + a[..., i, 2] * b[..., 2, 0],
41
+ a[..., i, 0] * b[..., 0, 1] + a[..., i, 1] * b[..., 1, 1] + a[..., i, 2] * b[..., 2, 1],
42
+ a[..., i, 0] * b[..., 0, 2] + a[..., i, 1] * b[..., 1, 2] + a[..., i, 2] * b[..., 2, 2],
43
+ ],
44
+ dim=-1,
45
+ )
46
+
47
+ return torch.stack(
48
+ [
49
+ row_mul(0),
50
+ row_mul(1),
51
+ row_mul(2),
52
+ ],
53
+ dim=-2,
54
+ )
55
+
56
+
57
+ def rot_vec_mul(r: torch.Tensor, t: torch.Tensor) -> torch.Tensor:
58
+ """
59
+ Applies a rotation to a vector. Written out by hand to avoid transfer to avoid AMP downcasting.
60
+
61
+ Args:
62
+ r: [*, 3, 3] rotation matrices
63
+ t: [*, 3] coordinate tensors
64
+ Returns:
65
+ [*, 3] rotated coordinates
66
+ """
67
+ x, y, z = torch.unbind(t, dim=-1)
68
+ return torch.stack(
69
+ [
70
+ r[..., 0, 0] * x + r[..., 0, 1] * y + r[..., 0, 2] * z,
71
+ r[..., 1, 0] * x + r[..., 1, 1] * y + r[..., 1, 2] * z,
72
+ r[..., 2, 0] * x + r[..., 2, 1] * y + r[..., 2, 2] * z,
73
+ ],
74
+ dim=-1,
75
+ )
76
+
77
+
78
+ @cache
79
+ def identity_rot_mats(
80
+ batch_dims: tuple[int, ...],
81
+ dtype: torch.dtype | None = None,
82
+ device: torch.device | None = None,
83
+ requires_grad: bool = True,
84
+ ) -> torch.Tensor:
85
+ rots = torch.eye(3, dtype=dtype, device=device, requires_grad=requires_grad)
86
+ rots = rots.view(*((1,) * len(batch_dims)), 3, 3)
87
+ rots = rots.expand(*batch_dims, -1, -1)
88
+ rots = rots.contiguous()
89
+
90
+ return rots
91
+
92
+
93
+ @cache
94
+ def identity_trans(
95
+ batch_dims: tuple[int, ...],
96
+ dtype: torch.dtype | None = None,
97
+ device: torch.device | None = None,
98
+ requires_grad: bool = True,
99
+ ) -> torch.Tensor:
100
+ trans = torch.zeros((*batch_dims, 3), dtype=dtype, device=device, requires_grad=requires_grad)
101
+ return trans
102
+
103
+
104
+ @cache
105
+ def identity_quats(
106
+ batch_dims: tuple[int, ...],
107
+ dtype: torch.dtype | None = None,
108
+ device: torch.device | None = None,
109
+ requires_grad: bool = True,
110
+ ) -> torch.Tensor:
111
+ quat = torch.zeros((*batch_dims, 4), dtype=dtype, device=device, requires_grad=requires_grad)
112
+
113
+ with torch.no_grad():
114
+ quat[..., 0] = 1
115
+
116
+ return quat
117
+
118
+
119
+ _quat_elements: list[str] = ["a", "b", "c", "d"]
120
+ _qtr_keys: list[str] = [l1 + l2 for l1 in _quat_elements for l2 in _quat_elements]
121
+ _qtr_ind_dict: dict[str, int] = {key: ind for ind, key in enumerate(_qtr_keys)}
122
+
123
+
124
+ def _to_mat(pairs: list[tuple[str, int]]) -> np.ndarray:
125
+ mat = np.zeros((4, 4))
126
+ for key, value in pairs:
127
+ ind = _qtr_ind_dict[key]
128
+ mat[ind // 4][ind % 4] = value
129
+
130
+ return mat
131
+
132
+
133
+ _QTR_MAT = np.zeros((4, 4, 3, 3))
134
+ _QTR_MAT[..., 0, 0] = _to_mat([("aa", 1), ("bb", 1), ("cc", -1), ("dd", -1)])
135
+ _QTR_MAT[..., 0, 1] = _to_mat([("bc", 2), ("ad", -2)])
136
+ _QTR_MAT[..., 0, 2] = _to_mat([("bd", 2), ("ac", 2)])
137
+ _QTR_MAT[..., 1, 0] = _to_mat([("bc", 2), ("ad", 2)])
138
+ _QTR_MAT[..., 1, 1] = _to_mat([("aa", 1), ("bb", -1), ("cc", 1), ("dd", -1)])
139
+ _QTR_MAT[..., 1, 2] = _to_mat([("cd", 2), ("ab", -2)])
140
+ _QTR_MAT[..., 2, 0] = _to_mat([("bd", 2), ("ac", -2)])
141
+ _QTR_MAT[..., 2, 1] = _to_mat([("cd", 2), ("ab", 2)])
142
+ _QTR_MAT[..., 2, 2] = _to_mat([("aa", 1), ("bb", -1), ("cc", -1), ("dd", 1)])
143
+
144
+
145
+ def quat_to_rot(quat: torch.Tensor) -> torch.Tensor:
146
+ """
147
+ Converts a quaternion to a rotation matrix.
148
+
149
+ Args:
150
+ quat: [*, 4] quaternions
151
+ Returns:
152
+ [*, 3, 3] rotation matrices
153
+ """
154
+ # [*, 4, 4]
155
+ quat = quat[..., None] * quat[..., None, :]
156
+
157
+ # [4, 4, 3, 3]
158
+ mat = _get_quat("_QTR_MAT", dtype=quat.dtype, device=quat.device)
159
+
160
+ # [*, 4, 4, 3, 3]
161
+ shaped_qtr_mat = mat.view((1,) * len(quat.shape[:-2]) + mat.shape)
162
+ quat = quat[..., None, None] * shaped_qtr_mat
163
+
164
+ # [*, 3, 3]
165
+ return torch.sum(quat, dim=(-3, -4))
166
+
167
+
168
+ def rot_to_quat(rot: torch.Tensor) -> torch.Tensor:
169
+ if rot.shape[-2:] != (3, 3):
170
+ raise ValueError("Input rotation is incorrectly shaped")
171
+
172
+ [[xx, xy, xz], [yx, yy, yz], [zx, zy, zz]] = [[rot[..., i, j] for j in range(3)] for i in range(3)]
173
+
174
+ k = [
175
+ [
176
+ xx + yy + zz,
177
+ zy - yz,
178
+ xz - zx,
179
+ yx - xy,
180
+ ],
181
+ [
182
+ zy - yz,
183
+ xx - yy - zz,
184
+ xy + yx,
185
+ xz + zx,
186
+ ],
187
+ [
188
+ xz - zx,
189
+ xy + yx,
190
+ yy - xx - zz,
191
+ yz + zy,
192
+ ],
193
+ [
194
+ yx - xy,
195
+ xz + zx,
196
+ yz + zy,
197
+ zz - xx - yy,
198
+ ],
199
+ ]
200
+
201
+ _, vectors = torch.linalg.eigh((1.0 / 3.0) * torch.stack([torch.stack(t, dim=-1) for t in k], dim=-2))
202
+ return vectors[..., -1]
203
+
204
+
205
+ _QUAT_MULTIPLY = np.zeros((4, 4, 4))
206
+ _QUAT_MULTIPLY[:, :, 0] = [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]]
207
+
208
+ _QUAT_MULTIPLY[:, :, 1] = [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, -1, 0]]
209
+
210
+ _QUAT_MULTIPLY[:, :, 2] = [[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0], [0, 1, 0, 0]]
211
+
212
+ _QUAT_MULTIPLY[:, :, 3] = [[0, 0, 0, 1], [0, 0, 1, 0], [0, -1, 0, 0], [1, 0, 0, 0]]
213
+
214
+ _QUAT_MULTIPLY_BY_VEC = _QUAT_MULTIPLY[:, 1:, :]
215
+
216
+ _CACHED_QUATS: dict[str, np.ndarray] = {
217
+ "_QTR_MAT": _QTR_MAT,
218
+ "_QUAT_MULTIPLY": _QUAT_MULTIPLY,
219
+ "_QUAT_MULTIPLY_BY_VEC": _QUAT_MULTIPLY_BY_VEC,
220
+ }
221
+
222
+
223
+ @cache
224
+ def _get_quat(quat_key: str, dtype: torch.dtype, device: torch.device) -> torch.Tensor:
225
+ return torch.tensor(_CACHED_QUATS[quat_key], dtype=dtype, device=device)
226
+
227
+
228
+ def quat_multiply(quat1: torch.Tensor, quat2: torch.Tensor) -> torch.Tensor:
229
+ """Multiply a quaternion by another quaternion."""
230
+ mat = _get_quat("_QUAT_MULTIPLY", dtype=quat1.dtype, device=quat1.device)
231
+ reshaped_mat = mat.view((1,) * len(quat1.shape[:-1]) + mat.shape)
232
+ return torch.sum(reshaped_mat * quat1[..., :, None, None] * quat2[..., None, :, None], dim=(-3, -2))
233
+
234
+
235
+ def quat_multiply_by_vec(quat: torch.Tensor, vec: torch.Tensor) -> torch.Tensor:
236
+ """Multiply a quaternion by a pure-vector quaternion."""
237
+ mat = _get_quat("_QUAT_MULTIPLY_BY_VEC", dtype=quat.dtype, device=quat.device)
238
+ reshaped_mat = mat.view((1,) * len(quat.shape[:-1]) + mat.shape)
239
+ return torch.sum(reshaped_mat * quat[..., :, None, None] * vec[..., None, :, None], dim=(-3, -2))
240
+
241
+
242
+ def invert_rot_mat(rot_mat: torch.Tensor) -> torch.Tensor:
243
+ return rot_mat.transpose(-1, -2)
244
+
245
+
246
+ def invert_quat(quat: torch.Tensor) -> torch.Tensor:
247
+ quat_prime = quat.clone()
248
+ quat_prime[..., 1:] *= -1
249
+ inv = quat_prime / torch.sum(quat**2, dim=-1, keepdim=True)
250
+ return inv
251
+
252
+
253
+ class Rotation:
254
+ """
255
+ A 3D rotation. Depending on how the object is initialized, the rotation is represented by either a rotation matrix
256
+ or a quaternion, though both formats are made available by helper functions. To simplify gradient computation, the
257
+ underlying format of the rotation cannot be changed in-place. Like Rigid, the class is designed to mimic the
258
+ behavior of a torch Tensor, almost as if each Rotation object were a tensor of rotations, in one format or another.
259
+ """
260
+
261
+ def __init__(
262
+ self,
263
+ rot_mats: torch.Tensor | None = None,
264
+ quats: torch.Tensor | None = None,
265
+ normalize_quats: bool = True,
266
+ ):
267
+ """
268
+ Args:
269
+ rot_mats:
270
+ A [*, 3, 3] rotation matrix tensor. Mutually exclusive with quats
271
+ quats:
272
+ A [*, 4] quaternion. Mutually exclusive with rot_mats. If normalize_quats is not True, must be a unit
273
+ quaternion
274
+ normalize_quats:
275
+ If quats is specified, whether to normalize quats
276
+ """
277
+ if (rot_mats is None and quats is None) or (rot_mats is not None and quats is not None):
278
+ raise ValueError("Exactly one input argument must be specified")
279
+
280
+ if (rot_mats is not None and rot_mats.shape[-2:] != (3, 3)) or (quats is not None and quats.shape[-1] != 4):
281
+ raise ValueError("Incorrectly shaped rotation matrix or quaternion")
282
+
283
+ # Force full-precision
284
+ if quats is not None:
285
+ quats = quats.to(dtype=torch.float32)
286
+ if rot_mats is not None:
287
+ rot_mats = rot_mats.to(dtype=torch.float32)
288
+
289
+ if quats is not None and normalize_quats:
290
+ quats = quats / torch.linalg.norm(quats, dim=-1, keepdim=True)
291
+
292
+ self._rot_mats = rot_mats
293
+ self._quats = quats
294
+
295
+ @staticmethod
296
+ def identity(
297
+ shape,
298
+ dtype: torch.dtype | None = None,
299
+ device: torch.device | None = None,
300
+ requires_grad: bool = True,
301
+ fmt: str = "quat",
302
+ ) -> Rotation:
303
+ """
304
+ Returns an identity Rotation.
305
+
306
+ Args:
307
+ shape:
308
+ The "shape" of the resulting Rotation object. See documentation for the shape property
309
+ dtype:
310
+ The torch dtype for the rotation
311
+ device:
312
+ The torch device for the new rotation
313
+ requires_grad:
314
+ Whether the underlying tensors in the new rotation object should require gradient computation
315
+ fmt:
316
+ One of "quat" or "rot_mat". Determines the underlying format of the new object's rotation
317
+ Returns:
318
+ A new identity rotation
319
+ """
320
+ if fmt == "rot_mat":
321
+ rot_mats = identity_rot_mats(
322
+ shape,
323
+ dtype,
324
+ device,
325
+ requires_grad,
326
+ )
327
+ return Rotation(rot_mats=rot_mats, quats=None)
328
+ elif fmt == "quat":
329
+ quats = identity_quats(shape, dtype, device, requires_grad)
330
+ return Rotation(rot_mats=None, quats=quats, normalize_quats=False)
331
+ else:
332
+ raise ValueError(f"Invalid format: f{fmt}")
333
+
334
+ # Magic methods
335
+
336
+ def __getitem__(self, index: Any) -> Rotation:
337
+ """
338
+ Allows torch-style indexing over the virtual shape of the rotation object. See documentation for the shape
339
+ property.
340
+
341
+ Args:
342
+ index:
343
+ A torch index. E.g. (1, 3, 2), or (slice(None,))
344
+ Returns:
345
+ The indexed rotation
346
+ """
347
+ if type(index) is not tuple:
348
+ index = (index,)
349
+
350
+ if self._rot_mats is not None:
351
+ rot_mats = self._rot_mats[index + (slice(None), slice(None))]
352
+ return Rotation(rot_mats=rot_mats)
353
+ elif self._quats is not None:
354
+ quats = self._quats[index + (slice(None),)]
355
+ return Rotation(quats=quats, normalize_quats=False)
356
+ else:
357
+ raise ValueError("Both rotations are None")
358
+
359
+ def __mul__(self, right: torch.Tensor) -> Rotation:
360
+ """
361
+ Pointwise left multiplication of the rotation with a tensor. Can be used to e.g. mask the Rotation.
362
+
363
+ Args:
364
+ right:
365
+ The tensor multiplicand
366
+ Returns:
367
+ The product
368
+ """
369
+ if not (isinstance(right, torch.Tensor)):
370
+ raise TypeError("The other multiplicand must be a Tensor")
371
+
372
+ if self._rot_mats is not None:
373
+ rot_mats = self._rot_mats * right[..., None, None]
374
+ return Rotation(rot_mats=rot_mats, quats=None)
375
+ elif self._quats is not None:
376
+ quats = self._quats * right[..., None]
377
+ return Rotation(rot_mats=None, quats=quats, normalize_quats=False)
378
+ else:
379
+ raise ValueError("Both rotations are None")
380
+
381
+ def __rmul__(self, left: torch.Tensor) -> Rotation:
382
+ """
383
+ Reverse pointwise multiplication of the rotation with a tensor.
384
+
385
+ Args:
386
+ left:
387
+ The left multiplicand
388
+ Returns:
389
+ The product
390
+ """
391
+ return self.__mul__(left)
392
+
393
+ # Properties
394
+
395
+ @property
396
+ def shape(self) -> torch.Size:
397
+ """
398
+ Returns the virtual shape of the rotation object. This shape is defined as the batch dimensions of the
399
+ underlying rotation matrix or quaternion. If the Rotation was initialized with a [10, 3, 3] rotation matrix
400
+ tensor, for example, the resulting shape would be [10].
401
+
402
+ Returns:
403
+ The virtual shape of the rotation object
404
+ """
405
+ if self._rot_mats is not None:
406
+ return self._rot_mats.shape[:-2]
407
+ elif self._quats is not None:
408
+ return self._quats.shape[:-1]
409
+ else:
410
+ raise ValueError("Both rotations are None")
411
+
412
+ @property
413
+ def dtype(self) -> torch.dtype:
414
+ """
415
+ Returns the dtype of the underlying rotation.
416
+
417
+ Returns:
418
+ The dtype of the underlying rotation
419
+ """
420
+ if self._rot_mats is not None:
421
+ return self._rot_mats.dtype
422
+ elif self._quats is not None:
423
+ return self._quats.dtype
424
+ else:
425
+ raise ValueError("Both rotations are None")
426
+
427
+ @property
428
+ def device(self) -> torch.device:
429
+ """
430
+ The device of the underlying rotation
431
+
432
+ Returns:
433
+ The device of the underlying rotation
434
+ """
435
+ if self._rot_mats is not None:
436
+ return self._rot_mats.device
437
+ elif self._quats is not None:
438
+ return self._quats.device
439
+ else:
440
+ raise ValueError("Both rotations are None")
441
+
442
+ @property
443
+ def requires_grad(self) -> bool:
444
+ """
445
+ Returns the requires_grad property of the underlying rotation
446
+
447
+ Returns:
448
+ The requires_grad property of the underlying tensor
449
+ """
450
+ if self._rot_mats is not None:
451
+ return self._rot_mats.requires_grad
452
+ elif self._quats is not None:
453
+ return self._quats.requires_grad
454
+ else:
455
+ raise ValueError("Both rotations are None")
456
+
457
+ def get_rot_mats(self) -> torch.Tensor:
458
+ """
459
+ Returns the underlying rotation as a rotation matrix tensor.
460
+
461
+ Returns:
462
+ The rotation as a rotation matrix tensor
463
+ """
464
+ if self._rot_mats is not None:
465
+ return self._rot_mats
466
+ elif self._quats is not None:
467
+ return quat_to_rot(self._quats)
468
+ else:
469
+ raise ValueError("Both rotations are None")
470
+
471
+ def get_quats(self) -> torch.Tensor:
472
+ """
473
+ Returns the underlying rotation as a quaternion tensor.
474
+
475
+ Depending on whether the Rotation was initialized with a quaternion, this function may call torch.linalg.eigh.
476
+
477
+ Returns:
478
+ The rotation as a quaternion tensor.
479
+ """
480
+ if self._rot_mats is not None:
481
+ return rot_to_quat(self._rot_mats)
482
+ elif self._quats is not None:
483
+ return self._quats
484
+ else:
485
+ raise ValueError("Both rotations are None")
486
+
487
+ def get_cur_rot(self) -> torch.Tensor:
488
+ """
489
+ Return the underlying rotation in its current form
490
+
491
+ Returns:
492
+ The stored rotation
493
+ """
494
+ if self._rot_mats is not None:
495
+ return self._rot_mats
496
+ elif self._quats is not None:
497
+ return self._quats
498
+ else:
499
+ raise ValueError("Both rotations are None")
500
+
501
+ # Rotation functions
502
+
503
+ def compose_q_update_vec(self, q_update_vec: torch.Tensor, normalize_quats: bool = True) -> Rotation:
504
+ """
505
+ Returns a new quaternion Rotation after updating the current object's underlying rotation with a quaternion
506
+ update, formatted as a [*, 3] tensor whose final three columns represent x, y, z such that (1, x, y, z) is the
507
+ desired (not necessarily unit) quaternion update.
508
+
509
+ Args:
510
+ q_update_vec:
511
+ A [*, 3] quaternion update tensor
512
+ normalize_quats:
513
+ Whether to normalize the output quaternion
514
+ Returns:
515
+ An updated Rotation
516
+ """
517
+ quats = self.get_quats()
518
+ new_quats = quats + quat_multiply_by_vec(quats, q_update_vec)
519
+ return Rotation(
520
+ rot_mats=None,
521
+ quats=new_quats,
522
+ normalize_quats=normalize_quats,
523
+ )
524
+
525
+ def compose_r(self, r: Rotation) -> Rotation:
526
+ """
527
+ Compose the rotation matrices of the current Rotation object with those of another.
528
+
529
+ Args:
530
+ r:
531
+ An update rotation object
532
+ Returns:
533
+ An updated rotation object
534
+ """
535
+ r1 = self.get_rot_mats()
536
+ r2 = r.get_rot_mats()
537
+ new_rot_mats = rot_matmul(r1, r2)
538
+ return Rotation(rot_mats=new_rot_mats, quats=None)
539
+
540
+ def compose_q(self, r: Rotation, normalize_quats: bool = True) -> Rotation:
541
+ """
542
+ Compose the quaternions of the current Rotation object with those of another.
543
+
544
+ Depending on whether either Rotation was initialized with quaternions, this function may call
545
+ torch.linalg.eigh.
546
+
547
+ Args:
548
+ r:
549
+ An update rotation object
550
+ Returns:
551
+ An updated rotation object
552
+ """
553
+ q1 = self.get_quats()
554
+ q2 = r.get_quats()
555
+ new_quats = quat_multiply(q1, q2)
556
+ return Rotation(rot_mats=None, quats=new_quats, normalize_quats=normalize_quats)
557
+
558
+ def apply(self, pts: torch.Tensor) -> torch.Tensor:
559
+ """
560
+ Apply the current Rotation as a rotation matrix to a set of 3D coordinates.
561
+
562
+ Args:
563
+ pts:
564
+ A [*, 3] set of points
565
+ Returns:
566
+ [*, 3] rotated points
567
+ """
568
+ rot_mats = self.get_rot_mats()
569
+ return rot_vec_mul(rot_mats, pts)
570
+
571
+ def invert_apply(self, pts: torch.Tensor) -> torch.Tensor:
572
+ """
573
+ The inverse of the apply() method.
574
+
575
+ Args:
576
+ pts:
577
+ A [*, 3] set of points
578
+ Returns:
579
+ [*, 3] inverse-rotated points
580
+ """
581
+ rot_mats = self.get_rot_mats()
582
+ inv_rot_mats = invert_rot_mat(rot_mats)
583
+ return rot_vec_mul(inv_rot_mats, pts)
584
+
585
+ def invert(self) -> Rotation:
586
+ """
587
+ Returns the inverse of the current Rotation.
588
+
589
+ Returns:
590
+ The inverse of the current Rotation
591
+ """
592
+ if self._rot_mats is not None:
593
+ return Rotation(rot_mats=invert_rot_mat(self._rot_mats), quats=None)
594
+ elif self._quats is not None:
595
+ return Rotation(
596
+ rot_mats=None,
597
+ quats=invert_quat(self._quats),
598
+ normalize_quats=False,
599
+ )
600
+ else:
601
+ raise ValueError("Both rotations are None")
602
+
603
+ # "Tensor" stuff
604
+
605
+ def unsqueeze(self, dim: int) -> Rotation:
606
+ """
607
+ Analogous to torch.unsqueeze. The dimension is relative to the shape of the Rotation object.
608
+
609
+ Args:
610
+ dim: A positive or negative dimension index.
611
+ Returns:
612
+ The unsqueezed Rotation.
613
+ """
614
+ if dim >= len(self.shape):
615
+ raise ValueError("Invalid dimension")
616
+
617
+ if self._rot_mats is not None:
618
+ rot_mats = self._rot_mats.unsqueeze(dim if dim >= 0 else dim - 2)
619
+ return Rotation(rot_mats=rot_mats, quats=None)
620
+ elif self._quats is not None:
621
+ quats = self._quats.unsqueeze(dim if dim >= 0 else dim - 1)
622
+ return Rotation(rot_mats=None, quats=quats, normalize_quats=False)
623
+ else:
624
+ raise ValueError("Both rotations are None")
625
+
626
+ @staticmethod
627
+ def cat(rs: Sequence[Rotation], dim: int) -> Rotation:
628
+ """
629
+ Concatenates rotations along one of the batch dimensions. Analogous to torch.cat().
630
+
631
+ Note that the output of this operation is always a rotation matrix, regardless of the format of input
632
+ rotations.
633
+
634
+ Args:
635
+ rs:
636
+ A list of rotation objects
637
+ dim:
638
+ The dimension along which the rotations should be concatenated
639
+ Returns:
640
+ A concatenated Rotation object in rotation matrix format
641
+ """
642
+ rot_mats = torch.cat(
643
+ [r.get_rot_mats() for r in rs],
644
+ dim=dim if dim >= 0 else dim - 2,
645
+ )
646
+
647
+ return Rotation(rot_mats=rot_mats, quats=None)
648
+
649
+ def map_tensor_fn(self, fn: Callable[[torch.Tensor], torch.Tensor]) -> Rotation:
650
+ """
651
+ Apply a Tensor -> Tensor function to underlying rotation tensors, mapping over the rotation dimension(s). Can
652
+ be used e.g. to sum out a one-hot batch dimension.
653
+
654
+ Args:
655
+ fn:
656
+ A Tensor -> Tensor function to be mapped over the Rotation
657
+ Returns:
658
+ The transformed Rotation object
659
+ """
660
+ if self._rot_mats is not None:
661
+ rot_mats = self._rot_mats.view(self._rot_mats.shape[:-2] + (9,))
662
+ rot_mats = torch.stack(list(map(fn, torch.unbind(rot_mats, dim=-1))), dim=-1)
663
+ rot_mats = rot_mats.view(rot_mats.shape[:-1] + (3, 3))
664
+ return Rotation(rot_mats=rot_mats, quats=None)
665
+ elif self._quats is not None:
666
+ quats = torch.stack(list(map(fn, torch.unbind(self._quats, dim=-1))), dim=-1)
667
+ return Rotation(rot_mats=None, quats=quats, normalize_quats=False)
668
+ else:
669
+ raise ValueError("Both rotations are None")
670
+
671
+ def cuda(self) -> Rotation:
672
+ """
673
+ Analogous to the cuda() method of torch Tensors
674
+
675
+ Returns:
676
+ A copy of the Rotation in CUDA memory
677
+ """
678
+ if self._rot_mats is not None:
679
+ return Rotation(rot_mats=self._rot_mats.cuda(), quats=None)
680
+ elif self._quats is not None:
681
+ return Rotation(rot_mats=None, quats=self._quats.cuda(), normalize_quats=False)
682
+ else:
683
+ raise ValueError("Both rotations are None")
684
+
685
+ def to(self, device: torch.device | None, dtype: torch.dtype | None) -> Rotation:
686
+ """
687
+ Analogous to the to() method of torch Tensors
688
+
689
+ Args:
690
+ device:
691
+ A torch device
692
+ dtype:
693
+ A torch dtype
694
+ Returns:
695
+ A copy of the Rotation using the new device and dtype
696
+ """
697
+ if self._rot_mats is not None:
698
+ return Rotation(
699
+ rot_mats=self._rot_mats.to(device=device, dtype=dtype),
700
+ quats=None,
701
+ )
702
+ elif self._quats is not None:
703
+ return Rotation(
704
+ rot_mats=None,
705
+ quats=self._quats.to(device=device, dtype=dtype),
706
+ normalize_quats=False,
707
+ )
708
+ else:
709
+ raise ValueError("Both rotations are None")
710
+
711
+ def detach(self) -> Rotation:
712
+ """
713
+ Returns a copy of the Rotation whose underlying Tensor has been detached from its torch graph.
714
+
715
+ Returns:
716
+ A copy of the Rotation whose underlying Tensor has been detached from its torch graph
717
+ """
718
+ if self._rot_mats is not None:
719
+ return Rotation(rot_mats=self._rot_mats.detach(), quats=None)
720
+ elif self._quats is not None:
721
+ return Rotation(
722
+ rot_mats=None,
723
+ quats=self._quats.detach(),
724
+ normalize_quats=False,
725
+ )
726
+ else:
727
+ raise ValueError("Both rotations are None")
728
+
729
+
730
+ class Rigid:
731
+ """
732
+ A class representing a rigid transformation. Little more than a wrapper around two objects: a Rotation object and a
733
+ [*, 3] translation Designed to behave approximately like a single torch tensor with the shape of the shared batch
734
+ dimensions of its component parts.
735
+ """
736
+
737
+ def __init__(self, rots: Rotation | None, trans: torch.Tensor | None):
738
+ """
739
+ Args:
740
+ rots: A [*, 3, 3] rotation tensor
741
+ trans: A corresponding [*, 3] translation tensor
742
+ """
743
+ # (we need device, dtype, etc. from at least one input)
744
+
745
+ batch_dims, dtype, device, requires_grad = None, None, None, None
746
+ if trans is not None:
747
+ batch_dims = trans.shape[:-1]
748
+ dtype = trans.dtype
749
+ device = trans.device
750
+ requires_grad = trans.requires_grad
751
+ elif rots is not None:
752
+ batch_dims = rots.shape
753
+ dtype = rots.dtype
754
+ device = rots.device
755
+ requires_grad = rots.requires_grad
756
+ else:
757
+ raise ValueError("At least one input argument must be specified")
758
+
759
+ if rots is None:
760
+ rots = Rotation.identity(
761
+ batch_dims,
762
+ dtype,
763
+ device,
764
+ requires_grad,
765
+ )
766
+ elif trans is None:
767
+ trans = identity_trans(
768
+ batch_dims,
769
+ dtype,
770
+ device,
771
+ requires_grad,
772
+ )
773
+
774
+ assert rots is not None
775
+ assert trans is not None
776
+
777
+ if (rots.shape != trans.shape[:-1]) or (rots.device != trans.device):
778
+ raise ValueError("Rots and trans incompatible")
779
+
780
+ # Force full precision. Happens to the rotations automatically.
781
+ trans = trans.to(dtype=torch.float32)
782
+
783
+ self._rots = rots
784
+ self._trans = trans
785
+
786
+ @staticmethod
787
+ def identity(
788
+ shape: tuple[int, ...],
789
+ dtype: torch.dtype | None = None,
790
+ device: torch.device | None = None,
791
+ requires_grad: bool = True,
792
+ fmt: str = "quat",
793
+ ) -> Rigid:
794
+ """
795
+ Constructs an identity transformation.
796
+
797
+ Args:
798
+ shape:
799
+ The desired shape
800
+ dtype:
801
+ The dtype of both internal tensors
802
+ device:
803
+ The device of both internal tensors
804
+ requires_grad:
805
+ Whether grad should be enabled for the internal tensors
806
+ Returns:
807
+ The identity transformation
808
+ """
809
+ return Rigid(
810
+ Rotation.identity(shape, dtype, device, requires_grad, fmt=fmt),
811
+ identity_trans(shape, dtype, device, requires_grad),
812
+ )
813
+
814
+ def __getitem__(self, index: Any) -> Rigid:
815
+ """
816
+ Indexes the affine transformation with PyTorch-style indices. The index is applied to the shared dimensions of
817
+ both the rotation and the translation.
818
+
819
+ E.g.::
820
+
821
+ r = Rotation(rot_mats=torch.rand(10, 10, 3, 3), quats=None) t = Rigid(r, torch.rand(10, 10, 3)) indexed =
822
+ t[3, 4:6] assert(indexed.shape == (2,)) assert(indexed.get_rots().shape == (2,))
823
+ assert(indexed.get_trans().shape == (2, 3))
824
+
825
+ Args:
826
+ index: A standard torch tensor index. E.g. 8, (10, None, 3),
827
+ or (3, slice(0, 1, None))
828
+ Returns:
829
+ The indexed tensor
830
+ """
831
+ if type(index) is not tuple:
832
+ index = (index,)
833
+
834
+ return Rigid(
835
+ self._rots[index],
836
+ self._trans[index + (slice(None),)],
837
+ )
838
+
839
+ def __mul__(self, right: torch.Tensor) -> Rigid:
840
+ """
841
+ Pointwise left multiplication of the transformation with a tensor. Can be used to e.g. mask the Rigid.
842
+
843
+ Args:
844
+ right:
845
+ The tensor multiplicand
846
+ Returns:
847
+ The product
848
+ """
849
+ if not (isinstance(right, torch.Tensor)):
850
+ raise TypeError("The other multiplicand must be a Tensor")
851
+
852
+ new_rots = self._rots * right
853
+ new_trans = self._trans * right[..., None]
854
+
855
+ return Rigid(new_rots, new_trans)
856
+
857
+ def __rmul__(self, left: torch.Tensor) -> Rigid:
858
+ """
859
+ Reverse pointwise multiplication of the transformation with a tensor.
860
+
861
+ Args:
862
+ left:
863
+ The left multiplicand
864
+ Returns:
865
+ The product
866
+ """
867
+ return self.__mul__(left)
868
+
869
+ @property
870
+ def shape(self) -> torch.Size:
871
+ """
872
+ Returns the shape of the shared dimensions of the rotation and the translation.
873
+
874
+ Returns:
875
+ The shape of the transformation
876
+ """
877
+ return self._trans.shape[:-1]
878
+
879
+ @property
880
+ def device(self) -> torch.device:
881
+ """
882
+ Returns the device on which the Rigid's tensors are located.
883
+
884
+ Returns:
885
+ The device on which the Rigid's tensors are located
886
+ """
887
+ return self._trans.device
888
+
889
+ def get_rots(self) -> Rotation:
890
+ """
891
+ Getter for the rotation.
892
+
893
+ Returns:
894
+ The rotation object
895
+ """
896
+ return self._rots
897
+
898
+ def get_trans(self) -> torch.Tensor:
899
+ """
900
+ Getter for the translation.
901
+
902
+ Returns:
903
+ The stored translation
904
+ """
905
+ return self._trans
906
+
907
+ def compose_q_update_vec(self, q_update_vec: torch.Tensor) -> Rigid:
908
+ """
909
+ Composes the transformation with a quaternion update vector of shape [*, 6], where the final 6 columns
910
+ represent the x, y, and z values of a quaternion of form (1, x, y, z) followed by a 3D translation.
911
+
912
+ Args:
913
+ q_vec: The quaternion update vector.
914
+ Returns:
915
+ The composed transformation.
916
+ """
917
+ q_vec, t_vec = q_update_vec[..., :3], q_update_vec[..., 3:]
918
+ new_rots = self._rots.compose_q_update_vec(q_vec)
919
+
920
+ trans_update = self._rots.apply(t_vec)
921
+ new_translation = self._trans + trans_update
922
+
923
+ return Rigid(new_rots, new_translation)
924
+
925
+ def compose(self, r: Rigid) -> Rigid:
926
+ """
927
+ Composes the current rigid object with another.
928
+
929
+ Args:
930
+ r:
931
+ Another Rigid object
932
+ Returns:
933
+ The composition of the two transformations
934
+ """
935
+ new_rot = self._rots.compose_r(r._rots)
936
+ new_trans = self._rots.apply(r._trans) + self._trans
937
+ return Rigid(new_rot, new_trans)
938
+
939
+ def apply(self, pts: torch.Tensor) -> torch.Tensor:
940
+ """
941
+ Applies the transformation to a coordinate tensor.
942
+
943
+ Args:
944
+ pts: A [*, 3] coordinate tensor.
945
+ Returns:
946
+ The transformed points.
947
+ """
948
+ rotated = self._rots.apply(pts)
949
+ return rotated + self._trans
950
+
951
+ def invert_apply(self, pts: torch.Tensor) -> torch.Tensor:
952
+ """
953
+ Applies the inverse of the transformation to a coordinate tensor.
954
+
955
+ Args:
956
+ pts: A [*, 3] coordinate tensor
957
+ Returns:
958
+ The transformed points.
959
+ """
960
+ pts = pts - self._trans
961
+ return self._rots.invert_apply(pts)
962
+
963
+ def invert(self) -> Rigid:
964
+ """
965
+ Inverts the transformation.
966
+
967
+ Returns:
968
+ The inverse transformation.
969
+ """
970
+ rot_inv = self._rots.invert()
971
+ trn_inv = rot_inv.apply(self._trans)
972
+
973
+ return Rigid(rot_inv, -1 * trn_inv)
974
+
975
+ def map_tensor_fn(self, fn: Callable[[torch.Tensor], torch.Tensor]) -> Rigid:
976
+ """
977
+ Apply a Tensor -> Tensor function to underlying translation and rotation tensors, mapping over the
978
+ translation/rotation dimensions respectively.
979
+
980
+ Args:
981
+ fn:
982
+ A Tensor -> Tensor function to be mapped over the Rigid
983
+ Returns:
984
+ The transformed Rigid object
985
+ """
986
+ new_rots = self._rots.map_tensor_fn(fn)
987
+ new_trans = torch.stack(list(map(fn, torch.unbind(self._trans, dim=-1))), dim=-1)
988
+
989
+ return Rigid(new_rots, new_trans)
990
+
991
+ def to_tensor_4x4(self) -> torch.Tensor:
992
+ """
993
+ Converts a transformation to a homogeneous transformation tensor.
994
+
995
+ Returns:
996
+ A [*, 4, 4] homogeneous transformation tensor
997
+ """
998
+ tensor = self._trans.new_zeros((*self.shape, 4, 4))
999
+ tensor[..., :3, :3] = self._rots.get_rot_mats()
1000
+ tensor[..., :3, 3] = self._trans
1001
+ tensor[..., 3, 3] = 1
1002
+ return tensor
1003
+
1004
+ @staticmethod
1005
+ def from_tensor_4x4(t: torch.Tensor) -> Rigid:
1006
+ """
1007
+ Constructs a transformation from a homogeneous transformation tensor.
1008
+
1009
+ Args:
1010
+ t: [*, 4, 4] homogeneous transformation tensor
1011
+ Returns:
1012
+ T object with shape [*]
1013
+ """
1014
+ if t.shape[-2:] != (4, 4):
1015
+ raise ValueError("Incorrectly shaped input tensor")
1016
+
1017
+ rots = Rotation(rot_mats=t[..., :3, :3], quats=None)
1018
+ trans = t[..., :3, 3]
1019
+
1020
+ return Rigid(rots, trans)
1021
+
1022
+ def to_tensor_7(self) -> torch.Tensor:
1023
+ """
1024
+ Converts a transformation to a tensor with 7 final columns, four for the quaternion followed by three for the
1025
+ translation.
1026
+
1027
+ Returns:
1028
+ A [*, 7] tensor representation of the transformation
1029
+ """
1030
+ tensor = self._trans.new_zeros((*self.shape, 7))
1031
+ tensor[..., :4] = self._rots.get_quats()
1032
+ tensor[..., 4:] = self._trans
1033
+
1034
+ return tensor
1035
+
1036
+ @staticmethod
1037
+ def from_tensor_7(t: torch.Tensor, normalize_quats: bool = False) -> Rigid:
1038
+ if t.shape[-1] != 7:
1039
+ raise ValueError("Incorrectly shaped input tensor")
1040
+
1041
+ quats, trans = t[..., :4], t[..., 4:]
1042
+
1043
+ rots = Rotation(rot_mats=None, quats=quats, normalize_quats=normalize_quats)
1044
+
1045
+ return Rigid(rots, trans)
1046
+
1047
+ @staticmethod
1048
+ def from_3_points(
1049
+ p_neg_x_axis: torch.Tensor, origin: torch.Tensor, p_xy_plane: torch.Tensor, eps: float = 1e-8
1050
+ ) -> Rigid:
1051
+ """
1052
+ Implements algorithm 21. Constructs transformations from sets of 3 points using the Gram-Schmidt algorithm.
1053
+
1054
+ Args:
1055
+ p_neg_x_axis: [*, 3] coordinates
1056
+ origin: [*, 3] coordinates used as frame origins
1057
+ p_xy_plane: [*, 3] coordinates
1058
+ eps: Small epsilon value
1059
+ Returns:
1060
+ A transformation object of shape [*]
1061
+ """
1062
+ p_neg_x_axis_unbound = torch.unbind(p_neg_x_axis, dim=-1)
1063
+ origin_unbound = torch.unbind(origin, dim=-1)
1064
+ p_xy_plane_unbound = torch.unbind(p_xy_plane, dim=-1)
1065
+
1066
+ e0 = [c1 - c2 for c1, c2 in zip(origin_unbound, p_neg_x_axis_unbound)]
1067
+ e1 = [c1 - c2 for c1, c2 in zip(p_xy_plane_unbound, origin_unbound)]
1068
+
1069
+ denom = torch.sqrt(sum(c * c for c in e0) + eps * torch.ones_like(e0[0]))
1070
+ e0 = [c / denom for c in e0]
1071
+ dot = sum((c1 * c2 for c1, c2 in zip(e0, e1)))
1072
+ e1 = [c2 - c1 * dot for c1, c2 in zip(e0, e1)]
1073
+ denom = torch.sqrt(sum(c * c for c in e1) + eps * torch.ones_like(e1[0]))
1074
+ e1 = [c / denom for c in e1]
1075
+ e2 = [
1076
+ e0[1] * e1[2] - e0[2] * e1[1],
1077
+ e0[2] * e1[0] - e0[0] * e1[2],
1078
+ e0[0] * e1[1] - e0[1] * e1[0],
1079
+ ]
1080
+
1081
+ rots = torch.stack([c for tup in zip(e0, e1, e2) for c in tup], dim=-1)
1082
+ rots = rots.reshape(rots.shape[:-1] + (3, 3))
1083
+
1084
+ rot_obj = Rotation(rot_mats=rots, quats=None)
1085
+
1086
+ return Rigid(rot_obj, torch.stack(origin_unbound, dim=-1))
1087
+
1088
+ def unsqueeze(self, dim: int) -> Rigid:
1089
+ """
1090
+ Analogous to torch.unsqueeze. The dimension is relative to the shared dimensions of the rotation/translation.
1091
+
1092
+ Args:
1093
+ dim: A positive or negative dimension index.
1094
+ Returns:
1095
+ The unsqueezed transformation.
1096
+ """
1097
+ if dim >= len(self.shape):
1098
+ raise ValueError("Invalid dimension")
1099
+ rots = self._rots.unsqueeze(dim)
1100
+ trans = self._trans.unsqueeze(dim if dim >= 0 else dim - 1)
1101
+
1102
+ return Rigid(rots, trans)
1103
+
1104
+ @staticmethod
1105
+ def cat(ts: Sequence[Rigid], dim: int) -> Rigid:
1106
+ """
1107
+ Concatenates transformations along a new dimension.
1108
+
1109
+ Args:
1110
+ ts:
1111
+ A list of T objects
1112
+ dim:
1113
+ The dimension along which the transformations should be concatenated
1114
+ Returns:
1115
+ A concatenated transformation object
1116
+ """
1117
+ rots = Rotation.cat([t._rots for t in ts], dim)
1118
+ trans = torch.cat([t._trans for t in ts], dim=dim if dim >= 0 else dim - 1)
1119
+
1120
+ return Rigid(rots, trans)
1121
+
1122
+ def apply_rot_fn(self, fn: Callable[[Rotation], Rotation]) -> Rigid:
1123
+ """
1124
+ Applies a Rotation -> Rotation function to the stored rotation object.
1125
+
1126
+ Args:
1127
+ fn: A function of type Rotation -> Rotation
1128
+ Returns:
1129
+ A transformation object with a transformed rotation.
1130
+ """
1131
+ return Rigid(fn(self._rots), self._trans)
1132
+
1133
+ def apply_trans_fn(self, fn: Callable[[torch.Tensor], torch.Tensor]) -> Rigid:
1134
+ """
1135
+ Applies a Tensor -> Tensor function to the stored translation.
1136
+
1137
+ Args:
1138
+ fn:
1139
+ A function of type Tensor -> Tensor to be applied to the translation
1140
+ Returns:
1141
+ A transformation object with a transformed translation.
1142
+ """
1143
+ return Rigid(self._rots, fn(self._trans))
1144
+
1145
+ def scale_translation(self, trans_scale_factor: float) -> Rigid:
1146
+ """
1147
+ Scales the translation by a constant factor.
1148
+
1149
+ Args:
1150
+ trans_scale_factor:
1151
+ The constant factor
1152
+ Returns:
1153
+ A transformation object with a scaled translation.
1154
+ """
1155
+ return self.apply_trans_fn(lambda t: t * trans_scale_factor)
1156
+
1157
+ def stop_rot_gradient(self) -> Rigid:
1158
+ """
1159
+ Detaches the underlying rotation object
1160
+
1161
+ Returns:
1162
+ A transformation object with detached rotations
1163
+ """
1164
+ return self.apply_rot_fn(lambda r: r.detach())
1165
+
1166
+ @staticmethod
1167
+ def make_transform_from_reference(
1168
+ n_xyz: torch.Tensor, ca_xyz: torch.Tensor, c_xyz: torch.Tensor, eps: float = 1e-20
1169
+ ) -> Rigid:
1170
+ """
1171
+ Returns a transformation object from reference coordinates.
1172
+
1173
+ Note that this method does not take care of symmetries. If you provide the atom positions in the non-standard
1174
+ way, the N atom will end up not at [-0.527250, 1.359329, 0.0] but instead at [-0.527250, -1.359329, 0.0]. You
1175
+ need to take care of such cases in your code.
1176
+
1177
+ Args:
1178
+ n_xyz: A [*, 3] tensor of nitrogen xyz coordinates.
1179
+ ca_xyz: A [*, 3] tensor of carbon alpha xyz coordinates.
1180
+ c_xyz: A [*, 3] tensor of carbon xyz coordinates.
1181
+ Returns:
1182
+ A transformation object. After applying the translation and rotation to the reference backbone, the
1183
+ coordinates will approximately equal to the input coordinates.
1184
+ """
1185
+ translation = -1 * ca_xyz
1186
+ n_xyz = n_xyz + translation
1187
+ c_xyz = c_xyz + translation
1188
+
1189
+ c_x, c_y, c_z = [c_xyz[..., i] for i in range(3)]
1190
+ norm = torch.sqrt(eps + c_x**2 + c_y**2)
1191
+ sin_c1 = -c_y / norm
1192
+ cos_c1 = c_x / norm
1193
+
1194
+ c1_rots = sin_c1.new_zeros((*sin_c1.shape, 3, 3))
1195
+ c1_rots[..., 0, 0] = cos_c1
1196
+ c1_rots[..., 0, 1] = -1 * sin_c1
1197
+ c1_rots[..., 1, 0] = sin_c1
1198
+ c1_rots[..., 1, 1] = cos_c1
1199
+ c1_rots[..., 2, 2] = 1
1200
+
1201
+ norm = torch.sqrt(eps + c_x**2 + c_y**2 + c_z**2)
1202
+ sin_c2 = c_z / norm
1203
+ cos_c2 = torch.sqrt(c_x**2 + c_y**2) / norm
1204
+
1205
+ c2_rots = sin_c2.new_zeros((*sin_c2.shape, 3, 3))
1206
+ c2_rots[..., 0, 0] = cos_c2
1207
+ c2_rots[..., 0, 2] = sin_c2
1208
+ c2_rots[..., 1, 1] = 1
1209
+ c2_rots[..., 2, 0] = -1 * sin_c2
1210
+ c2_rots[..., 2, 2] = cos_c2
1211
+
1212
+ c_rots = rot_matmul(c2_rots, c1_rots)
1213
+ n_xyz = rot_vec_mul(c_rots, n_xyz)
1214
+
1215
+ _, n_y, n_z = [n_xyz[..., i] for i in range(3)]
1216
+ norm = torch.sqrt(eps + n_y**2 + n_z**2)
1217
+ sin_n = -n_z / norm
1218
+ cos_n = n_y / norm
1219
+
1220
+ n_rots = sin_c2.new_zeros((*sin_c2.shape, 3, 3))
1221
+ n_rots[..., 0, 0] = 1
1222
+ n_rots[..., 1, 1] = cos_n
1223
+ n_rots[..., 1, 2] = -1 * sin_n
1224
+ n_rots[..., 2, 1] = sin_n
1225
+ n_rots[..., 2, 2] = cos_n
1226
+
1227
+ rots = rot_matmul(n_rots, c_rots)
1228
+
1229
+ rots = rots.transpose(-1, -2)
1230
+ translation = -1 * translation
1231
+
1232
+ rot_obj = Rotation(rot_mats=rots, quats=None)
1233
+
1234
+ return Rigid(rot_obj, translation)
1235
+
1236
+ def cuda(self) -> Rigid:
1237
+ """
1238
+ Moves the transformation object to GPU memory
1239
+
1240
+ Returns:
1241
+ A version of the transformation on GPU
1242
+ """
1243
+ return Rigid(self._rots.cuda(), self._trans.cuda())
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/ovis2/modeling_ovis2.py ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
2
+ # This file was automatically generated from src/transformers/models/ovis2/modular_ovis2.py.
3
+ # Do NOT edit this file manually as any edits will be overwritten by the generation of
4
+ # the file from the modular. If any change should be done, please apply the change to the
5
+ # modular_ovis2.py file directly. One of our CI enforces this.
6
+ # 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
7
+ # Copyright 2025 The HuggingFace Inc. team. All rights reserved.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ import math
22
+ from collections.abc import Callable
23
+ from dataclasses import dataclass
24
+
25
+ import torch
26
+ from torch import nn
27
+
28
+ from ... import initialization as init
29
+ from ...activations import ACT2FN
30
+ from ...cache_utils import Cache
31
+ from ...generation import GenerationMixin
32
+ from ...integrations import use_kernel_forward_from_hub
33
+ from ...modeling_layers import GradientCheckpointingLayer
34
+ from ...modeling_outputs import BaseModelOutput, BaseModelOutputWithPast, BaseModelOutputWithPooling
35
+ from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
36
+ from ...processing_utils import Unpack
37
+ from ...utils import ModelOutput, TransformersKwargs, auto_docstring, can_return_tuple, torch_compilable_check
38
+ from ...utils.generic import merge_with_config_defaults
39
+ from ...utils.output_capturing import capture_outputs
40
+ from ..auto import AutoModel
41
+ from .configuration_ovis2 import Ovis2Config, Ovis2VisionConfig
42
+
43
+
44
+ @auto_docstring
45
+ @dataclass
46
+ class BaseModelOutputWithVisualIndicatorFeatures(BaseModelOutputWithPooling):
47
+ r"""
48
+ visual_indicator_features (`torch.FloatTensor` of shape `(batch_size, visual_indicator_size)`):
49
+ Visual indicator features extracted from the model, which can be used for auxiliary tasks or further processing.
50
+ """
51
+
52
+ visual_indicator_features: torch.FloatTensor | None = None
53
+
54
+
55
+ @auto_docstring(
56
+ custom_intro="""
57
+ Base class for Llava outputs, with hidden states and attentions.
58
+ """
59
+ )
60
+ @dataclass
61
+ class Ovis2ModelOutputWithPast(BaseModelOutputWithPast):
62
+ r"""
63
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
64
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
65
+
66
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
67
+ `past_key_values` input) to speed up sequential decoding.
68
+ image_hidden_states (`torch.FloatTensor`, *optional*):
69
+ A `torch.FloatTensor` of size `(batch_size, num_images, sequence_length, hidden_size)`.
70
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
71
+ """
72
+
73
+ image_hidden_states: torch.FloatTensor | None = None
74
+
75
+
76
+ @auto_docstring(
77
+ custom_intro="""
78
+ Base class for Ovis2 causal language model (or autoregressive) outputs.
79
+ """
80
+ )
81
+ @dataclass
82
+ class Ovis2CausalLMOutputWithPast(ModelOutput):
83
+ r"""
84
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
85
+ Language modeling loss (for next-token prediction).
86
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
87
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
88
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
89
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
90
+
91
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
92
+ `past_key_values` input) to speed up sequential decoding.
93
+ image_hidden_states (`torch.FloatTensor`, *optional*):
94
+ A `torch.FloatTensor` of size (batch_size * num_patches, num_images, sequence_length, hidden_size)`.
95
+ image_hidden_states of the model produced by the vision encoder and after projecting the last hidden state.
96
+ """
97
+
98
+ loss: torch.FloatTensor | None = None
99
+ logits: torch.FloatTensor | None = None
100
+ past_key_values: Cache | None = None
101
+ hidden_states: tuple[torch.FloatTensor] | None = None
102
+ attentions: tuple[torch.FloatTensor] | None = None
103
+ image_hidden_states: torch.FloatTensor | None = None
104
+
105
+
106
+ @use_kernel_forward_from_hub("RMSNorm")
107
+ class Ovis2RMSNorm(nn.Module):
108
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
109
+ """
110
+ Ovis2RMSNorm is equivalent to T5LayerNorm
111
+ """
112
+ super().__init__()
113
+ self.weight = nn.Parameter(torch.ones(hidden_size))
114
+ self.variance_epsilon = eps
115
+
116
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
117
+ input_dtype = hidden_states.dtype
118
+ hidden_states = hidden_states.to(torch.float32)
119
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
120
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
121
+ return self.weight * hidden_states.to(input_dtype)
122
+
123
+ def extra_repr(self):
124
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
125
+
126
+
127
+ class Ovis2VisionMLP(nn.Module):
128
+ def __init__(self, config):
129
+ super().__init__()
130
+ self.config = config
131
+ self.hidden_size = config.hidden_size
132
+ self.intermediate_size = config.intermediate_size
133
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
134
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
135
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
136
+ self.act_fn = ACT2FN[config.hidden_act]
137
+
138
+ def forward(self, x):
139
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
140
+ return down_proj
141
+
142
+
143
+ class Ovis2VisionEmbeddings(nn.Module):
144
+ def __init__(self, config: Ovis2VisionConfig):
145
+ super().__init__()
146
+ self.config = config
147
+ self.embed_dim = config.hidden_size
148
+ self.image_size = config.image_size
149
+ self.patch_size = config.patch_size
150
+
151
+ self.patch_embedding = nn.Conv2d(
152
+ in_channels=config.num_channels,
153
+ out_channels=self.embed_dim,
154
+ kernel_size=self.patch_size,
155
+ stride=self.patch_size,
156
+ padding="valid",
157
+ )
158
+
159
+ self.num_patches = (self.image_size // self.patch_size) ** 2
160
+ self.num_positions = self.num_patches
161
+ self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
162
+ self.register_buffer("position_ids", torch.arange(self.num_positions).expand((1, -1)), persistent=False)
163
+ self.rms_norm = Ovis2RMSNorm(config.hidden_size, config.rms_norm_eps)
164
+
165
+ def forward(self, pixel_values: torch.FloatTensor) -> torch.Tensor:
166
+ target_dtype = self.patch_embedding.weight.dtype
167
+ patch_embeds = self.patch_embedding(pixel_values.to(dtype=target_dtype))
168
+ embeddings = patch_embeds.flatten(2).transpose(1, 2)
169
+ embeddings = self.rms_norm(embeddings)
170
+
171
+ embeddings = embeddings + self.position_embedding(self.position_ids)
172
+
173
+ return embeddings
174
+
175
+
176
+ def eager_attention_forward(
177
+ module: nn.Module,
178
+ query: torch.Tensor,
179
+ key: torch.Tensor,
180
+ value: torch.Tensor,
181
+ attention_mask: torch.Tensor | None,
182
+ scaling: float,
183
+ dropout: float = 0.0,
184
+ **kwargs,
185
+ ):
186
+ attn_weights = torch.matmul(query, key.transpose(-1, -2)) * scaling
187
+ if attention_mask is not None:
188
+ attn_weights = attn_weights + attention_mask
189
+
190
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
191
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
192
+
193
+ attn_output = torch.matmul(attn_weights, value)
194
+ attn_output = attn_output.transpose(1, 2).contiguous()
195
+
196
+ return attn_output, attn_weights
197
+
198
+
199
+ class Ovis2VisionAttention(nn.Module):
200
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
201
+
202
+ def __init__(self, config):
203
+ super().__init__()
204
+ self.config = config
205
+ self.embed_dim = config.hidden_size
206
+ self.num_heads = config.num_attention_heads
207
+ self.head_dim = self.embed_dim // self.num_heads
208
+ if self.head_dim * self.num_heads != self.embed_dim:
209
+ raise ValueError(
210
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
211
+ f" {self.num_heads})."
212
+ )
213
+ self.scale = self.head_dim**-0.5
214
+ self.dropout = config.attention_dropout
215
+ self.is_causal = False
216
+ self.k_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=config.qkv_bias)
217
+ self.v_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=config.qkv_bias)
218
+ self.q_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=config.qkv_bias)
219
+ self.out_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=config.qkv_bias)
220
+
221
+ def forward(
222
+ self,
223
+ hidden_states: torch.Tensor,
224
+ attention_mask: torch.Tensor | None = None,
225
+ **kwargs,
226
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
227
+ """Input shape: Batch x Time x Channel"""
228
+
229
+ input_shape = hidden_states.shape[:-1]
230
+
231
+ hidden_shape = (*input_shape, -1, self.head_dim)
232
+ queries = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
233
+ keys = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
234
+ values = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
235
+
236
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
237
+ self.config._attn_implementation, eager_attention_forward
238
+ )
239
+
240
+ attn_output, attn_weights = attention_interface(
241
+ self,
242
+ queries,
243
+ keys,
244
+ values,
245
+ attention_mask,
246
+ is_causal=self.is_causal,
247
+ scaling=self.scale,
248
+ dropout=0.0 if not self.training else self.dropout,
249
+ )
250
+
251
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
252
+ attn_output = self.out_proj(attn_output)
253
+
254
+ return attn_output, attn_weights
255
+
256
+
257
+ class Ovis2MLP(nn.Module):
258
+ def __init__(self, config):
259
+ super().__init__()
260
+ self.config = config
261
+ self.hidden_size = config.hidden_size
262
+ self.intermediate_size = config.intermediate_size
263
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
264
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=config.mlp_bias)
265
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.mlp_bias)
266
+ self.act_fn = ACT2FN[config.hidden_act]
267
+
268
+ def forward(self, x):
269
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
270
+ return down_proj
271
+
272
+
273
+ class Ovis2VisionEncoderLayer(GradientCheckpointingLayer):
274
+ def __init__(self, config: Ovis2VisionConfig):
275
+ super().__init__()
276
+ self.attention = Ovis2VisionAttention(config)
277
+ self.ffn = Ovis2MLP(config)
278
+ self.rms_norm1 = Ovis2RMSNorm(config.hidden_size, config.rms_norm_eps)
279
+ self.rms_norm2 = Ovis2RMSNorm(config.hidden_size, config.rms_norm_eps)
280
+
281
+ def forward(
282
+ self,
283
+ hidden_states: torch.Tensor,
284
+ attention_mask: torch.Tensor | None = None,
285
+ **kwargs: Unpack[TransformersKwargs],
286
+ ) -> torch.Tensor:
287
+ norm_hidden_states = self.rms_norm1(hidden_states)
288
+ attn_output, _ = self.attention(hidden_states=norm_hidden_states, attention_mask=attention_mask, **kwargs)
289
+
290
+ hidden_states = hidden_states + attn_output
291
+ norm_hidden_states = self.rms_norm2(hidden_states)
292
+ mlp_output = self.ffn(norm_hidden_states)
293
+
294
+ hidden_states = hidden_states + mlp_output
295
+ return hidden_states
296
+
297
+
298
+ class Ovis2VisionEncoder(nn.Module):
299
+ """
300
+ Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
301
+ [`Ovis2VisionEncoderLayer`].
302
+
303
+ Args:
304
+ config: Ovis2VisionConfig
305
+ """
306
+
307
+ def __init__(self, config: Ovis2VisionConfig):
308
+ super().__init__()
309
+ self.config = config
310
+ self.layers = nn.ModuleList([Ovis2VisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
311
+ self.gradient_checkpointing = False
312
+
313
+ # Ignore copy
314
+ @can_return_tuple
315
+ @auto_docstring
316
+ def forward(
317
+ self,
318
+ inputs_embeds,
319
+ attention_mask: torch.Tensor | None = None,
320
+ **kwargs: Unpack[TransformersKwargs],
321
+ ) -> BaseModelOutput:
322
+ hidden_states = inputs_embeds
323
+ for encoder_layer in self.layers:
324
+ hidden_states = encoder_layer(hidden_states, attention_mask, **kwargs)
325
+
326
+ return BaseModelOutput(last_hidden_state=hidden_states)
327
+
328
+
329
+ class Ovis2VisionTransformer(nn.Module):
330
+ def __init__(self, config: Ovis2VisionConfig):
331
+ super().__init__()
332
+ self.config = config
333
+ self.embeddings = Ovis2VisionEmbeddings(config)
334
+ self.encoder = Ovis2VisionEncoder(config)
335
+ self.rms_norm = Ovis2RMSNorm(config.hidden_size, config.rms_norm_eps)
336
+ self.gradient_checkpointing = False
337
+
338
+ @can_return_tuple
339
+ def forward(
340
+ self,
341
+ pixel_values,
342
+ attention_mask: torch.Tensor | None = None,
343
+ **kwargs,
344
+ ):
345
+ hidden_states = self.embeddings(pixel_values)
346
+
347
+ encoder_outputs: BaseModelOutput = self.encoder(
348
+ inputs_embeds=hidden_states,
349
+ attention_mask=attention_mask,
350
+ **kwargs,
351
+ )
352
+
353
+ last_hidden_state = encoder_outputs.last_hidden_state
354
+ last_hidden_state = self.rms_norm(last_hidden_state)
355
+
356
+ return BaseModelOutput(last_hidden_state=last_hidden_state)
357
+
358
+
359
+ class Ovis2VisualEmbeddingTable(nn.Embedding):
360
+ def forward(self, visual_tokens: torch.Tensor) -> torch.Tensor:
361
+ if visual_tokens.dtype in [torch.int8, torch.int16, torch.int32, torch.int64, torch.long]:
362
+ return super().forward(visual_tokens)
363
+ return torch.matmul(visual_tokens, self.weight)
364
+
365
+
366
+ class Ovis2PreTrainedModel(PreTrainedModel):
367
+ config: Ovis2Config
368
+ base_model_prefix = "model"
369
+ input_modalities = ("image", "text")
370
+ supports_gradient_checkpointing = True
371
+ _no_split_modules = ["Ovis2VisionAttention"]
372
+ _skip_keys_device_placement = ["past_key_values"]
373
+ _supports_cache_class = True
374
+ _supports_flash_attn = True
375
+ _supports_flex_attn = True
376
+ _supports_sdpa = True
377
+
378
+ _can_compile_fullgraph = True
379
+ _supports_attention_backend = True
380
+
381
+ def _init_weights(self, module):
382
+ super()._init_weights(module)
383
+ if isinstance(module, Ovis2VisionEmbeddings):
384
+ init.copy_(module.position_ids, torch.arange(module.position_ids.shape[-1]).expand((1, -1)))
385
+
386
+
387
+ def hard_softmax(logits: torch.Tensor, dim: int):
388
+ y_soft = logits.softmax(dim)
389
+ # Straight through.
390
+ index = y_soft.max(dim, keepdim=True)[1]
391
+ y_hard = torch.zeros_like(logits, memory_format=torch.legacy_contiguous_format).scatter_(dim, index, 1.0)
392
+ ret = y_hard - y_soft.detach() + y_soft
393
+
394
+ return ret
395
+
396
+
397
+ class Ovis2VisionModel(Ovis2PreTrainedModel):
398
+ config: Ovis2VisionConfig
399
+ _can_record_outputs = {
400
+ "hidden_states": Ovis2VisionEncoderLayer,
401
+ "attentions": Ovis2VisionAttention,
402
+ }
403
+
404
+ def __init__(self, config: Ovis2VisionConfig):
405
+ super().__init__(config)
406
+ self.config = config
407
+ self.transformer = Ovis2VisionTransformer(config)
408
+ self.num_visual_indicator_tokens = config.num_visual_indicator_tokens
409
+ self.vocab_size = config.vocab_size
410
+ self.head_linear = nn.Linear(
411
+ config.hidden_size * config.hidden_stride * config.hidden_stride,
412
+ self.vocab_size - self.num_visual_indicator_tokens,
413
+ bias=False,
414
+ )
415
+ self.head_norm = nn.LayerNorm(self.vocab_size - self.num_visual_indicator_tokens)
416
+
417
+ self.post_init()
418
+
419
+ @merge_with_config_defaults
420
+ @capture_outputs
421
+ def forward(
422
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
423
+ ) -> tuple | BaseModelOutputWithVisualIndicatorFeatures:
424
+ outputs = self.transformer(pixel_values, **kwargs)
425
+ last_hidden_state = outputs[0]
426
+ if self.config.hidden_stride > 1:
427
+ num_images, seq_len, hidden_dim = last_hidden_state.shape
428
+ hidden_stride = self.config.hidden_stride
429
+
430
+ sqrt_l = int(math.sqrt(seq_len))
431
+ if sqrt_l * sqrt_l != seq_len:
432
+ raise ValueError("Token sequence length must be a perfect square")
433
+
434
+ pad_size = (hidden_stride - (sqrt_l % hidden_stride)) % hidden_stride
435
+ last_hidden_state = nn.functional.pad(last_hidden_state, (0, 0, 0, pad_size, 0, pad_size), "constant", 0)
436
+ sqrt_l += pad_size
437
+
438
+ last_hidden_state = last_hidden_state.reshape(
439
+ num_images, sqrt_l // hidden_stride, hidden_stride, sqrt_l // hidden_stride, hidden_stride, hidden_dim
440
+ )
441
+ last_hidden_state = last_hidden_state.permute(0, 1, 3, 2, 4, 5)
442
+ last_hidden_state = last_hidden_state.reshape(
443
+ num_images, -1, hidden_stride * hidden_stride * hidden_dim
444
+ ) # (n, (sqrt_l//hs)^2, hs^2*d)
445
+
446
+ logits = self.head_linear(last_hidden_state)
447
+ logits = self.head_norm(logits)
448
+
449
+ if self.config.tokenize_function == "gumbel_argmax":
450
+ prob_token = nn.functional.gumbel_softmax(logits, dim=-1, hard=True)
451
+ elif self.config.tokenize_function == "st_argmax":
452
+ prob_token = hard_softmax(logits, dim=-1)
453
+ elif self.config.tokenize_function == "softmax":
454
+ prob_token = nn.functional.softmax(logits, dim=-1)
455
+
456
+ return BaseModelOutputWithVisualIndicatorFeatures(
457
+ last_hidden_state=last_hidden_state,
458
+ pooler_output=prob_token,
459
+ )
460
+
461
+
462
+ @auto_docstring(
463
+ custom_intro="""
464
+ The Ovis2 model which consists of a vision backbone and a language model, without a language modeling head.
465
+ """
466
+ )
467
+ class Ovis2Model(Ovis2PreTrainedModel):
468
+ def __init__(self, config: Ovis2Config):
469
+ super().__init__(config)
470
+ self.vision_tower = Ovis2VisionModel(config.vision_config)
471
+ self.language_model = AutoModel.from_config(config.text_config)
472
+ self.visual_embeddings_table = Ovis2VisualEmbeddingTable(config.vision_config.vocab_size, config.hidden_size)
473
+
474
+ self.visual_vocab_size = config.vision_config.vocab_size
475
+ self.vocab_size = config.vocab_size
476
+ self.visual_indicator_token_ids = config.visual_indicator_token_ids
477
+ self.post_init()
478
+
479
+ @can_return_tuple
480
+ @auto_docstring(
481
+ custom_intro="Obtains image last hidden states from the vision tower and apply multimodal projection."
482
+ )
483
+ def get_image_features(
484
+ self,
485
+ pixel_values: torch.FloatTensor,
486
+ **kwargs: Unpack[TransformersKwargs],
487
+ ) -> tuple | BaseModelOutputWithVisualIndicatorFeatures:
488
+ image_outputs = self.vision_tower(pixel_values, return_dict=True, **kwargs)
489
+ image_features = image_outputs.pooler_output
490
+ batch_size, img_seq_len, _ = image_features.shape
491
+ padding_tensor = torch.zeros(
492
+ (batch_size, img_seq_len, self.vision_tower.num_visual_indicator_tokens),
493
+ dtype=image_features.dtype,
494
+ device=image_features.device,
495
+ requires_grad=False,
496
+ layout=image_features.layout,
497
+ )
498
+ image_features = torch.cat([image_features, padding_tensor], dim=2)
499
+ image_features = self.visual_embeddings_table(image_features)
500
+
501
+ visual_indicator = torch.arange(
502
+ self.visual_vocab_size - self.vision_tower.num_visual_indicator_tokens,
503
+ self.visual_vocab_size,
504
+ dtype=torch.long,
505
+ ).to(image_features.device)
506
+ image_outputs.pooler_output = image_features
507
+ image_outputs.visual_indicator_features = self.visual_embeddings_table(visual_indicator)
508
+
509
+ return image_outputs
510
+
511
+ def get_placeholder_mask(
512
+ self, input_ids: torch.LongTensor, inputs_embeds: torch.FloatTensor, image_features: torch.FloatTensor
513
+ ):
514
+ """
515
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
516
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
517
+ """
518
+ if input_ids is None:
519
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
520
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
521
+ )
522
+ special_image_mask = special_image_mask.all(-1)
523
+ else:
524
+ special_image_mask = input_ids == self.config.image_token_id
525
+
526
+ n_image_tokens = special_image_mask.sum()
527
+ n_image_features = image_features.shape[0] * image_features.shape[1]
528
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
529
+ torch_compilable_check(
530
+ inputs_embeds[special_image_mask].numel() == image_features.numel(),
531
+ f"Image features and image tokens do not match, tokens: {n_image_tokens}, features: {n_image_features}",
532
+ )
533
+ return special_image_mask
534
+
535
+ @can_return_tuple
536
+ @auto_docstring
537
+ def forward(
538
+ self,
539
+ input_ids: torch.LongTensor | None = None,
540
+ pixel_values: torch.FloatTensor | None = None,
541
+ attention_mask: torch.Tensor | None = None,
542
+ position_ids: torch.LongTensor | None = None,
543
+ past_key_values: Cache | None = None,
544
+ inputs_embeds: torch.FloatTensor | None = None,
545
+ labels: torch.LongTensor | None = None,
546
+ use_cache: bool | None = None,
547
+ logits_to_keep: int | torch.Tensor = 0,
548
+ **kwargs,
549
+ ) -> tuple | Ovis2ModelOutputWithPast:
550
+ if (input_ids is None) ^ (inputs_embeds is not None):
551
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
552
+
553
+ if inputs_embeds is None:
554
+ inputs_embeds = self.get_input_embeddings()(input_ids)
555
+
556
+ if pixel_values is not None:
557
+ image_outputs = self.get_image_features(pixel_values=pixel_values, return_dict=True)
558
+ image_features = image_outputs.pooler_output
559
+ visual_indicator_features = image_outputs.visual_indicator_features
560
+
561
+ special_image_mask = self.get_placeholder_mask(
562
+ input_ids,
563
+ inputs_embeds=inputs_embeds,
564
+ image_features=image_features,
565
+ )
566
+ inputs_embeds = inputs_embeds.masked_scatter(special_image_mask, image_features)
567
+
568
+ for i, visual_indicator_id in enumerate(self.visual_indicator_token_ids):
569
+ if input_ids is None:
570
+ mask = inputs_embeds == self.get_input_embeddings()(
571
+ torch.tensor(visual_indicator_id, dtype=torch.long, device=inputs_embeds.device)
572
+ )
573
+ mask = mask.all(-1)
574
+ else:
575
+ mask = (input_ids == visual_indicator_id).to(inputs_embeds.device)
576
+
577
+ if mask.any():
578
+ inputs_embeds[mask] = (
579
+ visual_indicator_features[i]
580
+ .expand_as(inputs_embeds[mask])
581
+ .to(inputs_embeds.device, inputs_embeds.dtype)
582
+ )
583
+
584
+ outputs = self.language_model(
585
+ attention_mask=attention_mask,
586
+ position_ids=position_ids,
587
+ past_key_values=past_key_values,
588
+ inputs_embeds=inputs_embeds,
589
+ use_cache=use_cache,
590
+ logits_to_keep=logits_to_keep,
591
+ **kwargs,
592
+ )
593
+
594
+ return Ovis2ModelOutputWithPast(
595
+ last_hidden_state=outputs.last_hidden_state,
596
+ past_key_values=outputs.past_key_values,
597
+ hidden_states=outputs.hidden_states,
598
+ attentions=outputs.attentions,
599
+ image_hidden_states=image_features if pixel_values is not None else None,
600
+ )
601
+
602
+
603
+ @auto_docstring
604
+ class Ovis2ForConditionalGeneration(Ovis2PreTrainedModel, GenerationMixin):
605
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
606
+
607
+ def __init__(self, config: Ovis2Config):
608
+ super().__init__(config)
609
+ self.model = Ovis2Model(config)
610
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
611
+ self.post_init()
612
+
613
+ def get_output_embeddings(self) -> nn.Module:
614
+ return self.lm_head
615
+
616
+ @auto_docstring
617
+ def get_image_features(
618
+ self, pixel_values: torch.FloatTensor, **kwargs: Unpack[TransformersKwargs]
619
+ ) -> tuple | BaseModelOutputWithVisualIndicatorFeatures:
620
+ return self.model.get_image_features(pixel_values=pixel_values, **kwargs)
621
+
622
+ @can_return_tuple
623
+ @auto_docstring
624
+ def forward(
625
+ self,
626
+ input_ids: torch.LongTensor | None = None,
627
+ pixel_values: torch.FloatTensor | None = None,
628
+ attention_mask: torch.Tensor | None = None,
629
+ position_ids: torch.LongTensor | None = None,
630
+ past_key_values: Cache | None = None,
631
+ inputs_embeds: torch.FloatTensor | None = None,
632
+ labels: torch.LongTensor | None = None,
633
+ use_cache: bool | None = None,
634
+ logits_to_keep: int | torch.Tensor = 0,
635
+ **kwargs,
636
+ ) -> tuple | Ovis2CausalLMOutputWithPast:
637
+ r"""
638
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
639
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
640
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
641
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
642
+
643
+ Example:
644
+
645
+ ```python
646
+ >>> from PIL import Image
647
+ >>> import httpx
648
+ >>> from io import BytesIO
649
+ >>> from transformers import AutoProcessor, Ovis2ForConditionalGeneration
650
+
651
+ >>> model = Ovis2ForConditionalGeneration.from_pretrained("thisisiron/Ovis2-2B-hf")
652
+ >>> processor = AutoProcessor.from_pretrained("thisisiron/Ovis2-2B-hf")
653
+
654
+ >>> prompt = "<|im_start|>user\n<image>\nDescribe the image.<|im_end|>\n<|im_start|>assistant\n"
655
+ >>> url = "http://images.cocodataset.org/val2014/COCO_val2014_000000537955.jpg"
656
+ >>> with httpx.stream("GET", url) as response:
657
+ ... image = Image.open(BytesIO(response.read()))
658
+
659
+ >>> inputs = processor(images=image, text=prompt, return_tensors="pt")
660
+
661
+ >>> # Generate
662
+ >>> generate_ids = model.generate(**inputs, max_new_tokens=15)
663
+ >>> processor.batch_decode(generate_ids, skip_special_tokens=True)[0]
664
+ "user\n\nDescribe the image.\nassistant\nThe image features a brown dog standing on a wooden floor, looking up with"
665
+ ```"""
666
+ outputs = self.model(
667
+ input_ids=input_ids,
668
+ pixel_values=pixel_values,
669
+ attention_mask=attention_mask,
670
+ position_ids=position_ids,
671
+ past_key_values=past_key_values,
672
+ inputs_embeds=inputs_embeds,
673
+ use_cache=use_cache,
674
+ **kwargs,
675
+ )
676
+
677
+ hidden_states = outputs[0]
678
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
679
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
680
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
681
+
682
+ loss = None
683
+ if labels is not None:
684
+ loss = self.loss_function(
685
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
686
+ )
687
+
688
+ return Ovis2CausalLMOutputWithPast(
689
+ loss=loss,
690
+ logits=logits,
691
+ past_key_values=outputs.past_key_values,
692
+ hidden_states=outputs.hidden_states,
693
+ attentions=outputs.attentions,
694
+ image_hidden_states=outputs.image_hidden_states,
695
+ )
696
+
697
+ def prepare_inputs_for_generation(
698
+ self,
699
+ input_ids,
700
+ past_key_values=None,
701
+ inputs_embeds=None,
702
+ pixel_values=None,
703
+ attention_mask=None,
704
+ logits_to_keep=None,
705
+ is_first_iteration=False,
706
+ **kwargs,
707
+ ):
708
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
709
+
710
+ model_inputs = super().prepare_inputs_for_generation(
711
+ input_ids,
712
+ past_key_values=past_key_values,
713
+ inputs_embeds=inputs_embeds,
714
+ attention_mask=attention_mask,
715
+ logits_to_keep=logits_to_keep,
716
+ is_first_iteration=is_first_iteration,
717
+ **kwargs,
718
+ )
719
+
720
+ if is_first_iteration or not kwargs.get("use_cache", True):
721
+ # Pixel values are used only in the first iteration if available
722
+ # In subsequent iterations, they are already merged with text and cached
723
+ # NOTE: first iteration doesn't have to be prefill, it can be the first
724
+ # iteration with a question and cached system prompt (continue generate from cache)
725
+ model_inputs["pixel_values"] = pixel_values
726
+
727
+ return model_inputs
728
+
729
+
730
+ __all__ = ["Ovis2PreTrainedModel", "Ovis2Model", "Ovis2ForConditionalGeneration"]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/__init__.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from typing import TYPE_CHECKING
15
+
16
+ from ...utils import _LazyModule
17
+ from ...utils.import_utils import define_import_structure
18
+
19
+
20
+ if TYPE_CHECKING:
21
+ from .configuration_textnet import *
22
+ from .image_processing_pil_textnet import *
23
+ from .image_processing_textnet import *
24
+ from .modeling_textnet import *
25
+ else:
26
+ import sys
27
+
28
+ _file = globals()["__file__"]
29
+ sys.modules[__name__] = _LazyModule(__name__, _file, define_import_structure(_file), module_spec=__spec__)
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/configuration_textnet.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 the Fast authors and HuggingFace Inc. team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """TextNet model configuration"""
15
+
16
+ from huggingface_hub.dataclasses import strict
17
+
18
+ from ...backbone_utils import BackboneConfigMixin
19
+ from ...configuration_utils import PreTrainedConfig
20
+ from ...utils import auto_docstring
21
+
22
+
23
+ @auto_docstring(checkpoint="czczup/textnet-base")
24
+ @strict
25
+ class TextNetConfig(BackboneConfigMixin, PreTrainedConfig):
26
+ r"""
27
+ stem_kernel_size (`int`, *optional*, defaults to 3):
28
+ The kernel size for the initial convolution layer.
29
+ stem_stride (`int`, *optional*, defaults to 2):
30
+ The stride for the initial convolution layer.
31
+ stem_num_channels (`int`, *optional*, defaults to 3):
32
+ The num of channels in input for the initial convolution layer.
33
+ stem_out_channels (`int`, *optional*, defaults to 64):
34
+ The num of channels in out for the initial convolution layer.
35
+ stem_act_func (`str`, *optional*, defaults to `"relu"`):
36
+ The activation function for the initial convolution layer.
37
+ conv_layer_kernel_sizes (`list[list[list[int]]]`, *optional*):
38
+ A list of stage-wise kernel sizes. If `None`, defaults to:
39
+ `[[[3, 3], [3, 3], [3, 3]], [[3, 3], [1, 3], [3, 3], [3, 1]], [[3, 3], [3, 3], [3, 1], [1, 3]], [[3, 3], [3, 1], [1, 3], [3, 3]]]`.
40
+ conv_layer_strides (`list[list[int]]`, *optional*):
41
+ A list of stage-wise strides. If `None`, defaults to:
42
+ `[[1, 2, 1], [2, 1, 1, 1], [2, 1, 1, 1], [2, 1, 1, 1]]`.
43
+
44
+ Examples:
45
+
46
+ ```python
47
+ >>> from transformers import TextNetConfig, TextNetBackbone
48
+
49
+ >>> # Initializing a TextNetConfig
50
+ >>> configuration = TextNetConfig()
51
+
52
+ >>> # Initializing a model (with random weights)
53
+ >>> model = TextNetBackbone(configuration)
54
+
55
+ >>> # Accessing the model configuration
56
+ >>> configuration = model.config
57
+ ```"""
58
+
59
+ model_type = "textnet"
60
+
61
+ stem_kernel_size: int = 3
62
+ stem_stride: int = 2
63
+ stem_num_channels: int = 3
64
+ stem_out_channels: int = 64
65
+ stem_act_func: str = "relu"
66
+ image_size: list[int] | tuple[int, int] | int = (640, 640)
67
+ conv_layer_kernel_sizes: list | None = None
68
+ conv_layer_strides: list | None = None
69
+ hidden_sizes: list[int] | tuple[int, ...] = (64, 64, 128, 256, 512)
70
+ batch_norm_eps: float = 1e-5
71
+ initializer_range: float = 0.02
72
+ _out_features: list[str] | None = None
73
+ _out_indices: list[int] | None = None
74
+
75
+ def __post_init__(self, **kwargs):
76
+ if self.conv_layer_kernel_sizes is None:
77
+ self.conv_layer_kernel_sizes = [
78
+ [[3, 3], [3, 3], [3, 3]],
79
+ [[3, 3], [1, 3], [3, 3], [3, 1]],
80
+ [[3, 3], [3, 3], [3, 1], [1, 3]],
81
+ [[3, 3], [3, 1], [1, 3], [3, 3]],
82
+ ]
83
+ if self.conv_layer_strides is None:
84
+ self.conv_layer_strides = [[1, 2, 1], [2, 1, 1, 1], [2, 1, 1, 1], [2, 1, 1, 1]]
85
+
86
+ self.depths = [len(layer) for layer in self.conv_layer_kernel_sizes]
87
+ self.stage_names = ["stem"] + [f"stage{idx}" for idx in range(1, 5)]
88
+ self.set_output_features_output_indices(
89
+ out_indices=kwargs.pop("out_indices", None), out_features=kwargs.pop("out_features", None)
90
+ )
91
+ super().__post_init__(**kwargs)
92
+
93
+
94
+ __all__ = ["TextNetConfig"]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/image_processing_pil_textnet.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 the Fast authors and The HuggingFace Inc. team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Image processor class for TextNet."""
15
+
16
+ import numpy as np
17
+
18
+ from ...image_processing_backends import PilBackend
19
+ from ...image_processing_utils import BatchFeature
20
+ from ...image_transforms import get_resize_output_image_size
21
+ from ...image_transforms import resize as np_resize
22
+ from ...image_utils import (
23
+ IMAGENET_DEFAULT_MEAN,
24
+ IMAGENET_DEFAULT_STD,
25
+ ChannelDimension,
26
+ ImageInput,
27
+ PILImageResampling,
28
+ SizeDict,
29
+ )
30
+ from ...processing_utils import ImagesKwargs, Unpack
31
+ from ...utils import TensorType, auto_docstring
32
+
33
+
34
+ # Adapted from transformers.models.textnet.image_processing_textnet.TextNetImageProcessorKwargs
35
+ class TextNetImageProcessorKwargs(ImagesKwargs, total=False):
36
+ """
37
+ size_divisor (`int`, *optional*, defaults to `self.size_divisor`):
38
+ Ensures height and width are rounded to a multiple of this value after resizing.
39
+ """
40
+
41
+ size_divisor: int
42
+
43
+
44
+ @auto_docstring
45
+ class TextNetImageProcessorPil(PilBackend):
46
+ """PIL backend for TextNet with size_divisor resize."""
47
+
48
+ valid_kwargs = TextNetImageProcessorKwargs
49
+
50
+ resample = PILImageResampling.BILINEAR
51
+ image_mean = IMAGENET_DEFAULT_MEAN
52
+ image_std = IMAGENET_DEFAULT_STD
53
+ size = {"shortest_edge": 640}
54
+ default_to_square = False
55
+ crop_size = {"height": 224, "width": 224}
56
+ do_resize = True
57
+ do_center_crop = False
58
+ do_rescale = True
59
+ do_normalize = True
60
+ do_convert_rgb = True
61
+ size_divisor = 32
62
+
63
+ def __init__(self, **kwargs: Unpack[TextNetImageProcessorKwargs]):
64
+ super().__init__(**kwargs)
65
+
66
+ @auto_docstring
67
+ def preprocess(self, images: ImageInput, **kwargs: Unpack[TextNetImageProcessorKwargs]) -> BatchFeature:
68
+ return super().preprocess(images, **kwargs)
69
+
70
+ def resize(
71
+ self,
72
+ image: np.ndarray,
73
+ size: SizeDict,
74
+ resample: "PILImageResampling | None",
75
+ size_divisor: int = 32,
76
+ **kwargs,
77
+ ) -> np.ndarray:
78
+ """Resize to shortest_edge then round up to be divisible by size_divisor."""
79
+ if not size.shortest_edge:
80
+ raise ValueError(f"Size must contain 'shortest_edge' key. Got {size.keys()}")
81
+ height, width = get_resize_output_image_size(
82
+ image,
83
+ size=size.shortest_edge,
84
+ default_to_square=False,
85
+ input_data_format=ChannelDimension.FIRST,
86
+ )
87
+ # Round up to be divisible by size_divisor
88
+ if height % size_divisor != 0:
89
+ height += size_divisor - (height % size_divisor)
90
+ if width % size_divisor != 0:
91
+ width += size_divisor - (width % size_divisor)
92
+ return np_resize(
93
+ image,
94
+ size=(height, width),
95
+ resample=resample,
96
+ data_format=ChannelDimension.FIRST,
97
+ input_data_format=ChannelDimension.FIRST,
98
+ )
99
+
100
+ def _preprocess(
101
+ self,
102
+ images: list[np.ndarray],
103
+ do_resize: bool,
104
+ size: SizeDict,
105
+ resample: "PILImageResampling | None",
106
+ do_center_crop: bool,
107
+ crop_size: SizeDict,
108
+ do_rescale: bool,
109
+ rescale_factor: float,
110
+ do_normalize: bool,
111
+ image_mean: float | list[float] | None,
112
+ image_std: float | list[float] | None,
113
+ do_pad: bool | None,
114
+ pad_size: SizeDict | None,
115
+ return_tensors: str | TensorType | None,
116
+ size_divisor: int = 32,
117
+ **kwargs,
118
+ ) -> BatchFeature:
119
+ """Custom preprocessing for TextNet."""
120
+ processed_images = []
121
+ for image in images:
122
+ if do_resize:
123
+ image = self.resize(image, size, resample, size_divisor=size_divisor)
124
+ if do_center_crop:
125
+ image = self.center_crop(image, crop_size)
126
+ if do_rescale:
127
+ image = self.rescale(image, rescale_factor)
128
+ if do_normalize:
129
+ image = self.normalize(image, image_mean, image_std)
130
+ processed_images.append(image)
131
+ return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)
132
+
133
+
134
+ __all__ = ["TextNetImageProcessorPil"]
LTA_openwebtext_dualt/mini_owt_logdirichlet/.venv_qwen35_uv/lib/python3.12/site-packages/transformers/models/textnet/modeling_textnet.py ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 the Fast authors and The HuggingFace Inc. team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """PyTorch TextNet model."""
15
+
16
+ from typing import Any
17
+
18
+ import torch
19
+ import torch.nn as nn
20
+ from torch import Tensor
21
+
22
+ from ...activations import ACT2CLS
23
+ from ...backbone_utils import BackboneMixin, filter_output_hidden_states
24
+ from ...modeling_outputs import (
25
+ BackboneOutput,
26
+ BaseModelOutputWithNoAttention,
27
+ BaseModelOutputWithPoolingAndNoAttention,
28
+ ImageClassifierOutputWithNoAttention,
29
+ )
30
+ from ...modeling_utils import PreTrainedModel
31
+ from ...utils import auto_docstring, logging
32
+ from ...utils.generic import can_return_tuple
33
+ from .configuration_textnet import TextNetConfig
34
+
35
+
36
+ logger = logging.get_logger(__name__)
37
+
38
+
39
+ class TextNetConvLayer(nn.Module):
40
+ def __init__(self, config: TextNetConfig):
41
+ super().__init__()
42
+
43
+ self.kernel_size = config.stem_kernel_size
44
+ self.stride = config.stem_stride
45
+ self.activation_function = config.stem_act_func
46
+
47
+ padding = (
48
+ (config.kernel_size[0] // 2, config.kernel_size[1] // 2)
49
+ if isinstance(config.stem_kernel_size, tuple)
50
+ else config.stem_kernel_size // 2
51
+ )
52
+
53
+ self.conv = nn.Conv2d(
54
+ config.stem_num_channels,
55
+ config.stem_out_channels,
56
+ kernel_size=config.stem_kernel_size,
57
+ stride=config.stem_stride,
58
+ padding=padding,
59
+ bias=False,
60
+ )
61
+ self.batch_norm = nn.BatchNorm2d(config.stem_out_channels, config.batch_norm_eps)
62
+
63
+ self.activation = nn.Identity()
64
+ if self.activation_function is not None:
65
+ self.activation = ACT2CLS[self.activation_function]()
66
+
67
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
68
+ hidden_states = self.conv(hidden_states)
69
+ hidden_states = self.batch_norm(hidden_states)
70
+ return self.activation(hidden_states)
71
+
72
+
73
+ class TextNetRepConvLayer(nn.Module):
74
+ r"""
75
+ This layer supports re-parameterization by combining multiple convolutional branches
76
+ (e.g., main convolution, vertical, horizontal, and identity branches) during training.
77
+ At inference time, these branches can be collapsed into a single convolution for
78
+ efficiency, as per the re-parameterization paradigm.
79
+
80
+ The "Rep" in the name stands for "re-parameterization" (introduced by RepVGG).
81
+ """
82
+
83
+ def __init__(self, config: TextNetConfig, in_channels: int, out_channels: int, kernel_size: int, stride: int):
84
+ super().__init__()
85
+
86
+ self.num_channels = in_channels
87
+ self.out_channels = out_channels
88
+ self.kernel_size = kernel_size
89
+ self.stride = stride
90
+
91
+ padding = ((kernel_size[0] - 1) // 2, (kernel_size[1] - 1) // 2)
92
+
93
+ self.activation_function = nn.ReLU()
94
+
95
+ self.main_conv = nn.Conv2d(
96
+ in_channels=in_channels,
97
+ out_channels=out_channels,
98
+ kernel_size=kernel_size,
99
+ stride=stride,
100
+ padding=padding,
101
+ bias=False,
102
+ )
103
+ self.main_batch_norm = nn.BatchNorm2d(num_features=out_channels, eps=config.batch_norm_eps)
104
+
105
+ vertical_padding = ((kernel_size[0] - 1) // 2, 0)
106
+ horizontal_padding = (0, (kernel_size[1] - 1) // 2)
107
+
108
+ if kernel_size[1] != 1:
109
+ self.vertical_conv = nn.Conv2d(
110
+ in_channels=in_channels,
111
+ out_channels=out_channels,
112
+ kernel_size=(kernel_size[0], 1),
113
+ stride=stride,
114
+ padding=vertical_padding,
115
+ bias=False,
116
+ )
117
+ self.vertical_batch_norm = nn.BatchNorm2d(num_features=out_channels, eps=config.batch_norm_eps)
118
+ else:
119
+ self.vertical_conv, self.vertical_batch_norm = None, None
120
+
121
+ if kernel_size[0] != 1:
122
+ self.horizontal_conv = nn.Conv2d(
123
+ in_channels=in_channels,
124
+ out_channels=out_channels,
125
+ kernel_size=(1, kernel_size[1]),
126
+ stride=stride,
127
+ padding=horizontal_padding,
128
+ bias=False,
129
+ )
130
+ self.horizontal_batch_norm = nn.BatchNorm2d(num_features=out_channels, eps=config.batch_norm_eps)
131
+ else:
132
+ self.horizontal_conv, self.horizontal_batch_norm = None, None
133
+
134
+ self.rbr_identity = (
135
+ nn.BatchNorm2d(num_features=in_channels, eps=config.batch_norm_eps)
136
+ if out_channels == in_channels and stride == 1
137
+ else None
138
+ )
139
+
140
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
141
+ main_outputs = self.main_conv(hidden_states)
142
+ main_outputs = self.main_batch_norm(main_outputs)
143
+
144
+ # applies a convolution with a vertical kernel
145
+ if self.vertical_conv is not None:
146
+ vertical_outputs = self.vertical_conv(hidden_states)
147
+ vertical_outputs = self.vertical_batch_norm(vertical_outputs)
148
+ main_outputs = main_outputs + vertical_outputs
149
+
150
+ # applies a convolution with a horizontal kernel
151
+ if self.horizontal_conv is not None:
152
+ horizontal_outputs = self.horizontal_conv(hidden_states)
153
+ horizontal_outputs = self.horizontal_batch_norm(horizontal_outputs)
154
+ main_outputs = main_outputs + horizontal_outputs
155
+
156
+ if self.rbr_identity is not None:
157
+ id_out = self.rbr_identity(hidden_states)
158
+ main_outputs = main_outputs + id_out
159
+
160
+ return self.activation_function(main_outputs)
161
+
162
+
163
+ class TextNetStage(nn.Module):
164
+ def __init__(self, config: TextNetConfig, depth: int):
165
+ super().__init__()
166
+ kernel_size = config.conv_layer_kernel_sizes[depth]
167
+ stride = config.conv_layer_strides[depth]
168
+
169
+ num_layers = len(kernel_size)
170
+ stage_in_channel_size = config.hidden_sizes[depth]
171
+ stage_out_channel_size = config.hidden_sizes[depth + 1]
172
+
173
+ in_channels = [stage_in_channel_size] + [stage_out_channel_size] * (num_layers - 1)
174
+ out_channels = [stage_out_channel_size] * num_layers
175
+
176
+ stage = []
177
+ for stage_config in zip(in_channels, out_channels, kernel_size, stride):
178
+ stage.append(TextNetRepConvLayer(config, *stage_config))
179
+ self.stage = nn.ModuleList(stage)
180
+
181
+ def forward(self, hidden_state):
182
+ for block in self.stage:
183
+ hidden_state = block(hidden_state)
184
+ return hidden_state
185
+
186
+
187
+ class TextNetEncoder(nn.Module):
188
+ def __init__(self, config: TextNetConfig):
189
+ super().__init__()
190
+
191
+ stages = []
192
+ num_stages = len(config.conv_layer_kernel_sizes)
193
+ for stage_ix in range(num_stages):
194
+ stages.append(TextNetStage(config, stage_ix))
195
+
196
+ self.stages = nn.ModuleList(stages)
197
+
198
+ def forward(
199
+ self,
200
+ hidden_state: torch.Tensor,
201
+ output_hidden_states: bool | None = None,
202
+ return_dict: bool | None = None,
203
+ ) -> BaseModelOutputWithNoAttention:
204
+ hidden_states = [hidden_state]
205
+ for stage in self.stages:
206
+ hidden_state = stage(hidden_state)
207
+ hidden_states.append(hidden_state)
208
+
209
+ if not return_dict:
210
+ output = (hidden_state,)
211
+ return output + (hidden_states,) if output_hidden_states else output
212
+
213
+ return BaseModelOutputWithNoAttention(last_hidden_state=hidden_state, hidden_states=hidden_states)
214
+
215
+
216
+ @auto_docstring
217
+ class TextNetPreTrainedModel(PreTrainedModel):
218
+ config: TextNetConfig
219
+ base_model_prefix = "textnet"
220
+ main_input_name = "pixel_values"
221
+
222
+
223
+ @auto_docstring
224
+ class TextNetModel(TextNetPreTrainedModel):
225
+ def __init__(self, config):
226
+ super().__init__(config)
227
+ self.stem = TextNetConvLayer(config)
228
+ self.encoder = TextNetEncoder(config)
229
+ self.pooler = nn.AdaptiveAvgPool2d((2, 2))
230
+ self.post_init()
231
+
232
+ @auto_docstring
233
+ def forward(
234
+ self,
235
+ pixel_values: Tensor,
236
+ output_hidden_states: bool | None = None,
237
+ return_dict: bool | None = None,
238
+ **kwargs,
239
+ ) -> tuple[Any, list[Any]] | tuple[Any] | BaseModelOutputWithPoolingAndNoAttention:
240
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
241
+ output_hidden_states = (
242
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
243
+ )
244
+
245
+ hidden_state = self.stem(pixel_values)
246
+
247
+ encoder_outputs = self.encoder(
248
+ hidden_state, output_hidden_states=output_hidden_states, return_dict=return_dict
249
+ )
250
+
251
+ last_hidden_state = encoder_outputs[0]
252
+ pooled_output = self.pooler(last_hidden_state)
253
+
254
+ if not return_dict:
255
+ output = (last_hidden_state, pooled_output)
256
+ return output + (encoder_outputs[1],) if output_hidden_states else output
257
+
258
+ return BaseModelOutputWithPoolingAndNoAttention(
259
+ last_hidden_state=last_hidden_state,
260
+ pooler_output=pooled_output,
261
+ hidden_states=encoder_outputs[1] if output_hidden_states else None,
262
+ )
263
+
264
+
265
+ @auto_docstring(
266
+ custom_intro="""
267
+ TextNet Model with an image classification head on top (a linear layer on top of the pooled features), e.g. for
268
+ ImageNet.
269
+ """
270
+ )
271
+ class TextNetForImageClassification(TextNetPreTrainedModel):
272
+ def __init__(self, config):
273
+ super().__init__(config)
274
+ self.num_labels = config.num_labels
275
+ self.textnet = TextNetModel(config)
276
+ self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
277
+ self.flatten = nn.Flatten()
278
+ self.fc = nn.Linear(config.hidden_sizes[-1], config.num_labels) if config.num_labels > 0 else nn.Identity()
279
+
280
+ # classification head
281
+ self.classifier = nn.ModuleList([self.avg_pool, self.flatten])
282
+
283
+ # initialize weights and apply final processing
284
+ self.post_init()
285
+
286
+ @auto_docstring
287
+ def forward(
288
+ self,
289
+ pixel_values: torch.FloatTensor | None = None,
290
+ labels: torch.LongTensor | None = None,
291
+ output_hidden_states: bool | None = None,
292
+ return_dict: bool | None = None,
293
+ **kwargs,
294
+ ) -> ImageClassifierOutputWithNoAttention:
295
+ r"""
296
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
297
+ Labels for computing the image classification/regression loss. Indices should be in `[0, ...,
298
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
299
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
300
+
301
+ Examples:
302
+ ```python
303
+ >>> import torch
304
+ >>> import httpx
305
+ >>> from io import BytesIO
306
+ >>> from transformers import TextNetForImageClassification, TextNetImageProcessor
307
+ >>> from PIL import Image
308
+
309
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
310
+ >>> with httpx.stream("GET", url) as response:
311
+ ... image = Image.open(BytesIO(response.read()))
312
+
313
+ >>> processor = TextNetImageProcessor.from_pretrained("czczup/textnet-base")
314
+ >>> model = TextNetForImageClassification.from_pretrained("czczup/textnet-base")
315
+
316
+ >>> inputs = processor(images=image, return_tensors="pt")
317
+ >>> with torch.no_grad():
318
+ ... outputs = model(**inputs)
319
+ >>> outputs.logits.shape
320
+ torch.Size([1, 2])
321
+ ```"""
322
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
323
+
324
+ outputs = self.textnet(pixel_values, output_hidden_states=output_hidden_states, return_dict=return_dict)
325
+ last_hidden_state = outputs[0]
326
+ for layer in self.classifier:
327
+ last_hidden_state = layer(last_hidden_state)
328
+ logits = self.fc(last_hidden_state)
329
+ loss = None
330
+
331
+ if labels is not None:
332
+ loss = self.loss_function(labels, logits, self.config)
333
+
334
+ if not return_dict:
335
+ output = (logits,) + outputs[2:]
336
+ return (loss,) + output if loss is not None else output
337
+
338
+ return ImageClassifierOutputWithNoAttention(loss=loss, logits=logits, hidden_states=outputs.hidden_states)
339
+
340
+
341
+ @auto_docstring(
342
+ custom_intro="""
343
+ TextNet backbone, to be used with frameworks like DETR and MaskFormer.
344
+ """
345
+ )
346
+ class TextNetBackbone(BackboneMixin, TextNetPreTrainedModel):
347
+ has_attentions = False
348
+
349
+ def __init__(self, config):
350
+ super().__init__(config)
351
+
352
+ self.textnet = TextNetModel(config)
353
+ self.num_features = config.hidden_sizes
354
+
355
+ # initialize weights and apply final processing
356
+ self.post_init()
357
+
358
+ @can_return_tuple
359
+ @filter_output_hidden_states
360
+ @auto_docstring
361
+ def forward(
362
+ self,
363
+ pixel_values: Tensor,
364
+ output_hidden_states: bool | None = None,
365
+ return_dict: bool | None = None,
366
+ **kwargs,
367
+ ) -> tuple[tuple] | BackboneOutput:
368
+ r"""
369
+ Examples:
370
+
371
+ ```python
372
+ >>> import torch
373
+ >>> import httpx
374
+ >>> from io import BytesIO
375
+ >>> from PIL import Image
376
+ >>> from transformers import AutoImageProcessor, AutoBackbone
377
+
378
+ >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg"
379
+ >>> with httpx.stream("GET", url) as response:
380
+ ... image = Image.open(BytesIO(response.read()))
381
+
382
+ >>> processor = AutoImageProcessor.from_pretrained("czczup/textnet-base")
383
+ >>> model = AutoBackbone.from_pretrained("czczup/textnet-base")
384
+
385
+ >>> inputs = processor(image, return_tensors="pt")
386
+ >>> with torch.no_grad():
387
+ >>> outputs = model(**inputs)
388
+ ```"""
389
+ return_dict = return_dict if return_dict is not None else self.config.return_dict
390
+ output_hidden_states = (
391
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
392
+ )
393
+
394
+ outputs = self.textnet(pixel_values, output_hidden_states=True, return_dict=return_dict)
395
+
396
+ hidden_states = outputs.hidden_states if return_dict else outputs[2]
397
+
398
+ feature_maps = ()
399
+ for idx, stage in enumerate(self.stage_names):
400
+ if stage in self.out_features:
401
+ feature_maps += (hidden_states[idx],)
402
+
403
+ if not return_dict:
404
+ output = (feature_maps,)
405
+ if output_hidden_states:
406
+ hidden_states = outputs.hidden_states if return_dict else outputs[2]
407
+ output += (hidden_states,)
408
+ return output
409
+
410
+ return BackboneOutput(
411
+ feature_maps=feature_maps,
412
+ hidden_states=outputs.hidden_states if output_hidden_states else None,
413
+ attentions=None,
414
+ )
415
+
416
+
417
+ __all__ = ["TextNetBackbone", "TextNetModel", "TextNetPreTrainedModel", "TextNetForImageClassification"]