zipoop 0.1.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.
- zipoop-0.1.0/LICENSE +21 -0
- zipoop-0.1.0/PKG-INFO +51 -0
- zipoop-0.1.0/README.md +32 -0
- zipoop-0.1.0/pyproject.toml +35 -0
- zipoop-0.1.0/setup.cfg +4 -0
- zipoop-0.1.0/tests/test_zipoop.py +123 -0
- zipoop-0.1.0/zipoop/__init__.py +11 -0
- zipoop-0.1.0/zipoop/cli.py +30 -0
- zipoop-0.1.0/zipoop/data/encode-panel_1885.json +32502 -0
- zipoop-0.1.0/zipoop/dataset.py +119 -0
- zipoop-0.1.0/zipoop/decoder.py +62 -0
- zipoop-0.1.0/zipoop/evaluation.py +77 -0
- zipoop-0.1.0/zipoop/masks.py +73 -0
- zipoop-0.1.0/zipoop/network.py +102 -0
- zipoop-0.1.0/zipoop/panel.py +26 -0
- zipoop-0.1.0/zipoop/shard.py +105 -0
- zipoop-0.1.0/zipoop/teacher.py +43 -0
- zipoop-0.1.0/zipoop.egg-info/PKG-INFO +51 -0
- zipoop-0.1.0/zipoop.egg-info/SOURCES.txt +21 -0
- zipoop-0.1.0/zipoop.egg-info/dependency_links.txt +1 -0
- zipoop-0.1.0/zipoop.egg-info/entry_points.txt +2 -0
- zipoop-0.1.0/zipoop.egg-info/requires.txt +5 -0
- zipoop-0.1.0/zipoop.egg-info/top_level.txt +1 -0
zipoop-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zatumout
|
|
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.
|
zipoop-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zipoop
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Decoder and reader for Zipoop teacher shards (per-window neural compression of ENCODE signal tracks)
|
|
5
|
+
Author: zatumout
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Dataset, https://huggingface.co/datasets/zatumout/zipoop-teacher-shards
|
|
8
|
+
Keywords: genomics,ENCODE,neural compression,distillation
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: numpy>=1.26
|
|
15
|
+
Requires-Dist: torch>=2.2
|
|
16
|
+
Provides-Extra: test
|
|
17
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# zipoop
|
|
21
|
+
|
|
22
|
+
Decoder and reader for the Zipoop teacher shards (dataset: https://huggingface.co/datasets/zatumout/zipoop-teacher-shards): 89,438 windows of 32 kb, 1,885 ENCODE tracks, stored as one `<chrom>.npz` per chromosome (`weights_npz`, with `MANIFEST.tsv`).
|
|
23
|
+
|
|
24
|
+
## Data layout
|
|
25
|
+
|
|
26
|
+
`<root>/<chrom>.npz` (25 files) plus `MANIFEST.tsv` (chrom, windows, members, bytes, sha256). Each file is a deflate zip with members `<window>/<key>.npy` (161 arrays per window), windows in genome order. Window names are `<chrom>_<start>_<end>`.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from zipoop import Dataset
|
|
32
|
+
with Dataset("/path/to/weights_npz") as ds:
|
|
33
|
+
w = ds.load_window(ds.windows("chr21")[0]) # TeacherWindow: key, dna (L,), signal (L, T), valid (L, T)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
zipoop verify /path/to/weights_npz # size + sha256 against MANIFEST.tsv
|
|
38
|
+
zipoop show /path/to/weights_npz chr21_10027008_10059776
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Lower level: `Dataset.read(window)` gives the parsed `Shard`, `Decoder(shard).decode(start, end, tracks)` decodes any slice or track subset. Evaluation helpers (`window_pearson`, `summarize`, `calibrate`, `restore_real`) are in `zipoop.evaluation`.
|
|
42
|
+
|
|
43
|
+
Rules baked into `load_window` (see `zipoop/teacher.py`): RNA-seq tracks are 0 and valid where unrecorded; other tracks stay missing there. Opening a chromosome file parses its whole zip directory (chr1 about 3.6 s and 0.9 GB), so keep the `Dataset` open; forked DataLoader workers reopen their own handles.
|
|
44
|
+
|
|
45
|
+
Only the shard variant in this dataset is supported (hybrid `pooper-dna-v3`, max-pool, sinusoidal positions, mask `track-rle-lzma-v2`); other variants raise a clear error.
|
|
46
|
+
|
|
47
|
+
Tests: `ZIPOOP_DATA=<weights_npz> pytest tests` (the pooper-equivalence test also needs the original shards and the pooper package).
|
|
48
|
+
|
|
49
|
+
## License and data terms
|
|
50
|
+
|
|
51
|
+
The code is MIT-licensed (see `LICENSE`; keep the copyright notice "zatumout"). The shard dataset is released separately under CC BY 4.0 (credit: zatumout), commercial use and model distillation allowed. The shards and `zipoop/data/encode-panel_1885.json` are derived from public ENCODE tracks: when you use them, also cite and acknowledge the ENCODE Consortium as described at https://www.encodeproject.org/help/citing-encode.
|
zipoop-0.1.0/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# zipoop
|
|
2
|
+
|
|
3
|
+
Decoder and reader for the Zipoop teacher shards (dataset: https://huggingface.co/datasets/zatumout/zipoop-teacher-shards): 89,438 windows of 32 kb, 1,885 ENCODE tracks, stored as one `<chrom>.npz` per chromosome (`weights_npz`, with `MANIFEST.tsv`).
|
|
4
|
+
|
|
5
|
+
## Data layout
|
|
6
|
+
|
|
7
|
+
`<root>/<chrom>.npz` (25 files) plus `MANIFEST.tsv` (chrom, windows, members, bytes, sha256). Each file is a deflate zip with members `<window>/<key>.npy` (161 arrays per window), windows in genome order. Window names are `<chrom>_<start>_<end>`.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from zipoop import Dataset
|
|
13
|
+
with Dataset("/path/to/weights_npz") as ds:
|
|
14
|
+
w = ds.load_window(ds.windows("chr21")[0]) # TeacherWindow: key, dna (L,), signal (L, T), valid (L, T)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
zipoop verify /path/to/weights_npz # size + sha256 against MANIFEST.tsv
|
|
19
|
+
zipoop show /path/to/weights_npz chr21_10027008_10059776
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Lower level: `Dataset.read(window)` gives the parsed `Shard`, `Decoder(shard).decode(start, end, tracks)` decodes any slice or track subset. Evaluation helpers (`window_pearson`, `summarize`, `calibrate`, `restore_real`) are in `zipoop.evaluation`.
|
|
23
|
+
|
|
24
|
+
Rules baked into `load_window` (see `zipoop/teacher.py`): RNA-seq tracks are 0 and valid where unrecorded; other tracks stay missing there. Opening a chromosome file parses its whole zip directory (chr1 about 3.6 s and 0.9 GB), so keep the `Dataset` open; forked DataLoader workers reopen their own handles.
|
|
25
|
+
|
|
26
|
+
Only the shard variant in this dataset is supported (hybrid `pooper-dna-v3`, max-pool, sinusoidal positions, mask `track-rle-lzma-v2`); other variants raise a clear error.
|
|
27
|
+
|
|
28
|
+
Tests: `ZIPOOP_DATA=<weights_npz> pytest tests` (the pooper-equivalence test also needs the original shards and the pooper package).
|
|
29
|
+
|
|
30
|
+
## License and data terms
|
|
31
|
+
|
|
32
|
+
The code is MIT-licensed (see `LICENSE`; keep the copyright notice "zatumout"). The shard dataset is released separately under CC BY 4.0 (credit: zatumout), commercial use and model distillation allowed. The shards and `zipoop/data/encode-panel_1885.json` are derived from public ENCODE tracks: when you use them, also cite and acknowledge the ENCODE Consortium as described at https://www.encodeproject.org/help/citing-encode.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zipoop"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Decoder and reader for Zipoop teacher shards (per-window neural compression of ENCODE signal tracks)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{name = "zatumout"}]
|
|
14
|
+
keywords = ["genomics", "ENCODE", "neural compression", "distillation"]
|
|
15
|
+
classifiers = ["Programming Language :: Python :: 3", "Topic :: Scientific/Engineering :: Bio-Informatics"]
|
|
16
|
+
dependencies = ["numpy>=1.26", "torch>=2.2"]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
test = ["pytest>=8"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Dataset = "https://huggingface.co/datasets/zatumout/zipoop-teacher-shards"
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
zipoop = "zipoop.cli:main"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.packages.find]
|
|
28
|
+
include = ["zipoop*"]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.package-data]
|
|
31
|
+
zipoop = ["data/*.json"]
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
testpaths = ["tests"]
|
|
35
|
+
addopts = "-q"
|
zipoop-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Needs the real dataset: set ZIPOOP_DATA to the merged weights_npz directory (tests skip otherwise).
|
|
2
|
+
The equivalence tests additionally need the original per-window shards (ZIPOOP_SINGLE, default
|
|
3
|
+
/mnt/e/zipoop/weights) and the pooper package on the path."""
|
|
4
|
+
import os
|
|
5
|
+
import random
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pytest
|
|
10
|
+
import torch
|
|
11
|
+
|
|
12
|
+
import zipoop
|
|
13
|
+
from zipoop.masks import decode_mask
|
|
14
|
+
from zipoop.network import ModelConfig
|
|
15
|
+
|
|
16
|
+
DATA = os.environ.get("ZIPOOP_DATA")
|
|
17
|
+
SINGLE = Path(os.environ.get("ZIPOOP_SINGLE", "/mnt/e/zipoop/weights"))
|
|
18
|
+
needs_data = pytest.mark.skipif(not DATA, reason="ZIPOOP_DATA not set")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(scope="module")
|
|
22
|
+
def ds():
|
|
23
|
+
with zipoop.Dataset(DATA) as d:
|
|
24
|
+
yield d
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture(scope="module")
|
|
28
|
+
def windows(ds):
|
|
29
|
+
ws = ds.windows("chr21")
|
|
30
|
+
return random.Random(0).sample(ws, 3)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@needs_data
|
|
34
|
+
def test_manifest_verifies_small_chromosomes():
|
|
35
|
+
assert zipoop.verify(DATA, {"chrM", "chr22"}) == []
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@needs_data
|
|
39
|
+
def test_window_listing_is_genome_ordered(ds):
|
|
40
|
+
starts = [int(w.split("_")[1]) for w in ds.windows("chr21")]
|
|
41
|
+
assert starts == sorted(starts)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@needs_data
|
|
45
|
+
def test_load_window_shapes_and_rna_rule(ds, windows):
|
|
46
|
+
w = ds.load_window(windows[0])
|
|
47
|
+
L, T = w.signal.shape
|
|
48
|
+
assert (L, T) == (32768, 1885) and w.dna.shape == (L,) and w.valid.shape == (L, T)
|
|
49
|
+
zero = torch.from_numpy(zipoop.zero_tracks())
|
|
50
|
+
assert w.valid[:, zero].all()
|
|
51
|
+
assert torch.isfinite(w.signal).all()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@needs_data
|
|
55
|
+
def test_slice_and_track_subset_match_full_decode(ds, windows):
|
|
56
|
+
dec = zipoop.Decoder(ds.read(windows[0]))
|
|
57
|
+
full = dec.decode()
|
|
58
|
+
part = dec.decode(1000, 5000, tracks=[3, 1884, 7])
|
|
59
|
+
expected = full[1000:5000][:, [3, 1884, 7]]
|
|
60
|
+
assert torch.allclose(part, expected, atol=1e-5, equal_nan=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@needs_data
|
|
64
|
+
def test_region_is_concatenation(ds):
|
|
65
|
+
ws = ds.windows("chr22")[:2]
|
|
66
|
+
r = ds.load_region(ws)
|
|
67
|
+
assert r.signal.shape[0] == 2 * 32768 and r.key == "+".join(ws)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@needs_data
|
|
71
|
+
def test_unknown_window_and_chrom(ds):
|
|
72
|
+
with pytest.raises(KeyError):
|
|
73
|
+
ds.read("chr21_1_2")
|
|
74
|
+
with pytest.raises(FileNotFoundError):
|
|
75
|
+
ds.windows("chrZ")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@needs_data
|
|
79
|
+
def test_bitwise_identical_to_pooper(ds, windows):
|
|
80
|
+
ref = pytest.importorskip("pooper.teacher_io")
|
|
81
|
+
from pooper.hybrid import archive_decoder
|
|
82
|
+
for w in windows:
|
|
83
|
+
path = SINGLE / "chr21" / f"{w}.npz"
|
|
84
|
+
if not path.exists():
|
|
85
|
+
pytest.skip("original shards not available")
|
|
86
|
+
a, b = ds.load_window(w), ref.load_window(path)
|
|
87
|
+
assert a.key == b.key
|
|
88
|
+
for name in ("dna", "signal", "valid"):
|
|
89
|
+
assert torch.equal(getattr(a, name), getattr(b, name)), name
|
|
90
|
+
old = archive_decoder(str(SINGLE / "chr21" / f"{windows[0]}.npz"))
|
|
91
|
+
new = zipoop.Decoder(ds.read(windows[0]))
|
|
92
|
+
for args in ({}, {"start": 4096, "end": 9000, "tracks": [0, 5, 1884]}, {"missing_nan": False}):
|
|
93
|
+
x, y = new.decode(**args), old.decode(**args)
|
|
94
|
+
assert torch.equal(torch.isnan(x), torch.isnan(y))
|
|
95
|
+
assert torch.equal(x.nan_to_num(), y.nan_to_num())
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_model_config_rejects_unsupported_variants():
|
|
99
|
+
ok = {"width": 48, "rank": 48, "transformer_layers": 2, "attention_heads": 4, "downsample_levels": 7,
|
|
100
|
+
"pooling": "max", "position_encoding": "sinusoidal", "pair_channels": 0, "head_nonlinear_rank": 0}
|
|
101
|
+
assert ModelConfig.from_json(ok).width == 48
|
|
102
|
+
for bad in ({"pooling": "avg"}, {"position_encoding": "rope"}, {"pair_channels": 4}, {"head_nonlinear_rank": 8}):
|
|
103
|
+
with pytest.raises(ValueError):
|
|
104
|
+
ModelConfig.from_json({**ok, **bad})
|
|
105
|
+
legacy = {k: v for k, v in ok.items() if k != "pooling"}
|
|
106
|
+
with pytest.raises(ValueError):
|
|
107
|
+
ModelConfig.from_json(legacy)
|
|
108
|
+
with pytest.raises(ValueError):
|
|
109
|
+
ModelConfig.from_json({**ok, "width": 50})
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_decode_mask_rejects_garbage():
|
|
113
|
+
with pytest.raises(ValueError):
|
|
114
|
+
decode_mask(b"\x05\x00", (10, 3))
|
|
115
|
+
with pytest.raises(ValueError):
|
|
116
|
+
decode_mask(b"\x80", (10, 1))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@needs_data
|
|
120
|
+
def test_dataset_is_picklable_for_spawn_workers(ds):
|
|
121
|
+
import pickle
|
|
122
|
+
clone = pickle.loads(pickle.dumps(ds))
|
|
123
|
+
assert clone.chroms() == ds.chroms()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Zipoop: decoder and reader for the per-window teacher shards (merged per-chromosome npz)."""
|
|
2
|
+
from .dataset import Dataset, verify
|
|
3
|
+
from .decoder import Decoder
|
|
4
|
+
from .evaluation import calibrate, restore_real, summarize, window_pearson
|
|
5
|
+
from .panel import panel_tracks, zero_tracks
|
|
6
|
+
from .shard import Shard, read_shard
|
|
7
|
+
from .teacher import TeacherWindow, load_window
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
__all__ = ["Dataset", "verify", "Decoder", "Shard", "read_shard", "TeacherWindow", "load_window",
|
|
11
|
+
"panel_tracks", "zero_tracks", "calibrate", "restore_real", "summarize", "window_pearson"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .dataset import Dataset, verify
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main(argv=None):
|
|
8
|
+
parser = argparse.ArgumentParser(prog="zipoop", description="Zipoop teacher-shard dataset tools")
|
|
9
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
10
|
+
v = sub.add_parser("verify", help="check <root>/*.npz against MANIFEST.tsv (size + sha256)")
|
|
11
|
+
v.add_argument("root")
|
|
12
|
+
v.add_argument("--chroms", default="", help="comma-separated subset")
|
|
13
|
+
v.add_argument("--workers", type=int, default=4, help="files hashed in parallel")
|
|
14
|
+
s = sub.add_parser("show", help="decode one window and print its shapes")
|
|
15
|
+
s.add_argument("root")
|
|
16
|
+
s.add_argument("window")
|
|
17
|
+
args = parser.parse_args(argv)
|
|
18
|
+
if args.cmd == "verify":
|
|
19
|
+
chroms = set(args.chroms.split(",")) if args.chroms else None
|
|
20
|
+
bad = verify(args.root, chroms, workers=args.workers, progress=lambda c, problem: print(f"{c}: {problem or 'ok'}", flush=True))
|
|
21
|
+
print(f"{len(bad)} problem(s)" if bad else "all files verified")
|
|
22
|
+
return 1 if bad else 0
|
|
23
|
+
with Dataset(args.root) as ds:
|
|
24
|
+
w = ds.load_window(args.window)
|
|
25
|
+
print(f"{w.key}: dna {tuple(w.dna.shape)}, signal {tuple(w.signal.shape)}, valid fraction {w.valid.float().mean():.4f}")
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
sys.exit(main())
|