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.
- altar_sei-0.3.0/.gitignore +33 -0
- altar_sei-0.3.0/LICENSE +21 -0
- altar_sei-0.3.0/PKG-INFO +71 -0
- altar_sei-0.3.0/README.md +48 -0
- altar_sei-0.3.0/altar_sei/__init__.py +29 -0
- altar_sei-0.3.0/altar_sei/plugin.py +324 -0
- altar_sei-0.3.0/altar_sei/py.typed +1 -0
- altar_sei-0.3.0/pyproject.toml +46 -0
- altar_sei-0.3.0/tests/test_end_to_end.py +159 -0
- altar_sei-0.3.0/tests/test_golden_lineage_ids.py +183 -0
- altar_sei-0.3.0/tests/test_plugin.py +463 -0
- altar_sei-0.3.0/tests/test_preparation.py +181 -0
|
@@ -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
|
+
|
altar_sei-0.3.0/LICENSE
ADDED
|
@@ -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.
|
altar_sei-0.3.0/PKG-INFO
ADDED
|
@@ -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
|