zipoop 0.1.0__tar.gz → 0.2.1__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.
Files changed (29) hide show
  1. zipoop-0.2.1/PKG-INFO +68 -0
  2. zipoop-0.2.1/README.md +48 -0
  3. {zipoop-0.1.0 → zipoop-0.2.1}/pyproject.toml +4 -3
  4. zipoop-0.2.1/tests/test_eval_subwindow.py +67 -0
  5. zipoop-0.2.1/tests/test_v4_format.py +60 -0
  6. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/__init__.py +3 -3
  7. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/evaluation.py +53 -0
  8. zipoop-0.2.1/zipoop/shard.py +158 -0
  9. zipoop-0.2.1/zipoop.egg-info/PKG-INFO +68 -0
  10. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop.egg-info/SOURCES.txt +2 -0
  11. zipoop-0.1.0/PKG-INFO +0 -51
  12. zipoop-0.1.0/README.md +0 -32
  13. zipoop-0.1.0/zipoop/shard.py +0 -105
  14. zipoop-0.1.0/zipoop.egg-info/PKG-INFO +0 -51
  15. {zipoop-0.1.0 → zipoop-0.2.1}/LICENSE +0 -0
  16. {zipoop-0.1.0 → zipoop-0.2.1}/setup.cfg +0 -0
  17. {zipoop-0.1.0 → zipoop-0.2.1}/tests/test_zipoop.py +0 -0
  18. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/cli.py +0 -0
  19. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/data/encode-panel_1885.json +0 -0
  20. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/dataset.py +0 -0
  21. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/decoder.py +0 -0
  22. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/masks.py +0 -0
  23. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/network.py +0 -0
  24. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/panel.py +0 -0
  25. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop/teacher.py +0 -0
  26. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop.egg-info/dependency_links.txt +0 -0
  27. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop.egg-info/entry_points.txt +0 -0
  28. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop.egg-info/requires.txt +0 -0
  29. {zipoop-0.1.0 → zipoop-0.2.1}/zipoop.egg-info/top_level.txt +0 -0
zipoop-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: zipoop
3
+ Version: 0.2.1
4
+ Summary: Decoder and reader for Zipoop teacher shards (per-window neural compression of ENCODE signal tracks), v3 and v4 archives
5
+ Author: zatumout
6
+ License-Expression: MIT
7
+ Project-URL: Dataset, https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4
8
+ Project-URL: DatasetV3, https://huggingface.co/datasets/zatumout/zipoop_32k1885_010
9
+ Keywords: genomics,ENCODE,neural compression,distillation
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=1.26
16
+ Requires-Dist: torch>=2.2
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=8; extra == "test"
19
+ Dynamic: license-file
20
+
21
+ # zipoop
22
+
23
+ Decoder and reader for the Zipoop teacher shards: 89,438 windows of 32 kb, 1,885 ENCODE tracks (GRCh38), stored as one `<chrom>.npz` per chromosome plus `MANIFEST.tsv`.
24
+
25
+ Two equivalent releases are published:
26
+
27
+ - [zatumout/zipoop_32k1885_010_v4](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4) — `pooper-dna-v4`, 89.3 GiB, **requires `zipoop>=0.2`**;
28
+ - [zatumout/zipoop_32k1885_010](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010) — the older `pooper-dna-v3` encoding, 128.4 GiB, read by 0.1.x as well.
29
+
30
+ Both decode to the same signals; v4 only changes how the arrays are stored (byte-shuffled members, LZMA, and a float16 sparse FFT with one float32 scale per track).
31
+
32
+ ## Data layout
33
+
34
+ `<root>/<chrom>.npz` (25 files) plus `MANIFEST.tsv` (chrom, windows, members, bytes, sha256). Each file is a zip with members `<window>/<key>.npy` (161 arrays per window in v3, 164 in v4), windows in genome order. Window names are `<chrom>_<start>_<end>`.
35
+
36
+ ## Usage
37
+
38
+ ```python
39
+ from zipoop import Dataset
40
+ with Dataset("/path/to/weights_npz") as ds:
41
+ w = ds.load_window(ds.windows("chr21")[0]) # TeacherWindow: key, dna (L,), signal (L, T), valid (L, T)
42
+ ```
43
+
44
+ ```
45
+ zipoop verify /path/to/weights_npz # size + sha256 against MANIFEST.tsv
46
+ zipoop show /path/to/weights_npz chr21_10027008_10059776
47
+ ```
48
+
49
+ 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`.
50
+
51
+ `zipoop.evaluation.subwindow_metrics(pred, target, valid, subwindow=32768)` is the evaluator frozen in the D5 pipeline: it splits the length axis into contiguous sub-windows (4 x 32,768 bp inside a 131,072 bp segment) and returns `pearson` / `proj` / `nmse` / `total_err` / `eligible` arrays of shape (sub-windows, tracks), with the same clamps and the same eligibility rule (at least 2 observed positions and a non-constant target). The aggregation used by the frozen pipeline is the mean Pearson over eligible pairs and the median `proj` / `nmse`.
52
+
53
+ 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.
54
+
55
+ ## Supported formats
56
+
57
+ | archive | `format` | `hybrid_format` | storage |
58
+ |---|---|---|---|
59
+ | v3 | `pooper-dna-v3` | `fft-plus-dna-residual-v1` | deflate, `fft_coefficients` complex64 |
60
+ | v4 | `pooper-dna-v4` | `fft-plus-dna-residual-v2` | LZMA, byte-shuffled floats, `fft_packed` float16 + `fft_scale` float32 |
61
+
62
+ `read_shard` accepts both and rejects anything else with a clear error. The v4 decode path is verified against v3: parameters, masks and calibrations are bit-identical, the FFT payload differs only by float16 rounding (max relative error about 4.7e-4), and decoded windows reach a per-track Pearson of at least 0.9999997 against the v3 decode.
63
+
64
+ Tests: `ZIPOOP_DATA=<weights_npz> pytest tests` (the pooper-equivalence test also needs the original shards and the pooper package). The v4 storage encoding has dataset-free unit tests in `tests/test_v4_format.py`.
65
+
66
+ ## License and data terms
67
+
68
+ 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.2.1/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # zipoop
2
+
3
+ Decoder and reader for the Zipoop teacher shards: 89,438 windows of 32 kb, 1,885 ENCODE tracks (GRCh38), stored as one `<chrom>.npz` per chromosome plus `MANIFEST.tsv`.
4
+
5
+ Two equivalent releases are published:
6
+
7
+ - [zatumout/zipoop_32k1885_010_v4](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4) — `pooper-dna-v4`, 89.3 GiB, **requires `zipoop>=0.2`**;
8
+ - [zatumout/zipoop_32k1885_010](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010) — the older `pooper-dna-v3` encoding, 128.4 GiB, read by 0.1.x as well.
9
+
10
+ Both decode to the same signals; v4 only changes how the arrays are stored (byte-shuffled members, LZMA, and a float16 sparse FFT with one float32 scale per track).
11
+
12
+ ## Data layout
13
+
14
+ `<root>/<chrom>.npz` (25 files) plus `MANIFEST.tsv` (chrom, windows, members, bytes, sha256). Each file is a zip with members `<window>/<key>.npy` (161 arrays per window in v3, 164 in v4), windows in genome order. Window names are `<chrom>_<start>_<end>`.
15
+
16
+ ## Usage
17
+
18
+ ```python
19
+ from zipoop import Dataset
20
+ with Dataset("/path/to/weights_npz") as ds:
21
+ w = ds.load_window(ds.windows("chr21")[0]) # TeacherWindow: key, dna (L,), signal (L, T), valid (L, T)
22
+ ```
23
+
24
+ ```
25
+ zipoop verify /path/to/weights_npz # size + sha256 against MANIFEST.tsv
26
+ zipoop show /path/to/weights_npz chr21_10027008_10059776
27
+ ```
28
+
29
+ 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`.
30
+
31
+ `zipoop.evaluation.subwindow_metrics(pred, target, valid, subwindow=32768)` is the evaluator frozen in the D5 pipeline: it splits the length axis into contiguous sub-windows (4 x 32,768 bp inside a 131,072 bp segment) and returns `pearson` / `proj` / `nmse` / `total_err` / `eligible` arrays of shape (sub-windows, tracks), with the same clamps and the same eligibility rule (at least 2 observed positions and a non-constant target). The aggregation used by the frozen pipeline is the mean Pearson over eligible pairs and the median `proj` / `nmse`.
32
+
33
+ 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.
34
+
35
+ ## Supported formats
36
+
37
+ | archive | `format` | `hybrid_format` | storage |
38
+ |---|---|---|---|
39
+ | v3 | `pooper-dna-v3` | `fft-plus-dna-residual-v1` | deflate, `fft_coefficients` complex64 |
40
+ | v4 | `pooper-dna-v4` | `fft-plus-dna-residual-v2` | LZMA, byte-shuffled floats, `fft_packed` float16 + `fft_scale` float32 |
41
+
42
+ `read_shard` accepts both and rejects anything else with a clear error. The v4 decode path is verified against v3: parameters, masks and calibrations are bit-identical, the FFT payload differs only by float16 rounding (max relative error about 4.7e-4), and decoded windows reach a per-track Pearson of at least 0.9999997 against the v3 decode.
43
+
44
+ Tests: `ZIPOOP_DATA=<weights_npz> pytest tests` (the pooper-equivalence test also needs the original shards and the pooper package). The v4 storage encoding has dataset-free unit tests in `tests/test_v4_format.py`.
45
+
46
+ ## License and data terms
47
+
48
+ 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.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
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)"
7
+ version = "0.2.1"
8
+ description = "Decoder and reader for Zipoop teacher shards (per-window neural compression of ENCODE signal tracks), v3 and v4 archives"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  license = "MIT"
@@ -19,7 +19,8 @@ dependencies = ["numpy>=1.26", "torch>=2.2"]
19
19
  test = ["pytest>=8"]
20
20
 
21
21
  [project.urls]
22
- Dataset = "https://huggingface.co/datasets/zatumout/zipoop-teacher-shards"
22
+ Dataset = "https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4"
23
+ DatasetV3 = "https://huggingface.co/datasets/zatumout/zipoop_32k1885_010"
23
24
 
24
25
  [project.scripts]
25
26
  zipoop = "zipoop.cli:main"
@@ -0,0 +1,67 @@
1
+ """Parity tests for zipoop.evaluation.subwindow_metrics against the frozen D5 evaluator."""
2
+ import numpy as np
3
+ import torch
4
+
5
+ from zipoop.evaluation import subwindow_metrics
6
+
7
+
8
+ def frozen_subwindow_metrics(p, y, m, N, W):
9
+ """Verbatim copy of the evaluator frozen in zipoop_32k1885_010_128k2rd_D5_frozen_20261017/d5_genome.py."""
10
+ out = {k: [] for k in ("pearson", "proj", "nmse", "total_err", "eligible")}
11
+ for k in range(N):
12
+ s = slice(k * W, (k + 1) * W)
13
+ mf = m[s].float(); n = mf.sum(0).clamp_min(1)
14
+ yc = (y[s] - (y[s] * mf).sum(0) / n) * mf; pc = (p[s] - (p[s] * mf).sum(0) / n) * mf
15
+ vy = yc.square().sum(0); yy = (y[s].square() * mf).sum(0)
16
+ out["pearson"].append((pc * yc).sum(0) / (pc.square().sum(0) * vy).clamp_min(1e-24).sqrt())
17
+ out["proj"].append((p[s] * y[s] * mf).sum(0) / yy.clamp_min(1e-30))
18
+ out["nmse"].append(((p[s] - y[s]).square() * mf).sum(0) / yy.clamp_min(1e-30))
19
+ out["total_err"].append(((p[s] * mf).sum(0) - (y[s] * mf).sum(0)).abs() / (y[s] * mf).sum(0).abs().clamp_min(1e-30))
20
+ out["eligible"].append((n >= 2) & (vy > 1e-12))
21
+ return {k: torch.stack(v).cpu().numpy() for k, v in out.items()}
22
+
23
+
24
+ def test_matches_frozen_evaluator():
25
+ torch.manual_seed(0)
26
+ L, T, W, N = 4 * 256, 7, 256, 4
27
+ target = torch.randn(L, T)
28
+ pred = target * 0.9 + torch.randn(L, T) * 0.3
29
+ valid = torch.rand(L, T) > 0.2
30
+ valid[:W] = False # first sub-window has no observations
31
+ target[:, 3] = 1.0 # a constant track must be ineligible
32
+
33
+ want = frozen_subwindow_metrics(pred, target, valid, N, W)
34
+ got = subwindow_metrics(pred, target, valid, subwindow=W)
35
+
36
+ assert set(want) == set(got)
37
+ for k in want:
38
+ assert got[k].shape == (N, T), (k, got[k].shape)
39
+ assert np.array_equal(got[k], want[k]) if got[k].dtype == bool else np.allclose(got[k], want[k], rtol=0, atol=0), k
40
+ assert not got["eligible"][0].any()
41
+ assert not got["eligible"][:, 3].any()
42
+
43
+
44
+ def test_identity_prediction():
45
+ torch.manual_seed(1)
46
+ L, T, W = 2 * 128, 5, 128
47
+ target = torch.randn(L, T)
48
+ valid = torch.ones(L, T, dtype=torch.bool)
49
+ m = subwindow_metrics(target.clone(), target, valid, subwindow=W)
50
+ assert np.allclose(m["pearson"], 1.0)
51
+ assert np.allclose(m["proj"], 1.0)
52
+ assert np.allclose(m["nmse"], 0.0)
53
+ assert np.allclose(m["total_err"], 0.0)
54
+ assert m["eligible"].all()
55
+
56
+
57
+ def test_accepts_numpy_and_validates_shape():
58
+ t = np.random.RandomState(0).randn(2 * 64, 3).astype(np.float32)
59
+ v = np.ones_like(t, dtype=bool)
60
+ m = subwindow_metrics(t, t, v, subwindow=64)
61
+ assert m["pearson"].shape == (2, 3)
62
+ try:
63
+ subwindow_metrics(t, t[:100], v, subwindow=64)
64
+ except ValueError:
65
+ pass
66
+ else:
67
+ raise AssertionError("shape mismatch must raise")
@@ -0,0 +1,60 @@
1
+ """Unit tests for the v4 storage encoding (no dataset required)."""
2
+ import numpy as np
3
+
4
+ from zipoop.shard import HYBRID_FORMAT_V1, HYBRID_FORMAT_V2, _Reader
5
+
6
+
7
+ def shuffle(a):
8
+ """Mirror of the writer: float/complex arrays -> (itemsize, n) uint8 columns."""
9
+ v = np.asarray(a)
10
+ return np.ascontiguousarray(v).view(np.uint8).reshape(-1, v.dtype.itemsize).T.copy()
11
+
12
+
13
+ def spec_of(**arrays):
14
+ return {k: {"dtype": np.asarray(v).dtype.str, "shape": list(np.asarray(v).shape)} for k, v in arrays.items()}
15
+
16
+
17
+ def test_byte_shuffle_roundtrip():
18
+ rng = np.random.default_rng(0)
19
+ a = rng.standard_normal((7, 5)).astype(np.float32)
20
+ view = {"gain": shuffle(a)}
21
+ out = _Reader(view, spec_of(gain=a), HYBRID_FORMAT_V1)["gain"]
22
+ assert out.dtype == np.float32
23
+ assert np.array_equal(out, a)
24
+
25
+
26
+ def test_unshuffled_members_pass_through():
27
+ a = np.arange(12, dtype=np.uint16)
28
+ assert np.array_equal(_Reader({"fft_indices": a}, {}, HYBRID_FORMAT_V1)["fft_indices"], a)
29
+
30
+
31
+ def test_fft_v2_dequantization():
32
+ rng = np.random.default_rng(1)
33
+ tracks, bins = 4, 6
34
+ coefficients = (rng.standard_normal((tracks, bins)) + 1j * rng.standard_normal((tracks, bins))).astype(np.complex64)
35
+ pairs = np.stack((coefficients.real, coefficients.imag), axis=-1)
36
+ scale = np.abs(pairs).max(axis=(1, 2), keepdims=True).astype(np.float32)
37
+ packed = (pairs / scale).astype(np.float16)
38
+ scale = scale.reshape(tracks, 1, 1)
39
+ indices = np.sort(rng.choice(64, size=bins, replace=False)).astype(np.uint16)[None, :].repeat(tracks, 0)
40
+
41
+ view = {"fft_indices": indices, "fft_packed": shuffle(packed), "fft_scale": shuffle(scale)}
42
+ reader = _Reader(view, spec_of(fft_packed=packed, fft_scale=scale), HYBRID_FORMAT_V2)
43
+ got_i, got_c = reader.fft()
44
+ assert np.array_equal(got_i, indices)
45
+ assert got_c.dtype == np.complex64
46
+ assert got_c.shape == (tracks, bins)
47
+ reference = packed.astype(np.float32) * scale # the same float32 reference the writer used
48
+ reference = reference[..., 0] + 1j * reference[..., 1]
49
+ assert np.allclose(got_c, reference, rtol=0, atol=1e-6)
50
+ # and it stays within float16 rounding of the original complex64 payload
51
+ assert np.max(np.abs(got_c - coefficients)) < 0.02 * np.max(np.abs(coefficients))
52
+
53
+
54
+ def test_v3_reader_uses_complex_payload():
55
+ tracks, bins = 2, 3
56
+ coefficients = (np.ones((tracks, bins)) * (1 + 2j)).astype(np.complex64)
57
+ indices = np.zeros((tracks, bins), dtype=np.uint16)
58
+ got_i, got_c = _Reader({"fft_indices": indices, "fft_coefficients": coefficients}, {}, HYBRID_FORMAT_V1).fft()
59
+ assert np.array_equal(got_i, indices)
60
+ assert np.array_equal(got_c, coefficients)
@@ -1,11 +1,11 @@
1
1
  """Zipoop: decoder and reader for the per-window teacher shards (merged per-chromosome npz)."""
2
2
  from .dataset import Dataset, verify
3
3
  from .decoder import Decoder
4
- from .evaluation import calibrate, restore_real, summarize, window_pearson
4
+ from .evaluation import calibrate, restore_real, subwindow_metrics, summarize, window_pearson
5
5
  from .panel import panel_tracks, zero_tracks
6
6
  from .shard import Shard, read_shard
7
7
  from .teacher import TeacherWindow, load_window
8
8
 
9
- __version__ = "0.1.0"
9
+ __version__ = "0.2.1"
10
10
  __all__ = ["Dataset", "verify", "Decoder", "Shard", "read_shard", "TeacherWindow", "load_window",
11
- "panel_tracks", "zero_tracks", "calibrate", "restore_real", "summarize", "window_pearson"]
11
+ "panel_tracks", "zero_tracks", "calibrate", "restore_real", "subwindow_metrics", "summarize", "window_pearson"]
@@ -61,6 +61,59 @@ def window_pearson(pred, target, valid, window=WINDOW, chunk=256):
61
61
  return torch.cat(cols, 1)
62
62
 
63
63
 
64
+ def subwindow_metrics(pred, target, valid, subwindow=WINDOW):
65
+ """Per (sub-window, track) metrics, the convention of the frozen D5 evaluator.
66
+
67
+ ``pred`` and ``target`` are (L, T) signals, ``valid`` the (L, T) observed-position mask; the length
68
+ axis is split into ``L // subwindow`` contiguous blocks (the D5 pipeline uses 4 x 32,768 bp inside a
69
+ 131,072 bp segment). Returns a dict of numpy arrays with shape (sub-windows, tracks):
70
+
71
+ ``pearson`` masked 1 bp Pearson
72
+ ``proj`` projection ratio sum(pred * target) / sum(target ** 2)
73
+ ``nmse`` sum((pred - target) ** 2) / sum(target ** 2)
74
+ ``total_err`` |sum(pred) - sum(target)| / |sum(target)|
75
+ ``eligible`` at least 2 observed positions and a non-constant target inside that sub-window
76
+
77
+ The percentages and clamps (1e-24, 1e-30) are the frozen evaluator's, unchanged.
78
+ """
79
+ if torch.is_tensor(pred):
80
+ pred = pred.detach()
81
+ else:
82
+ pred = torch.as_tensor(np.asarray(pred))
83
+ if torch.is_tensor(target):
84
+ target = target.detach()
85
+ else:
86
+ target = torch.as_tensor(np.asarray(target))
87
+ if torch.is_tensor(valid):
88
+ valid = valid.detach()
89
+ else:
90
+ valid = torch.as_tensor(np.asarray(valid))
91
+ if pred.shape != target.shape or pred.shape != valid.shape:
92
+ raise ValueError(f"shape mismatch: pred {tuple(pred.shape)}, target {tuple(target.shape)}, valid {tuple(valid.shape)}")
93
+ length, tracks = target.shape
94
+ if not 0 < subwindow <= length or length % subwindow:
95
+ raise ValueError(f"length {length} is not a multiple of subwindow {subwindow}")
96
+
97
+ pred = pred.float()
98
+ target = target.float()
99
+ out = {k: [] for k in ("pearson", "proj", "nmse", "total_err", "eligible")}
100
+ for k in range(length // subwindow):
101
+ s = slice(k * subwindow, (k + 1) * subwindow)
102
+ mf = valid[s].float()
103
+ n = mf.sum(0).clamp_min(1)
104
+ yc = (target[s] - (target[s] * mf).sum(0) / n) * mf
105
+ pc = (pred[s] - (pred[s] * mf).sum(0) / n) * mf
106
+ vy = yc.square().sum(0)
107
+ yy = (target[s].square() * mf).sum(0)
108
+ out["pearson"].append((pc * yc).sum(0) / (pc.square().sum(0) * vy).clamp_min(1e-24).sqrt())
109
+ out["proj"].append((pred[s] * target[s] * mf).sum(0) / yy.clamp_min(1e-30))
110
+ out["nmse"].append(((pred[s] - target[s]).square() * mf).sum(0) / yy.clamp_min(1e-30))
111
+ out["total_err"].append(((pred[s] * mf).sum(0) - (target[s] * mf).sum(0)).abs()
112
+ / (target[s] * mf).sum(0).abs().clamp_min(1e-30))
113
+ out["eligible"].append((n >= 2) & (vy > 1e-12))
114
+ return {k: torch.stack(v).cpu().numpy() for k, v in out.items()}
115
+
116
+
64
117
  def summarize(p, tracks=None):
65
118
  """Mean/median over evaluable pairs, plus per-modality and per-window means."""
66
119
  tracks = panel_tracks() if tracks is None else tracks
@@ -0,0 +1,158 @@
1
+ """Parse and validate one teacher shard (a ``pooper-dna-v3`` or ``pooper-dna-v4`` hybrid archive) into a typed record."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import os
6
+ from contextlib import contextmanager
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+ import numpy as np
11
+
12
+ from .masks import MASK_FORMAT, decode_mask
13
+ from .network import ModelConfig
14
+
15
+ SHARD_FORMAT_V3 = "pooper-dna-v3"
16
+ SHARD_FORMAT_V4 = "pooper-dna-v4"
17
+ SHARD_FORMATS = (SHARD_FORMAT_V3, SHARD_FORMAT_V4)
18
+ HYBRID_FORMAT_V1 = "fft-plus-dna-residual-v1"
19
+ HYBRID_FORMAT_V2 = "fft-plus-dna-residual-v2"
20
+ HYBRID_FORMATS = (HYBRID_FORMAT_V1, HYBRID_FORMAT_V2)
21
+ STORAGE_ENCODING_V4 = "byte-shuffle-lzma-v1"
22
+
23
+
24
+ class ShardView:
25
+ """Read-only mapping over one window inside a merged per-chromosome npz (members "<window>/<key>")."""
26
+
27
+ def __init__(self, npz, key: str, members):
28
+ self._npz, self.key, self._members = npz, key, frozenset(members)
29
+
30
+ def __contains__(self, name):
31
+ return name in self._members
32
+
33
+ def __getitem__(self, name):
34
+ if name not in self._members:
35
+ raise KeyError(name)
36
+ return self._npz[f"{self.key}/{name}"]
37
+
38
+
39
+ @dataclass
40
+ class Shard:
41
+ key: str
42
+ shape: tuple[int, int] # (length, tracks)
43
+ metadata: dict
44
+ config: ModelConfig
45
+ dna: np.ndarray # (L,) uint8
46
+ valid: np.ndarray # (L, T) bool
47
+ parameters: dict[str, np.ndarray]
48
+ mean: np.ndarray # per-track target scaling and calibration, (T,)
49
+ power: np.ndarray
50
+ gain: np.ndarray
51
+ offset: np.ndarray
52
+ fft_indices: np.ndarray # (T, K) retained rfft bins
53
+ fft_coefficients: np.ndarray # (T, K) complex64
54
+ final_gain: np.ndarray
55
+ final_offset: np.ndarray
56
+
57
+
58
+ @contextmanager
59
+ def _opened(source):
60
+ if isinstance(source, (str, os.PathLike)):
61
+ with np.load(source, allow_pickle=False) as z:
62
+ yield z, Path(source).stem
63
+ else:
64
+ yield source, source.key
65
+
66
+
67
+ class _Reader:
68
+ """Member access that transparently undoes the v4 byte-shuffle and FFT quantization."""
69
+
70
+ def __init__(self, view, spec, hybrid_format):
71
+ self._view = view
72
+ self._spec = spec
73
+ self._hybrid = hybrid_format
74
+
75
+ def __contains__(self, name):
76
+ return name in self._view
77
+
78
+ def raw(self, name):
79
+ return self._view[name]
80
+
81
+ def __getitem__(self, name):
82
+ a = self._view[name]
83
+ meta = self._spec.get(name)
84
+ if meta is not None:
85
+ # v4 stores float/complex arrays as (itemsize, n) uint8 columns.
86
+ packed = np.ascontiguousarray(np.asarray(a))
87
+ if packed.dtype != np.uint8 or packed.ndim != 2:
88
+ raise ValueError(f"Invalid byte-shuffled member {name!r}")
89
+ b = np.ascontiguousarray(packed.T)
90
+ a = b.view(np.dtype(meta["dtype"])).reshape(tuple(meta["shape"]))
91
+ return a
92
+
93
+ def fft(self):
94
+ """(indices, complex64 coefficients) for either hybrid format."""
95
+ indices = np.asarray(self["fft_indices"])
96
+ if self._hybrid == HYBRID_FORMAT_V2:
97
+ packed = np.asarray(self["fft_packed"], dtype=np.float32)
98
+ scale = np.asarray(self["fft_scale"], dtype=np.float32)
99
+ if packed.ndim != 3 or packed.shape[-1] != 2 or packed.shape[:2] != indices.shape:
100
+ raise ValueError("Invalid packed FFT payload")
101
+ coefficients = (packed[..., 0] + 1j * packed[..., 1]) * scale.reshape(-1, 1)
102
+ return indices, np.ascontiguousarray(coefficients, dtype=np.complex64)
103
+ return indices, np.asarray(self["fft_coefficients"], dtype=np.complex64)
104
+
105
+
106
+ def read_shard(source) -> Shard:
107
+ """``source``: a shard path, or a ShardView from :class:`zipoop.Dataset`."""
108
+ with _opened(source) as (z, key):
109
+ if "format" not in z or str(z["format"]) not in SHARD_FORMATS:
110
+ raise ValueError(f"Unsupported shard format: {z['format'] if 'format' in z else None}")
111
+ if "hybrid_format" not in z or str(z["hybrid_format"]) not in HYBRID_FORMATS:
112
+ raise ValueError("Unsupported hybrid archive")
113
+ if "valid_encoding" not in z or str(z["valid_encoding"]) != MASK_FORMAT:
114
+ raise ValueError("Unsupported mask encoding")
115
+ spec = {}
116
+ if "byte_shuffle" in z:
117
+ spec = json.loads(z["byte_shuffle"].tobytes())
118
+ if not isinstance(spec, dict):
119
+ raise ValueError("Invalid byte-shuffle index")
120
+ if "storage_encoding" in z and str(z["storage_encoding"]) != STORAGE_ENCODING_V4:
121
+ raise ValueError(f"Unsupported storage encoding: {str(z['storage_encoding'])}")
122
+ view = _Reader(z, spec, str(z["hybrid_format"]))
123
+ shape = tuple(int(n) for n in z["shape"])
124
+ if len(shape) != 2:
125
+ raise ValueError("Invalid shape")
126
+ length, tracks = shape
127
+ dna = view["dna"].copy()
128
+ if dna.shape != (length,) or np.any(dna > 4):
129
+ raise ValueError("Invalid DNA data in archive")
130
+ names = json.loads(view["parameter_names"].tobytes())
131
+ if len(names) != len(set(names)):
132
+ raise ValueError("Duplicate parameter names")
133
+ fft_indices, fft_coefficients = view.fft()
134
+ shard = Shard(
135
+ key=key, shape=shape, metadata=json.loads(view["metadata"].tobytes()),
136
+ config=ModelConfig.from_json(json.loads(view["model_config"].tobytes())),
137
+ dna=dna, valid=decode_mask(view["valid_v2"].tobytes(), shape),
138
+ parameters={name: view[f"parameter_{i}"].copy() for i, name in enumerate(names)},
139
+ fft_indices=fft_indices.copy(), fft_coefficients=fft_coefficients.copy(),
140
+ **{name: view[name].copy() for name in ("mean", "power", "gain", "offset",
141
+ "final_gain", "final_offset")})
142
+ _validate(shard)
143
+ return shard
144
+
145
+
146
+ def _validate(s: Shard) -> None:
147
+ length, tracks = s.shape
148
+ if any(a.shape != (tracks,) for a in (s.mean, s.power, s.gain, s.offset)):
149
+ raise ValueError("Invalid track scales")
150
+ i, c = s.fft_indices, s.fft_coefficients
151
+ if (i.ndim != 2 or i.shape[0] != tracks or c.shape != i.shape or not np.issubdtype(i.dtype, np.integer)
152
+ or (i < 0).any() or (i > length // 2).any() or not np.isfinite(c).all()):
153
+ raise ValueError("Invalid FFT payload")
154
+ for a in (s.final_gain, s.final_offset):
155
+ if a.shape != (tracks,) or not np.isfinite(a).all():
156
+ raise ValueError("Invalid final-signal calibration")
157
+ if (s.final_gain < 0).any():
158
+ raise ValueError("Invalid final-signal calibration")
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: zipoop
3
+ Version: 0.2.1
4
+ Summary: Decoder and reader for Zipoop teacher shards (per-window neural compression of ENCODE signal tracks), v3 and v4 archives
5
+ Author: zatumout
6
+ License-Expression: MIT
7
+ Project-URL: Dataset, https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4
8
+ Project-URL: DatasetV3, https://huggingface.co/datasets/zatumout/zipoop_32k1885_010
9
+ Keywords: genomics,ENCODE,neural compression,distillation
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=1.26
16
+ Requires-Dist: torch>=2.2
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=8; extra == "test"
19
+ Dynamic: license-file
20
+
21
+ # zipoop
22
+
23
+ Decoder and reader for the Zipoop teacher shards: 89,438 windows of 32 kb, 1,885 ENCODE tracks (GRCh38), stored as one `<chrom>.npz` per chromosome plus `MANIFEST.tsv`.
24
+
25
+ Two equivalent releases are published:
26
+
27
+ - [zatumout/zipoop_32k1885_010_v4](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010_v4) — `pooper-dna-v4`, 89.3 GiB, **requires `zipoop>=0.2`**;
28
+ - [zatumout/zipoop_32k1885_010](https://huggingface.co/datasets/zatumout/zipoop_32k1885_010) — the older `pooper-dna-v3` encoding, 128.4 GiB, read by 0.1.x as well.
29
+
30
+ Both decode to the same signals; v4 only changes how the arrays are stored (byte-shuffled members, LZMA, and a float16 sparse FFT with one float32 scale per track).
31
+
32
+ ## Data layout
33
+
34
+ `<root>/<chrom>.npz` (25 files) plus `MANIFEST.tsv` (chrom, windows, members, bytes, sha256). Each file is a zip with members `<window>/<key>.npy` (161 arrays per window in v3, 164 in v4), windows in genome order. Window names are `<chrom>_<start>_<end>`.
35
+
36
+ ## Usage
37
+
38
+ ```python
39
+ from zipoop import Dataset
40
+ with Dataset("/path/to/weights_npz") as ds:
41
+ w = ds.load_window(ds.windows("chr21")[0]) # TeacherWindow: key, dna (L,), signal (L, T), valid (L, T)
42
+ ```
43
+
44
+ ```
45
+ zipoop verify /path/to/weights_npz # size + sha256 against MANIFEST.tsv
46
+ zipoop show /path/to/weights_npz chr21_10027008_10059776
47
+ ```
48
+
49
+ 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`.
50
+
51
+ `zipoop.evaluation.subwindow_metrics(pred, target, valid, subwindow=32768)` is the evaluator frozen in the D5 pipeline: it splits the length axis into contiguous sub-windows (4 x 32,768 bp inside a 131,072 bp segment) and returns `pearson` / `proj` / `nmse` / `total_err` / `eligible` arrays of shape (sub-windows, tracks), with the same clamps and the same eligibility rule (at least 2 observed positions and a non-constant target). The aggregation used by the frozen pipeline is the mean Pearson over eligible pairs and the median `proj` / `nmse`.
52
+
53
+ 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.
54
+
55
+ ## Supported formats
56
+
57
+ | archive | `format` | `hybrid_format` | storage |
58
+ |---|---|---|---|
59
+ | v3 | `pooper-dna-v3` | `fft-plus-dna-residual-v1` | deflate, `fft_coefficients` complex64 |
60
+ | v4 | `pooper-dna-v4` | `fft-plus-dna-residual-v2` | LZMA, byte-shuffled floats, `fft_packed` float16 + `fft_scale` float32 |
61
+
62
+ `read_shard` accepts both and rejects anything else with a clear error. The v4 decode path is verified against v3: parameters, masks and calibrations are bit-identical, the FFT payload differs only by float16 rounding (max relative error about 4.7e-4), and decoded windows reach a per-track Pearson of at least 0.9999997 against the v3 decode.
63
+
64
+ Tests: `ZIPOOP_DATA=<weights_npz> pytest tests` (the pooper-equivalence test also needs the original shards and the pooper package). The v4 storage encoding has dataset-free unit tests in `tests/test_v4_format.py`.
65
+
66
+ ## License and data terms
67
+
68
+ 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.
@@ -1,6 +1,8 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
+ tests/test_eval_subwindow.py
5
+ tests/test_v4_format.py
4
6
  tests/test_zipoop.py
5
7
  zipoop/__init__.py
6
8
  zipoop/cli.py
zipoop-0.1.0/PKG-INFO DELETED
@@ -1,51 +0,0 @@
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 DELETED
@@ -1,32 +0,0 @@
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.
@@ -1,105 +0,0 @@
1
- """Parse and validate one teacher shard (a ``pooper-dna-v3`` hybrid archive) into a typed record."""
2
- from __future__ import annotations
3
-
4
- import json
5
- import os
6
- from contextlib import contextmanager
7
- from dataclasses import dataclass
8
- from pathlib import Path
9
-
10
- import numpy as np
11
-
12
- from .masks import MASK_FORMAT, decode_mask
13
- from .network import ModelConfig
14
-
15
- SHARD_FORMAT = "pooper-dna-v3"
16
- HYBRID_FORMAT = "fft-plus-dna-residual-v1"
17
-
18
-
19
- class ShardView:
20
- """Read-only mapping over one window inside a merged per-chromosome npz (members "<window>/<key>")."""
21
-
22
- def __init__(self, npz, key: str, members):
23
- self._npz, self.key, self._members = npz, key, frozenset(members)
24
-
25
- def __contains__(self, name):
26
- return name in self._members
27
-
28
- def __getitem__(self, name):
29
- if name not in self._members:
30
- raise KeyError(name)
31
- return self._npz[f"{self.key}/{name}"]
32
-
33
-
34
- @dataclass
35
- class Shard:
36
- key: str
37
- shape: tuple[int, int] # (length, tracks)
38
- metadata: dict
39
- config: ModelConfig
40
- dna: np.ndarray # (L,) uint8
41
- valid: np.ndarray # (L, T) bool
42
- parameters: dict[str, np.ndarray]
43
- mean: np.ndarray # per-track target scaling and calibration, (T,)
44
- power: np.ndarray
45
- gain: np.ndarray
46
- offset: np.ndarray
47
- fft_indices: np.ndarray # (T, K) retained rfft bins
48
- fft_coefficients: np.ndarray # (T, K) complex64
49
- final_gain: np.ndarray
50
- final_offset: np.ndarray
51
-
52
-
53
- @contextmanager
54
- def _opened(source):
55
- if isinstance(source, (str, os.PathLike)):
56
- with np.load(source, allow_pickle=False) as z:
57
- yield z, Path(source).stem
58
- else:
59
- yield source, source.key
60
-
61
-
62
- def read_shard(source) -> Shard:
63
- """``source``: a shard path, or a ShardView from :class:`zipoop.Dataset`."""
64
- with _opened(source) as (z, key):
65
- if "format" not in z or str(z["format"]) != SHARD_FORMAT:
66
- raise ValueError(f"Unsupported shard format: {z['format'] if 'format' in z else None}")
67
- if "hybrid_format" not in z or str(z["hybrid_format"]) != HYBRID_FORMAT:
68
- raise ValueError("Unsupported hybrid archive")
69
- if "valid_encoding" not in z or str(z["valid_encoding"]) != MASK_FORMAT:
70
- raise ValueError("Unsupported mask encoding")
71
- shape = tuple(int(n) for n in z["shape"])
72
- if len(shape) != 2:
73
- raise ValueError("Invalid shape")
74
- length, tracks = shape
75
- dna = z["dna"].copy()
76
- if dna.shape != (length,) or np.any(dna > 4):
77
- raise ValueError("Invalid DNA data in archive")
78
- names = json.loads(z["parameter_names"].tobytes())
79
- if len(names) != len(set(names)):
80
- raise ValueError("Duplicate parameter names")
81
- shard = Shard(
82
- key=key, shape=shape, metadata=json.loads(z["metadata"].tobytes()),
83
- config=ModelConfig.from_json(json.loads(z["model_config"].tobytes())),
84
- dna=dna, valid=decode_mask(z["valid_v2"].tobytes(), shape),
85
- parameters={name: z[f"parameter_{i}"].copy() for i, name in enumerate(names)},
86
- **{name: z[name].copy() for name in ("mean", "power", "gain", "offset")},
87
- fft_indices=z["fft_indices"].copy(), fft_coefficients=z["fft_coefficients"].copy(),
88
- final_gain=z["final_gain"].copy(), final_offset=z["final_offset"].copy())
89
- _validate(shard)
90
- return shard
91
-
92
-
93
- def _validate(s: Shard) -> None:
94
- length, tracks = s.shape
95
- if any(a.shape != (tracks,) for a in (s.mean, s.power, s.gain, s.offset)):
96
- raise ValueError("Invalid track scales")
97
- i, c = s.fft_indices, s.fft_coefficients
98
- if (i.ndim != 2 or i.shape[0] != tracks or c.shape != i.shape or not np.issubdtype(i.dtype, np.integer)
99
- or (i < 0).any() or (i > length // 2).any() or not np.isfinite(c).all()):
100
- raise ValueError("Invalid FFT payload")
101
- for a in (s.final_gain, s.final_offset):
102
- if a.shape != (tracks,) or not np.isfinite(a).all():
103
- raise ValueError("Invalid final-signal calibration")
104
- if (s.final_gain < 0).any():
105
- raise ValueError("Invalid final-signal calibration")
@@ -1,51 +0,0 @@
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.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes