altar-sei 0.3.0__tar.gz

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,33 @@
1
+ *.pyc
2
+ *~
3
+ **/__pycache__/*
4
+ *.swp
5
+ .vscode/
6
+ .idea/
7
+ .DS_Store
8
+ .env*
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+ coverage.xml
15
+ pytest.xml
16
+ .hypothesis/
17
+ .python-version
18
+ .venv
19
+ .venv-*/
20
+ *.egg-info/
21
+ build/
22
+ dist/
23
+ site/
24
+ *.sqlite3
25
+ *.log
26
+
27
+ # Generated variant indexes and datasets. The small canonical gene table is tracked.
28
+ altar/altar/variants/data/ccres.dnatree
29
+ altar/altar/variants/data/region_annotations.parquet
30
+ altar/altar/variants/data/variants.pkl.gz
31
+ altar/altar/variants/data/region_provenance.json
32
+ altar/altar/variants/data/raw/
33
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Riya Sinha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.5
2
+ Name: altar-sei
3
+ Version: 0.3.0
4
+ Summary: Sei model binding for large-scale Altar variant scoring
5
+ Project-URL: Documentation, https://kundajelab.github.io/altar/
6
+ Author: Riya Sinha
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: altar<0.4,>=0.3.0
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'test'
21
+ Requires-Dist: pytest>=8; extra == 'test'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Altar Sei binding
25
+
26
+ `altar-sei` is the lightweight control-plane binding for high-throughput Sei variant scoring. It registers
27
+ `SEI` in Altar's canonical model-plugin group and does not install PyTorch, SciPy, the model architecture, or
28
+ the official resources.
29
+
30
+ Each model instance identifies the official checkpoint, projection vectors, histone-profile indices,
31
+ sequence-class metadata, and an immutable runtime image by SHA-256 digest. The image defaults to
32
+ `altar_sei.DEFAULT_IMAGE`, a published image validated against FunctionLab's official Sei framework
33
+ (`runtimes/sei/validation/cpu-official-81f595f.json`). The binding emits one independently retryable task for each staged batch, and the
34
+ runtime loads Sei once and microbatches every reference/alternate sequence pair. `prepare_scoring_batches`
35
+ cache-filters Sei batches when it is given the `DetailStore` that holds `sequence_class_effects`.
36
+
37
+ The primary row reports the class with the largest absolute effect while retaining its sign. The
38
+ `sequence_class_effects` detail table preserves all 40 published class effects. Effects reproduce the official
39
+ workflow: strand-averaged 21,907-profile predictions are nucleosome-occupancy adjusted as a reference/alternate
40
+ pair, projected onto the published class vectors, and subtracted as alternate minus reference.
41
+
42
+ The per-profile predictions behind those effects are an opt-in artifact. A run whose output selection is
43
+ `SeiPlugin.profile_effects_output` also writes `chromatin_profile_effects`: one `.npz` per batch
44
+ (`application/x-npz`) with `variant_ids`, the float32 `effects` matrix of strand-averaged alternate-minus-reference
45
+ predictions for all 21,907 profiles (before nucleosome adjustment, NaN for an unscorable variant), the
46
+ `profile_index` of each column, and `profile_names`, the published name of each column's profile, so a consumer
47
+ maps columns to profiles without the runtime or its resources. The names come from the official archive's
48
+ `target.names`, which `SeiConfiguration.target_names` identifies by digest; scores never read it, so only a run
49
+ that selects the artifact requires it and stages it. It is not part of the score lineage. Altar never decodes the archive; the scoring engine digests
50
+ the file and records it in an `ArtifactStore` with the variants it covers, and batch preparation treats a variant
51
+ without it as a cache miss. The default selection's command line is unchanged; selecting the artifact is a
52
+ different output selection and therefore a different score lineage. In an `altar run` spec, name the artifact in
53
+ the model's `output` and declare an `[artifact_store]`.
54
+
55
+ The initial runtime accepts SNVs on hg19 or hg38. The manifest declares SNV-only variant eligibility, so
56
+ Altar's batch preparation filters out and reports other variant classes before they reach the runtime. The
57
+ runtime records incomplete windows, reference mismatches, unsupported allele symbols, and unsupported IUPAC
58
+ window bases as null-score rows that keep the variant's identity, logs the reason, and continues to score valid
59
+ batch neighbors; the primary table holds scores only. Such a row has `n_classes_scored` 0 and no class rows. The
60
+ binding declares `n_classes_scored` as the row count of `sequence_class_effects`, so batch preparation counts
61
+ the variant as complete on later runs. It also writes a null-score row for an indel in a batch
62
+ staged without that filter, but Altar's scoring engine rejects that row and fails the
63
+ batch rather than store a score for a variant the manifest excludes. A repeated canonical variant fails the
64
+ batch, as in every other runtime. `N` bases in otherwise valid windows
65
+ use Selene's uniform encoding and are flagged in `contains_unk`. That is narrower than Selene's general indel
66
+ machinery and is explicit: unsupported alleles do not receive an unversioned alignment policy. The operational
67
+ `max_abs_class_effect >= 1` prioritization default is not a universal biological cutoff; consumers should
68
+ calibrate prioritization for their cohort and question.
69
+
70
+ The executor lives under `runtimes/sei`. The official Sei implementation and resources are restricted to
71
+ academic and research use; those terms apply independently of this MIT-licensed binding.
@@ -0,0 +1,48 @@
1
+ # Altar Sei binding
2
+
3
+ `altar-sei` is the lightweight control-plane binding for high-throughput Sei variant scoring. It registers
4
+ `SEI` in Altar's canonical model-plugin group and does not install PyTorch, SciPy, the model architecture, or
5
+ the official resources.
6
+
7
+ Each model instance identifies the official checkpoint, projection vectors, histone-profile indices,
8
+ sequence-class metadata, and an immutable runtime image by SHA-256 digest. The image defaults to
9
+ `altar_sei.DEFAULT_IMAGE`, a published image validated against FunctionLab's official Sei framework
10
+ (`runtimes/sei/validation/cpu-official-81f595f.json`). The binding emits one independently retryable task for each staged batch, and the
11
+ runtime loads Sei once and microbatches every reference/alternate sequence pair. `prepare_scoring_batches`
12
+ cache-filters Sei batches when it is given the `DetailStore` that holds `sequence_class_effects`.
13
+
14
+ The primary row reports the class with the largest absolute effect while retaining its sign. The
15
+ `sequence_class_effects` detail table preserves all 40 published class effects. Effects reproduce the official
16
+ workflow: strand-averaged 21,907-profile predictions are nucleosome-occupancy adjusted as a reference/alternate
17
+ pair, projected onto the published class vectors, and subtracted as alternate minus reference.
18
+
19
+ The per-profile predictions behind those effects are an opt-in artifact. A run whose output selection is
20
+ `SeiPlugin.profile_effects_output` also writes `chromatin_profile_effects`: one `.npz` per batch
21
+ (`application/x-npz`) with `variant_ids`, the float32 `effects` matrix of strand-averaged alternate-minus-reference
22
+ predictions for all 21,907 profiles (before nucleosome adjustment, NaN for an unscorable variant), the
23
+ `profile_index` of each column, and `profile_names`, the published name of each column's profile, so a consumer
24
+ maps columns to profiles without the runtime or its resources. The names come from the official archive's
25
+ `target.names`, which `SeiConfiguration.target_names` identifies by digest; scores never read it, so only a run
26
+ that selects the artifact requires it and stages it. It is not part of the score lineage. Altar never decodes the archive; the scoring engine digests
27
+ the file and records it in an `ArtifactStore` with the variants it covers, and batch preparation treats a variant
28
+ without it as a cache miss. The default selection's command line is unchanged; selecting the artifact is a
29
+ different output selection and therefore a different score lineage. In an `altar run` spec, name the artifact in
30
+ the model's `output` and declare an `[artifact_store]`.
31
+
32
+ The initial runtime accepts SNVs on hg19 or hg38. The manifest declares SNV-only variant eligibility, so
33
+ Altar's batch preparation filters out and reports other variant classes before they reach the runtime. The
34
+ runtime records incomplete windows, reference mismatches, unsupported allele symbols, and unsupported IUPAC
35
+ window bases as null-score rows that keep the variant's identity, logs the reason, and continues to score valid
36
+ batch neighbors; the primary table holds scores only. Such a row has `n_classes_scored` 0 and no class rows. The
37
+ binding declares `n_classes_scored` as the row count of `sequence_class_effects`, so batch preparation counts
38
+ the variant as complete on later runs. It also writes a null-score row for an indel in a batch
39
+ staged without that filter, but Altar's scoring engine rejects that row and fails the
40
+ batch rather than store a score for a variant the manifest excludes. A repeated canonical variant fails the
41
+ batch, as in every other runtime. `N` bases in otherwise valid windows
42
+ use Selene's uniform encoding and are flagged in `contains_unk`. That is narrower than Selene's general indel
43
+ machinery and is explicit: unsupported alleles do not receive an unversioned alignment policy. The operational
44
+ `max_abs_class_effect >= 1` prioritization default is not a universal biological cutoff; consumers should
45
+ calibrate prioritization for their cohort and question.
46
+
47
+ The executor lives under `runtimes/sei`. The official Sei implementation and resources are restricted to
48
+ academic and research use; those terms apply independently of this MIT-licensed binding.
@@ -0,0 +1,29 @@
1
+ # Copyright 2026 Riya Sinha
2
+ """Sei model binding for Altar."""
3
+
4
+ from altar_sei.plugin import (
5
+ CHROMATIN_PROFILE_COUNT,
6
+ DEFAULT_IMAGE,
7
+ KIND_SCORING,
8
+ PROFILE_EFFECTS,
9
+ SEQUENCE_CLASS_COUNT,
10
+ SEQUENCE_LENGTH,
11
+ SeiConfiguration,
12
+ SeiPlugin,
13
+ SeiRunInputs,
14
+ SeiShardKind,
15
+ )
16
+
17
+
18
+ __all__ = [
19
+ "CHROMATIN_PROFILE_COUNT",
20
+ "DEFAULT_IMAGE",
21
+ "KIND_SCORING",
22
+ "PROFILE_EFFECTS",
23
+ "SEQUENCE_CLASS_COUNT",
24
+ "SEQUENCE_LENGTH",
25
+ "SeiConfiguration",
26
+ "SeiPlugin",
27
+ "SeiRunInputs",
28
+ "SeiShardKind",
29
+ ]
@@ -0,0 +1,324 @@
1
+ # Copyright 2026 Riya Sinha
2
+ """Backend-neutral Sei binding for sequence-class variant effects."""
3
+
4
+ from __future__ import annotations
5
+ import enum
6
+ from typing import cast
7
+
8
+ from altar.execution import GpuPool, ResourceRequest, Transfer
9
+ from altar.models import (
10
+ ApiVersionRange,
11
+ ArtifactSchema,
12
+ ContainerImageDigest,
13
+ ContainerScoringPlan,
14
+ DetailSchema,
15
+ ModelConfiguration,
16
+ ModelPlugin,
17
+ OutputSelection,
18
+ PluginCapability,
19
+ PluginKind,
20
+ PluginManifest,
21
+ PrioritizationPolicy,
22
+ ReferenceGenomeRunInputs,
23
+ ResourceReference,
24
+ ResultField,
25
+ ResultSchema,
26
+ RuntimeIdentityPolicy,
27
+ RuntimeKind,
28
+ RuntimeRequirement,
29
+ ScoringRequest,
30
+ VariantClass,
31
+ VariantEligibility,
32
+ require_resource_digests,
33
+ resource_transfer,
34
+ single_container_plan,
35
+ )
36
+ from altar.predicates import Col, Ge, Lit, Predicate
37
+ from pydantic import JsonValue
38
+ from pydantic import model_validator
39
+
40
+
41
+ SEQUENCE_LENGTH = 4_096
42
+ CHROMATIN_PROFILE_COUNT = 21_907
43
+ SEQUENCE_CLASS_COUNT = 40
44
+ # Built from commit 81f595f; validated in runtimes/sei/validation/cpu-official-81f595f.json.
45
+ DEFAULT_IMAGE = "riyavsinha/altar-sei@sha256:e6522cdfb01c3ea3ce3f2048a7d8cad0b40e61194a4952db0a41e9f25a2139c1"
46
+
47
+
48
+ class SeiShardKind(enum.StrEnum):
49
+ """Stable execution kind emitted by :class:`SeiPlugin`."""
50
+
51
+ SCORING = "sei_scoring"
52
+
53
+
54
+ KIND_SCORING = SeiShardKind.SCORING
55
+ # 0.0.2 added profile_names, so a consumer maps matrix columns to profiles without the runtime or its resources.
56
+ PROFILE_EFFECTS = ArtifactSchema(
57
+ name="chromatin_profile_effects",
58
+ version="0.0.2",
59
+ description=(
60
+ "One NumPy .npz archive per scoring batch: variant_ids (str, n), effects (float32, n x 21,907; the "
61
+ "strand-averaged alternate-minus-reference prediction of every chromatin profile, before nucleosome "
62
+ "adjustment, NaN for a variant the runtime could not score), profile_index (int32, 21,907; the "
63
+ "model's output column of each effect), and profile_names (str, 21,907; the published name of each "
64
+ "column's chromatin profile: line profile_index + 1 of the official target.names)."
65
+ ),
66
+ media_type="application/x-npz",
67
+ )
68
+ _PROFILE_EFFECTS_EXTENSION = ".npz"
69
+ _SCORE_RESOURCES = ResourceRequest(gpu=GpuPool.NORMAL_GPU, count=1, memory_gb=16)
70
+ _CLASS_EFFECT_THRESHOLD = 1.0
71
+
72
+
73
+ class SeiConfiguration(ModelConfiguration):
74
+ """Immutable official Sei resources and runtime image.
75
+
76
+ Strand averaging, the 4,096 bp input, all 21,907 chromatin profiles, all 40 sequence classes, and
77
+ nucleosome-occupancy adjustment are fixed parts of the published scoring semantics rather than user options.
78
+ ``image`` defaults to :data:`DEFAULT_IMAGE`, the published runtime validated against the official Sei
79
+ framework.
80
+
81
+ ``target_names`` is the official archive's ``target.names``, the published name of each of the 21,907
82
+ chromatin profiles in model-output order. A run that selects the ``chromatin_profile_effects`` artifact
83
+ requires it, to name the archive's columns. No score reads it, so it is never part of the scientific
84
+ projection: setting it keeps the score lineage. The saved configuration includes it only while it is set.
85
+ """
86
+
87
+ weights: ResourceReference
88
+ projection_vectors: ResourceReference
89
+ histone_indices: ResourceReference
90
+ sequence_class_names: ResourceReference
91
+ target_names: ResourceReference | None = None
92
+ image: ContainerImageDigest = DEFAULT_IMAGE
93
+
94
+ @model_validator(mode="after")
95
+ def validate_resources(self) -> SeiConfiguration:
96
+ require_resource_digests(self, owner="Sei")
97
+ return self
98
+
99
+ def to_dict(self) -> dict[str, JsonValue]:
100
+ """Return the saved configuration, without ``target_names`` while it is unset."""
101
+ return _without_unset_target_names(self, super().to_dict())
102
+
103
+ def scientific_dict(self) -> dict[str, JsonValue]:
104
+ """Return the scientific projection, which never includes ``target_names``."""
105
+ value = super().scientific_dict()
106
+ value.pop("target_names", None)
107
+ return value
108
+
109
+
110
+ def _without_unset_target_names(configuration: SeiConfiguration, value: dict[str, JsonValue]) -> dict[str, JsonValue]:
111
+ if configuration.target_names is None:
112
+ value.pop("target_names", None)
113
+ return value
114
+
115
+
116
+ class SeiRunInputs(ReferenceGenomeRunInputs):
117
+ """Per-run reference inputs supported by the official Sei workflow."""
118
+
119
+ supported_genome_builds = frozenset({"hg19", "hg38"})
120
+
121
+
122
+ class SeiPlugin(ModelPlugin):
123
+ """Predicts how a variant changes 40 sequence classes of regulatory activity."""
124
+
125
+ model_type = "SEI"
126
+ manifest = PluginManifest(
127
+ plugin_id="org.kundajelab.altar.model.sei",
128
+ kind=PluginKind.MODEL,
129
+ capabilities=frozenset({PluginCapability.SCORE, PluginCapability.MATERIALIZE_RESULTS}),
130
+ architecture="Sei",
131
+ # The plugin version is part of every score lineage, and the profile names change neither the scores
132
+ # nor a configuration that does not name target_names, so adding them bumps only the artifact's version.
133
+ plugin_version="0.0.2",
134
+ altar_api=ApiVersionRange("0.1.0", "0.2.0"),
135
+ # target_names is optional and defaulted, so every saved 0.0.1 configuration still loads unchanged.
136
+ configuration_schema_version="0.0.1",
137
+ # 0.0.2 dropped the validation status and message columns: the primary table holds scores only, and
138
+ # an unscorable variant is a null-score row whose reason the runtime logs. 0.0.3 gives that row
139
+ # n_classes_scored 0, the row count of sequence_class_effects, so it counts as complete in the cache.
140
+ result_schema=ResultSchema(
141
+ version="0.0.3",
142
+ fields=(
143
+ ResultField("top_class_index", "int", "Top Class Index", "Index with largest absolute class effect"),
144
+ ResultField("top_class_label", "str", "Top Class", "Published label with largest absolute effect"),
145
+ ResultField(
146
+ "top_class_effect",
147
+ "float",
148
+ "Top Class Effect",
149
+ "Signed nucleosome-adjusted alternate-minus-reference projection score",
150
+ ),
151
+ ResultField(
152
+ "max_abs_class_effect",
153
+ "float",
154
+ "Maximum Absolute Class Effect",
155
+ "Absolute value of the largest-magnitude sequence-class effect",
156
+ ),
157
+ ResultField(
158
+ "n_classes_scored",
159
+ "int",
160
+ "Classes Scored",
161
+ "Sequence-class effect rows emitted for the variant: 40, or 0 for a variant the runtime could "
162
+ "not score",
163
+ ),
164
+ ResultField(
165
+ "contains_unk",
166
+ "bool",
167
+ "Contains Unknown Base",
168
+ "Whether the scored reference window contained at least one N encoded uniformly across bases",
169
+ ),
170
+ ),
171
+ ),
172
+ detail_schemas=(
173
+ DetailSchema(
174
+ name="sequence_class_effects",
175
+ version="0.0.1",
176
+ description="All 40 official nucleosome-adjusted Sei sequence-class variant effects.",
177
+ fields=(
178
+ ResultField("class_index", "int", "Class Index", "Zero-based published class index", False),
179
+ ResultField("class_label", "str", "Class Label", "Published short class label", False),
180
+ ResultField("class_name", "str", "Class Name", "Published class description", False),
181
+ ResultField(
182
+ "class_effect",
183
+ "float",
184
+ "Class Effect",
185
+ "Signed nucleosome-adjusted alternate-minus-reference projection score",
186
+ False,
187
+ ),
188
+ ),
189
+ key_fields=("class_index",),
190
+ row_count_field="n_classes_scored",
191
+ ),
192
+ ),
193
+ # The Sei runtime substitutes one reference base; batch preparation filters out indels and MNVs.
194
+ variant_eligibility=VariantEligibility(frozenset({VariantClass.SNV})),
195
+ # Opt-in: the full profile matrix is ~88 kB per variant, so only a run that selects it pays for it.
196
+ artifact_schemas=(PROFILE_EFFECTS,),
197
+ prioritization=PrioritizationPolicy(name="sei-max-absolute-class-effect-v1", version="0.0.1"),
198
+ runtimes=(
199
+ RuntimeRequirement(
200
+ name="model_runtime",
201
+ kind=RuntimeKind.CONTAINER_IMAGE,
202
+ policy=RuntimeIdentityPolicy.DIGEST_REQUIRED,
203
+ ),
204
+ ),
205
+ )
206
+ configuration_schema_id = "org.kundajelab.altar.model.sei.configuration"
207
+ configuration_model = SeiConfiguration
208
+ run_input_model = SeiRunInputs
209
+ default_output = OutputSelection(
210
+ outputs=("scores", "sequence_class_effects"),
211
+ reducer="official_nucleosome_adjusted_sequence_class_effect_v1",
212
+ )
213
+ profile_effects_output = OutputSelection(
214
+ outputs=(*default_output.outputs, PROFILE_EFFECTS.name),
215
+ reducer=default_output.reducer,
216
+ )
217
+ """The output selection that also writes the per-batch ``chromatin_profile_effects`` artifact.
218
+
219
+ It requires :attr:`SeiConfiguration.target_names`, which the runtime copies into the archive as
220
+ ``profile_names``."""
221
+
222
+ def validate_output_selection(self, output: OutputSelection) -> OutputSelection:
223
+ """Accept the default selection, or the default plus the opt-in profile-effect artifact."""
224
+ if output in (self.default_output, self.profile_effects_output):
225
+ return output
226
+ msg = (
227
+ f"{self.model_type} supports output selections {self.default_output.to_dict()} and "
228
+ f"{self.profile_effects_output.to_dict()}, got {output.to_dict()}"
229
+ )
230
+ raise ValueError(msg)
231
+
232
+ def scoring_runtime_identities(self, configuration: ModelConfiguration) -> dict[str, str]:
233
+ """Record the configured Sei image as the scoring plan's one runtime."""
234
+ return {"model_runtime": self.parse_configuration_as(configuration, SeiConfiguration).image}
235
+
236
+ def build_scoring_plan(self, request: ScoringRequest) -> ContainerScoringPlan:
237
+ configuration = self.parse_configuration_as(request.configuration, SeiConfiguration)
238
+ run_inputs = self.parse_run_inputs_as(request.run_inputs, SeiRunInputs)
239
+ output = self.validate_output_selection(request.output)
240
+ layout, resolver = request.layout, request.resolver
241
+ resources = (
242
+ (configuration.weights, "sei.pth", "--weights", "--weights-digest"),
243
+ (configuration.projection_vectors, "projvec_targets.npy", "--projection-vectors", "--projection-digest"),
244
+ (configuration.histone_indices, "histone_inds.npy", "--histone-indices", "--histone-indices-digest"),
245
+ (configuration.sequence_class_names, "seqclass.names", "--class-names", "--class-names-digest"),
246
+ )
247
+ command = ["sei-score", "score"]
248
+ inputs: list[Transfer] = []
249
+ for resource, filename, path_argument, digest_argument in resources:
250
+ logical = layout.model_resource_file(request.model_id, filename)
251
+ command += [path_argument, resolver.resolve(logical), digest_argument, cast("str", resource.digest)]
252
+ inputs.append(resource_transfer(resource, logical, locality_key=request.model_id))
253
+ genome_logical = layout.genome_fasta(request.genome_label)
254
+ variants_logical = layout.model_variants_file(request.job_id, request.model_id, request.scoring_batch)
255
+ primary_logical = layout.scoring_result_file(request.job_id, request.model_id, request.scoring_batch)
256
+ details_logical = layout.detail_result_file(
257
+ request.job_id, request.model_id, "sequence_class_effects", request.scoring_batch
258
+ )
259
+ command += [
260
+ "--genome",
261
+ resolver.resolve(genome_logical),
262
+ "--genome-digest",
263
+ cast("str", run_inputs.genome.digest),
264
+ "--variants",
265
+ resolver.resolve(variants_logical),
266
+ "--primary-output",
267
+ resolver.resolve(primary_logical),
268
+ "--detail-output",
269
+ resolver.resolve(details_logical),
270
+ ]
271
+ if PROFILE_EFFECTS.name in output.outputs:
272
+ target_names = configuration.target_names
273
+ if target_names is None:
274
+ msg = (
275
+ f"selecting the {PROFILE_EFFECTS.name} artifact requires SeiConfiguration.target_names, the "
276
+ "official target.names that names the archive's 21,907 profile columns"
277
+ )
278
+ raise ValueError(msg)
279
+ target_names_logical = layout.model_resource_file(request.model_id, "target.names")
280
+ inputs.append(resource_transfer(target_names, target_names_logical, locality_key=request.model_id))
281
+ profile_effects_logical = layout.artifact_result_file(
282
+ request.job_id,
283
+ request.model_id,
284
+ PROFILE_EFFECTS.name,
285
+ request.scoring_batch,
286
+ extension=_PROFILE_EFFECTS_EXTENSION,
287
+ )
288
+ command += [
289
+ "--target-names",
290
+ resolver.resolve(target_names_logical),
291
+ "--target-names-digest",
292
+ cast("str", target_names.digest),
293
+ "--profile-effects-output",
294
+ resolver.resolve(profile_effects_logical),
295
+ ]
296
+ inputs.append(resource_transfer(run_inputs.genome, genome_logical))
297
+ return single_container_plan(
298
+ self,
299
+ request,
300
+ kind=KIND_SCORING,
301
+ image=configuration.image,
302
+ command=command,
303
+ resources=_SCORE_RESOURCES,
304
+ inputs=inputs,
305
+ artifact_extensions={PROFILE_EFFECTS.name: _PROFILE_EFFECTS_EXTENSION},
306
+ )
307
+
308
+ def prioritize_predicate(self) -> Predicate:
309
+ # Operational default only; Sei does not publish a universal biological cutoff.
310
+ return Ge(Col("max_abs_class_effect"), Lit(_CLASS_EFFECT_THRESHOLD))
311
+
312
+
313
+ __all__ = [
314
+ "CHROMATIN_PROFILE_COUNT",
315
+ "DEFAULT_IMAGE",
316
+ "KIND_SCORING",
317
+ "PROFILE_EFFECTS",
318
+ "SEQUENCE_CLASS_COUNT",
319
+ "SEQUENCE_LENGTH",
320
+ "SeiConfiguration",
321
+ "SeiPlugin",
322
+ "SeiRunInputs",
323
+ "SeiShardKind",
324
+ ]
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "altar-sei"
3
+ version = "0.3.0"
4
+ description = "Sei model binding for large-scale Altar variant scoring"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [{ name = "Riya Sinha" }]
8
+ license = "MIT"
9
+ license-files = ["LICENSE"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Science/Research",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
18
+ "Typing :: Typed",
19
+ ]
20
+ dependencies = ["altar>=0.3.0,<0.4"]
21
+
22
+ [project.urls]
23
+ Documentation = "https://kundajelab.github.io/altar/"
24
+
25
+ [project.entry-points."altar.model_plugins"]
26
+ SEI = "altar_sei:SeiPlugin"
27
+
28
+ [project.optional-dependencies]
29
+ test = ["pytest>=8", "pytest-asyncio>=0.24"]
30
+
31
+ [build-system]
32
+ requires = ["hatchling"]
33
+ build-backend = "hatchling.build"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["altar_sei"]
37
+
38
+ [tool.pytest.ini_options]
39
+ asyncio_mode = "auto"
40
+ asyncio_default_fixture_loop_scope = "function"
41
+ testpaths = ["tests"]
42
+
43
+ [tool.mypy]
44
+ python_version = "3.12"
45
+ strict = true
46
+ ignore_missing_imports = true