alphafold3-input 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ """Package metadata for AlphaFold 3 input models.
2
+
3
+ Provides a normalized interface to distribution metadata for the package.
4
+
5
+ Exports:
6
+ - :data:`__title__`: Package title.
7
+ - :data:`__description__`: Package summary.
8
+ - :data:`__author__`: Package author.
9
+ - :data:`__version__`: Installed version.
10
+ - :data:`__package__`: Distribution name.
11
+ - :data:`__module_name__`: Import path.
12
+ - :data:`__repository__`: Repository URL.
13
+ - :data:`__documentation__`: Documentation URL.
14
+ - :data:`__issues__`: Issue tracker URL.
15
+ """
16
+
17
+ from importlib.metadata import PackageMetadata, metadata, version
18
+
19
+ __all__: list[str] = [
20
+ "__author__",
21
+ "__description__",
22
+ "__documentation__",
23
+ "__issues__",
24
+ "__module_name__",
25
+ "__package__",
26
+ "__repository__",
27
+ "__title__",
28
+ "__version__",
29
+ ]
30
+
31
+ __package__: str = "alphafold3_input"
32
+ """Distribution package name."""
33
+
34
+ __module_name__: str = "alphafold3_input"
35
+ """Importable top-level module name."""
36
+
37
+ __version__: str = version(__package__)
38
+ """Installed package version string."""
39
+
40
+ __metadata__: PackageMetadata = metadata(__package__)
41
+ """Raw distribution metadata."""
42
+
43
+ __title__: str = __metadata__["Name"]
44
+ """Distribution package name."""
45
+
46
+ __description__: str = __metadata__.get("Summary", "")
47
+ """Package summary."""
48
+
49
+ __author__: str = __metadata__.get("Author", "")
50
+ """Package author."""
51
+
52
+ __repository__: str = ""
53
+ """Repository URL."""
54
+
55
+ __documentation__: str = ""
56
+ """Documentation URL."""
57
+
58
+ __issues__: str = ""
59
+ """Issue tracker URL."""
60
+
61
+ for item in __metadata__.json.get("project_url", []):
62
+ label, url = item.split(", ", 1)
63
+ match label:
64
+ case "Repository":
65
+ __repository__ = url
66
+ case "Documentation":
67
+ __documentation__ = url
68
+ case "Issues":
69
+ __issues__ = url
@@ -0,0 +1,61 @@
1
+ """AlphaFold 3 input models.
2
+
3
+ This package provides models for constructing AlphaFold 3 input files.
4
+
5
+ It offers a Pythonic, object-oriented interface for defining AlphaFold 3
6
+ jobs, abstracting the underlying JSON input format into typed models and
7
+ validated structures. The implementation closely follows the official
8
+ AlphaFold 3 input specification provided by DeepMind.
9
+
10
+ For full details on the expected input format and supported features,
11
+ refer to the official `AlphaFold 3 input specification
12
+ <https://github.com/google-deepmind/alphafold3/blob/main/docs/input.md>`_.
13
+
14
+ Exports:
15
+ - :data:`JSON_SCHEMA_URL`: canonical JSON Schema URL for editor \
16
+ validation.
17
+ - :class:`Job`, :class:`Dialect`, :class:`Version`: top-level job model \
18
+ and input format enums.
19
+ - :class:`DNA`, :class:`RNA`, :class:`Protein`, :class:`Ligand`: \
20
+ entity models used under :attr:`Job.entities`.
21
+ - :class:`Modification`, :class:`Entity`: residue modification model and \
22
+ its entity-scope enum.
23
+ - :class:`Template`: structural template specification for proteins.
24
+ - :class:`Atom`, :class:`Bond`: covalent bond specification models.
25
+ - :class:`Operation`, :func:`trace`, :func:`reindex`, :func:`realign`: \
26
+ operation trace generation, reindexing, and realignment utilities.
27
+ - :func:`ccd`, :func:`component`: generation of custom chemical \
28
+ component dictionaries.
29
+ """
30
+
31
+ from .bond import Atom, Bond
32
+ from .dna import DNA
33
+ from .job import JSON_SCHEMA_URL, Dialect, Job, Version
34
+ from .ligand import Ligand
35
+ from .modification import Entity, Modification
36
+ from .protein import Protein
37
+ from .rna import RNA
38
+ from .template import Template
39
+ from .utils import Operation, ccd, component, realign, reindex, trace
40
+
41
+ __all__: list[str] = [
42
+ "DNA",
43
+ "JSON_SCHEMA_URL",
44
+ "RNA",
45
+ "Atom",
46
+ "Bond",
47
+ "Dialect",
48
+ "Entity",
49
+ "Job",
50
+ "Ligand",
51
+ "Modification",
52
+ "Operation",
53
+ "Protein",
54
+ "Template",
55
+ "Version",
56
+ "ccd",
57
+ "component",
58
+ "realign",
59
+ "reindex",
60
+ "trace",
61
+ ]
@@ -0,0 +1,222 @@
1
+ """Covalent bond models.
2
+
3
+ This submodule defines :class:`Atom` and :class:`Bond` for specifying
4
+ covalent bonds between entities in an AlphaFold 3 input.
5
+
6
+ Exports:
7
+ - :class:`Atom`: atom specification within an entity.
8
+ - :class:`Bond`: covalent bond between two atoms.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Sequence
14
+ from typing import Any, Self
15
+
16
+ from pydantic import (
17
+ BaseModel,
18
+ ConfigDict,
19
+ Field,
20
+ model_serializer,
21
+ model_validator,
22
+ )
23
+
24
+ __all__: list[str] = [
25
+ "Atom",
26
+ "Bond",
27
+ ]
28
+
29
+
30
+ class Atom(BaseModel):
31
+ """Atom specification within an entity.
32
+
33
+ An atom is defined by an :attr:`entity` identifier, a 1-based
34
+ :attr:`residue` index, and an atom :attr:`name`.
35
+
36
+ Attributes:
37
+ entity (str): Entity identifier.
38
+ residue (int): Residue index within the entity.
39
+ name (str): Atom name within the residue.
40
+
41
+ Examples:
42
+ Atom definition.
43
+
44
+ >>> Atom(entity="A", residue=1, name="CB")
45
+ """
46
+
47
+ model_config = ConfigDict(
48
+ extra="forbid",
49
+ frozen=True,
50
+ validate_assignment=False,
51
+ validate_by_name=True,
52
+ validate_by_alias=True,
53
+ )
54
+
55
+ entity: str = Field(
56
+ title="entity",
57
+ alias="entity",
58
+ description="Entity identifier.",
59
+ pattern="^[A-Z]+$",
60
+ validation_alias="entity",
61
+ serialization_alias="entity",
62
+ )
63
+ """Entity identifier."""
64
+
65
+ residue: int = Field(
66
+ title="residue",
67
+ alias="residue",
68
+ description="Residue index within the entity.",
69
+ ge=1,
70
+ validation_alias="residue",
71
+ serialization_alias="residue",
72
+ )
73
+ """Residue index within the entity."""
74
+
75
+ name: str = Field(
76
+ title="name",
77
+ alias="name",
78
+ description="Atom name within the residue.",
79
+ min_length=1,
80
+ validation_alias="name",
81
+ serialization_alias="name",
82
+ )
83
+ """Atom name within the residue."""
84
+
85
+ @model_validator(mode="before")
86
+ @classmethod
87
+ def __validate_model(cls: type[Self], data: Any) -> Any:
88
+ """Coerce compact atom definitions to a mapping.
89
+
90
+ Accepts the AlphaFold 3 list form ``[entity, residue, name]`` and
91
+ converts it to a mapping compatible with field validation.
92
+
93
+ Args:
94
+ data (Any): Raw input data.
95
+
96
+ Returns:
97
+ Any: A mapping with keys ``entity``, ``residue``, and ``name`` if
98
+ ``data`` is a sequence, otherwise the original input.
99
+
100
+ Raises:
101
+ ValueError: If ``data`` is a sequence but does not contain exactly
102
+ three items.
103
+ """
104
+ if not isinstance(data, Sequence) or isinstance(
105
+ data,
106
+ (str, bytes, bytearray),
107
+ ):
108
+ return data
109
+
110
+ if len(data) != len(cls.model_fields):
111
+ msg: str = (
112
+ "Invalid atom definition: expected `[entity, residue, name]`."
113
+ )
114
+ raise ValueError(msg)
115
+ return {"entity": data[0], "residue": data[1], "name": data[2]}
116
+
117
+ @model_serializer(mode="plain")
118
+ def __serialize_model(self: Self) -> tuple[str, int, str]:
119
+ """Serialize the atom as ``[entity, residue, name]``.
120
+
121
+ Returns:
122
+ tuple[str, int, str]: Compact AlphaFold 3 atom representation.
123
+ """
124
+ return (self.entity, self.residue, self.name)
125
+
126
+
127
+ class Bond(BaseModel):
128
+ """Covalent bond specification.
129
+
130
+ Defines a covalent bond between the :attr:`source` and the :attr:`target`
131
+ atoms as an AlphaFold 3 bonded atom pair.
132
+
133
+ Bonds are intended for covalently linked multi-residue :class:`Ligand`
134
+ entities, for example glycans. Covalent bonds within or between polymer
135
+ entities such as :class:`DNA`, :class:`RNA`, or :class:`Protein` are not
136
+ supported by AlphaFold 3.
137
+
138
+ Attributes:
139
+ source (Atom): Source atom address.
140
+ target (Atom): Target atom address.
141
+
142
+ Examples:
143
+ Covalent bond between two entities.
144
+
145
+ >>> Bond(
146
+ ... source=Atom(entity="A", residue=1, name="CA"),
147
+ ... target=Atom(entity="G", residue=1, name="CHA"),
148
+ ... )
149
+
150
+ Covalent bond within a multi-residue entity.
151
+
152
+ >>> Bond(
153
+ ... source=Atom(entity="I", residue=1, name="O6"),
154
+ ... target=Atom(entity="I", residue=2, name="C1"),
155
+ ... )
156
+ """
157
+
158
+ model_config = ConfigDict(
159
+ extra="forbid",
160
+ frozen=False,
161
+ validate_assignment=True,
162
+ validate_by_name=True,
163
+ validate_by_alias=True,
164
+ )
165
+
166
+ source: Atom = Field(
167
+ title="source",
168
+ alias="source",
169
+ description="Source atom address.",
170
+ validation_alias="source",
171
+ serialization_alias="source",
172
+ )
173
+ """Source atom address."""
174
+
175
+ target: Atom = Field(
176
+ title="target",
177
+ alias="target",
178
+ description="Target atom address.",
179
+ validation_alias="target",
180
+ serialization_alias="target",
181
+ )
182
+ """Target atom address."""
183
+
184
+ @model_validator(mode="before")
185
+ @classmethod
186
+ def __validate_model(cls: type[Self], data: Any) -> Any:
187
+ """Coerce compact bond definitions to a mapping.
188
+
189
+ Accepts the AlphaFold 3 list form ``[[...], [...]]`` and converts it
190
+ to a mapping compatible with field validation.
191
+
192
+ Args:
193
+ data (Any): Raw input data.
194
+
195
+ Returns:
196
+ Any: A mapping with keys ``source`` and ``target`` if ``data`` is
197
+ a sequence, otherwise the original input.
198
+
199
+ Raises:
200
+ ValueError: If ``data`` is a sequence but does not contain exactly
201
+ two items.
202
+ """
203
+ if not isinstance(data, Sequence) or isinstance(
204
+ data,
205
+ (str, bytes, bytearray),
206
+ ):
207
+ return data
208
+
209
+ if len(data) != len(cls.model_fields):
210
+ msg: str = "Invalid bond definition: expected `[source, target]`."
211
+ raise ValueError(msg)
212
+ return {"source": data[0], "target": data[1]}
213
+
214
+ @model_serializer(mode="plain")
215
+ def __serialize_model(self: Self) -> tuple[Atom, Atom]:
216
+ """Serialize the bond as an AlphaFold 3 bonded atom pair.
217
+
218
+ Returns:
219
+ tuple[Atom, Atom]: Compact AlphaFold 3 bonded atom pair
220
+ representation.
221
+ """
222
+ return (self.source, self.target)
@@ -0,0 +1,260 @@
1
+ """DNA chain entity models.
2
+
3
+ This submodule defines :class:`DNA`, which can be used to include polymeric DNA
4
+ entities in an AlphaFold 3 input.
5
+
6
+ Exports:
7
+ - :class:`DNA`: DNA chain entity model.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Sequence
13
+ from typing import Annotated, Any, Self
14
+
15
+ from pydantic import (
16
+ AliasPath,
17
+ BaseModel,
18
+ ConfigDict,
19
+ Field,
20
+ SerializerFunctionWrapHandler,
21
+ StringConstraints,
22
+ field_validator,
23
+ model_serializer,
24
+ model_validator,
25
+ )
26
+
27
+ from .modification import Entity, Modification
28
+
29
+ __all__: list[str] = [
30
+ "DNA",
31
+ ]
32
+
33
+
34
+ class DNA(BaseModel):
35
+ """DNA chain entity specification.
36
+
37
+ A DNA chain is defined by its nucleotide :attr:`sequence`, optional
38
+ :attr:`modifications`, and one or more chain identifiers via :attr:`id`.
39
+ Multiple copies can be defined either by setting :attr:`copies` or by
40
+ providing multiple explicit identifiers in :attr:`id`.
41
+
42
+ The optional :attr:`description` field is supported only when
43
+ :attr:`Job.version` is set to :attr:`Version.IV`.
44
+
45
+ Attributes:
46
+ id (str | Sequence[str] | None): DNA chain identifier(s).
47
+ description (str | None): Free-text DNA chain description.
48
+ sequence (str): DNA chain nucleotide sequence.
49
+ modifications (Sequence[Modification]): DNA chain residue
50
+ modifications.
51
+ copies (int): Number of DNA chain copies.
52
+
53
+ Examples:
54
+ DNA chain with a description.
55
+
56
+ >>> DNA(
57
+ ... description="Promoter for bacteriophage T7 RNA polymerase",
58
+ ... sequence="TAATACGACTCACTATAGG",
59
+ ... )
60
+
61
+ Multiple copies of a DNA chain.
62
+
63
+ >>> DNA(
64
+ ... sequence="GAATTC",
65
+ ... copies=2,
66
+ ... )
67
+
68
+ DNA chain with modified residues.
69
+
70
+ >>> heptamer = DNA(sequence="GACCTCT")
71
+ >>> heptamer.modify(
72
+ ... Modification(type="6OG", position=1),
73
+ ... Modification(type="6MA", position=2),
74
+ ... )
75
+ """
76
+
77
+ model_config = ConfigDict(
78
+ extra="forbid",
79
+ frozen=False,
80
+ validate_assignment=True,
81
+ validate_by_name=True,
82
+ validate_by_alias=True,
83
+ use_enum_values=False,
84
+ )
85
+
86
+ id: (
87
+ Annotated[str, StringConstraints(pattern="^[A-Z]+$")]
88
+ | Sequence[Annotated[str, StringConstraints(pattern="^[A-Z]+$")]]
89
+ | None
90
+ ) = Field(
91
+ title="id",
92
+ alias="id",
93
+ description="DNA chain identifier(s).",
94
+ min_length=1,
95
+ validation_alias=AliasPath("dna", "id"),
96
+ serialization_alias="id",
97
+ default=None,
98
+ )
99
+ """DNA chain identifier(s)."""
100
+
101
+ description: str | None = Field(
102
+ title="description",
103
+ alias="description",
104
+ description="Free-text DNA chain description.",
105
+ validation_alias=AliasPath("dna", "description"),
106
+ serialization_alias="description",
107
+ default=None,
108
+ )
109
+ """Free-text DNA chain description."""
110
+
111
+ sequence: Annotated[str, StringConstraints(pattern="^[ACGT]+$")] = Field(
112
+ title="sequence",
113
+ alias="sequence",
114
+ description="DNA chain nucleotide sequence.",
115
+ min_length=1,
116
+ validation_alias=AliasPath("dna", "sequence"),
117
+ serialization_alias="sequence",
118
+ )
119
+ """DNA chain nucleotide sequence."""
120
+
121
+ modifications: Sequence[Modification] = Field(
122
+ title="modifications",
123
+ alias="modifications",
124
+ description="DNA chain residue modifications.",
125
+ validation_alias=AliasPath("dna", "modifications"),
126
+ serialization_alias="modifications",
127
+ default_factory=tuple,
128
+ )
129
+ """DNA chain residue modifications."""
130
+
131
+ copies: int = Field(
132
+ title="copies",
133
+ alias="copies",
134
+ description="Number of DNA chain copies.",
135
+ ge=1,
136
+ validation_alias="copies",
137
+ exclude=True,
138
+ repr=False,
139
+ default=1,
140
+ )
141
+ """Number of DNA chain copies."""
142
+
143
+ def modify(self: Self, *modifications: Modification) -> Self:
144
+ """Append residue modifications to the DNA chain.
145
+
146
+ Args:
147
+ *modifications (Modification): One or more modifications to add.
148
+
149
+ Returns:
150
+ Self: DNA chain with appended modifications.
151
+
152
+ Raises:
153
+ TypeError: If no modifications were provided.
154
+ """
155
+ if not modifications:
156
+ msg: str = (
157
+ "Invalid DNA modification: no modifications were provided."
158
+ )
159
+ raise TypeError(msg)
160
+
161
+ self.modifications: tuple[Modification, ...] = tuple(
162
+ self.modifications,
163
+ ) + tuple(modifications)
164
+ return self
165
+
166
+ @field_validator("modifications", mode="after")
167
+ @classmethod
168
+ def __scope_modifications(
169
+ cls: type[Self],
170
+ value: Sequence[Modification],
171
+ ) -> Sequence[Modification]:
172
+ """Assign DNA scope to each modification.
173
+
174
+ Args:
175
+ value (Sequence[Modification]): Modification entries.
176
+
177
+ Returns:
178
+ Sequence[Modification]: Scoped modification entries.
179
+ """
180
+ for modification in value:
181
+ object.__setattr__(modification, "scope", Entity.DNA)
182
+ return value
183
+
184
+ @model_validator(mode="after")
185
+ def __validate_modifications(self: Self) -> Self:
186
+ """Validate residue modifications against the DNA sequence.
187
+
188
+ Ensures that each modification position is within the sequence and that
189
+ no residue is modified more than once.
190
+
191
+ Returns:
192
+ Self: Validated DNA chain instance.
193
+
194
+ Raises:
195
+ ValueError: If a modification position is out of range.
196
+ ValueError: If multiple modifications target the same position.
197
+ """
198
+ length: int = len(self.sequence)
199
+ modified: set[int] = set()
200
+
201
+ for modification in self.modifications:
202
+ if modification.position > length:
203
+ msg: str = (
204
+ "Invalid DNA modification: modification `position` is out "
205
+ f"of range (position={modification.position}, "
206
+ f"len(sequence)={length})."
207
+ )
208
+ raise ValueError(msg)
209
+
210
+ if modification.position in modified:
211
+ msg: str = (
212
+ "Invalid DNA modification list: multiple modifications on "
213
+ f"the same `position` (position={modification.position})."
214
+ )
215
+ raise ValueError(msg)
216
+
217
+ modified.add(modification.position)
218
+
219
+ return self
220
+
221
+ @model_validator(mode="after")
222
+ def __validate_copies(self: Self) -> Self:
223
+ """Validate consistency between ``id`` and ``copies``.
224
+
225
+ If :attr:`id` is provided, :attr:`copies` is set to ``len(id)``. If
226
+ :attr:`copies` was explicitly provided and is inconsistent with
227
+ :attr:`id`, a :class:`ValueError` is raised.
228
+
229
+ Returns:
230
+ Self: Validated DNA chain instance.
231
+
232
+ Raises:
233
+ ValueError: If ``copies`` is inconsistent with ``id``.
234
+ """
235
+ if self.id is None:
236
+ return self
237
+
238
+ n: int = len(self.id) if not isinstance(self.id, str) else 1
239
+
240
+ if "copies" in self.model_fields_set and self.copies != n:
241
+ msg: str = (
242
+ "Conflicting DNA configuration: `copies` is inconsistent "
243
+ f"with the length of `id` (copies={self.copies}, len(id)={n})."
244
+ )
245
+ raise ValueError(msg)
246
+
247
+ object.__setattr__(self, "copies", n)
248
+ return self
249
+
250
+ @model_serializer(mode="wrap")
251
+ def __wrapped_serialization(
252
+ self: Self,
253
+ handler: SerializerFunctionWrapHandler,
254
+ ) -> dict[str, Any]:
255
+ """Serialize the entity in wrapped AlphaFold 3 form.
256
+
257
+ Returns:
258
+ dict[str, Any]: Wrapped entity mapping.
259
+ """
260
+ return {self.__class__.__name__.lower(): handler(self)}