ptwm 1.0.0__cp313-cp313-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. ptwm/__init__.py +52 -0
  2. ptwm/_config.py +162 -0
  3. ptwm/_core.cpython-313-darwin.so +0 -0
  4. ptwm/_exceptions.py +26 -0
  5. ptwm/_loader.py +48 -0
  6. ptwm/_rust/__init__.py +313 -0
  7. ptwm/_rust/_errors.py +38 -0
  8. ptwm/_rust/ext.py +19 -0
  9. ptwm/_rust/host.py +7 -0
  10. ptwm/_rust/policy.py +7 -0
  11. ptwm/_rust/trust.py +37 -0
  12. ptwm/ablation/__init__.py +34 -0
  13. ptwm/ablation/_attribute.py +192 -0
  14. ptwm/ablation/_bench.py +171 -0
  15. ptwm/ablation/_compare.py +55 -0
  16. ptwm/ablation/_replay.py +85 -0
  17. ptwm/ablation/_resolve.py +71 -0
  18. ptwm/classify/__init__.py +24 -0
  19. ptwm/classify/_audit.py +143 -0
  20. ptwm/classify/_chain.py +32 -0
  21. ptwm/classify/_flags.py +50 -0
  22. ptwm/classify/_heuristic.py +63 -0
  23. ptwm/classify/_hf_quant.py +91 -0
  24. ptwm/classify/_protocol.py +24 -0
  25. ptwm/classify/_ptwm_config.py +53 -0
  26. ptwm/classify/_role.py +26 -0
  27. ptwm/cli/README.md +81 -0
  28. ptwm/cli/__init__.py +2 -0
  29. ptwm/cli/bench.py +156 -0
  30. ptwm/cli/chains.py +550 -0
  31. ptwm/cli/compress.py +1185 -0
  32. ptwm/cli/decompress.py +466 -0
  33. ptwm/cli/ext.py +298 -0
  34. ptwm/cli/main.py +39 -0
  35. ptwm/cli/policy.py +56 -0
  36. ptwm/cli/trust.py +188 -0
  37. ptwm/cli/utils.py +64 -0
  38. ptwm/codecs/__init__.py +205 -0
  39. ptwm/core/__init__.py +9 -0
  40. ptwm/core/_compressor.py +202 -0
  41. ptwm/core/_decompressor.py +158 -0
  42. ptwm/delta/__init__.py +88 -0
  43. ptwm/entropy/__init__.py +98 -0
  44. ptwm/ext_tooling/__init__.py +0 -0
  45. ptwm/ext_tooling/_templates/assemblyscript/manifest.toml +14 -0
  46. ptwm/ext_tooling/_templates/assemblyscript/package.json +11 -0
  47. ptwm/ext_tooling/_templates/assemblyscript/src/index.ts +28 -0
  48. ptwm/ext_tooling/_templates/c/Makefile +14 -0
  49. ptwm/ext_tooling/_templates/c/manifest.toml +14 -0
  50. ptwm/ext_tooling/_templates/c/src/lib.c +37 -0
  51. ptwm/ext_tooling/_templates/rust/Cargo.toml +11 -0
  52. ptwm/ext_tooling/_templates/rust/manifest.toml +14 -0
  53. ptwm/ext_tooling/_templates/rust/src/lib.rs +44 -0
  54. ptwm/ext_tooling/_templates/zig/build.zig +19 -0
  55. ptwm/ext_tooling/_templates/zig/manifest.toml +14 -0
  56. ptwm/ext_tooling/_templates/zig/src/main.zig +30 -0
  57. ptwm/ext_tooling/build.py +145 -0
  58. ptwm/ext_tooling/init.py +58 -0
  59. ptwm/ext_tooling/pack.py +57 -0
  60. ptwm/ext_tooling/sign.py +118 -0
  61. ptwm/integrations/__init__.py +14 -0
  62. ptwm/integrations/_compress_safetensors.py +401 -0
  63. ptwm/integrations/_hf.py +302 -0
  64. ptwm/integrations/_safetensors.py +163 -0
  65. ptwm/preprocessing/__init__.py +73 -0
  66. ptwm/preprocessing/_cache.py +188 -0
  67. ptwm/preprocessing/_chains.py +1077 -0
  68. ptwm/preprocessing/_explorer.py +410 -0
  69. ptwm/preprocessing/_plane.py +26 -0
  70. ptwm/random_access/__init__.py +132 -0
  71. ptwm/sharding/__init__.py +15 -0
  72. ptwm/sharding/_index.py +45 -0
  73. ptwm/sharding/_reader.py +64 -0
  74. ptwm/sharding/_sharder.py +42 -0
  75. ptwm/sharding/_writer.py +100 -0
  76. ptwm/stores/__init__.py +100 -0
  77. ptwm/stores/_common.py +204 -0
  78. ptwm/stores/_lmdb.py +415 -0
  79. ptwm/stores/_webdataset.py +330 -0
  80. ptwm/utils/__init__.py +34 -0
  81. ptwm/utils/_decode.py +93 -0
  82. ptwm/utils/_patch.py +37 -0
  83. ptwm/utils/_safetensors.py +53 -0
  84. ptwm/utils/_torch.py +240 -0
  85. ptwm-1.0.0.dist-info/METADATA +461 -0
  86. ptwm-1.0.0.dist-info/RECORD +90 -0
  87. ptwm-1.0.0.dist-info/WHEEL +4 -0
  88. ptwm-1.0.0.dist-info/entry_points.txt +2 -0
  89. ptwm-1.0.0.dist-info/licenses/LICENSE +21 -0
  90. ptwm-1.0.0.dist-info/sboms/ptwm-py.cyclonedx.json +13061 -0
ptwm/__init__.py ADDED
@@ -0,0 +1,52 @@
1
+ """PTWM — lossless compression for PyTorch model weights.
2
+
3
+ A typed preprocessing graph (bit reorder, byte/nibble split) feeds two
4
+ peer entropy coders — Huffman and rANS — alongside a Zstd plane codec
5
+ and a dedicated microscaling-format codec. The on-disk format is a
6
+ multi-tensor ``.ptwm`` container with per-tensor random-access lookup.
7
+
8
+ Public entry points: :class:`Compressor`, :class:`Decompressor`,
9
+ :class:`CompressionConfig`, :class:`DecompressionConfig`,
10
+ :class:`Method`, :class:`Format`. Third-party integrations
11
+ (HuggingFace `transformers`, `safetensors`) live in :mod:`ptwm.integrations`
12
+ and are imported from there explicitly, not re-exported here.
13
+ """
14
+
15
+ from importlib.metadata import PackageNotFoundError, version
16
+
17
+ from ._config import (
18
+ CompressionConfig,
19
+ DecompressionConfig,
20
+ Format,
21
+ Lossy,
22
+ Method,
23
+ )
24
+ from ._exceptions import (
25
+ CompressionMethodNotSupportedError,
26
+ Error,
27
+ HeaderParseError,
28
+ InvalidDTypeError,
29
+ )
30
+ from .core import Compressor, Decompressor
31
+ from .integrations import materialize_hf_cache
32
+
33
+ try:
34
+ __version__ = version("ptwm")
35
+ except PackageNotFoundError:
36
+ __version__ = "0.0.0+unknown"
37
+
38
+ __all__ = [
39
+ "CompressionConfig",
40
+ "CompressionMethodNotSupportedError",
41
+ "Compressor",
42
+ "DecompressionConfig",
43
+ "Decompressor",
44
+ "Format",
45
+ "Lossy",
46
+ "Method",
47
+ "Error",
48
+ "HeaderParseError",
49
+ "InvalidDTypeError",
50
+ "__version__",
51
+ "materialize_hf_cache",
52
+ ]
ptwm/_config.py ADDED
@@ -0,0 +1,162 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, replace
4
+ from enum import Enum
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from ptwm._rust.policy import ResolvedPolicy
9
+
10
+ from .codecs import CodecId
11
+
12
+
13
+ class Method(Enum):
14
+ AUTO = 0
15
+ HUFFMAN = 1
16
+ ZSTD = 2
17
+ IDENTITY = 3
18
+ RANS = 4
19
+ MICROSCALE = 5
20
+
21
+ @classmethod
22
+ def _missing_(cls, value):
23
+ if isinstance(value, str):
24
+ value = value.upper()
25
+ if value in cls.__members__:
26
+ return cls.__members__[value]
27
+ return None
28
+
29
+
30
+ class Format(Enum):
31
+ BYTE = 1
32
+ TORCH = 2
33
+ NUMPY = 3
34
+ FILE = 4
35
+
36
+ @classmethod
37
+ def _missing_(cls, value):
38
+ if isinstance(value, str):
39
+ value = value.upper()
40
+ if value in cls.__members__:
41
+ return cls.__members__[value]
42
+ return None
43
+
44
+
45
+ class Lossy(Enum):
46
+ NONE = 0
47
+ INTEGER = 1
48
+ UNSIGN = 2
49
+
50
+ @classmethod
51
+ def _missing_(cls, value):
52
+ if isinstance(value, str):
53
+ value = value.upper()
54
+ if value in cls.__members__:
55
+ return cls.__members__[value]
56
+ return None
57
+
58
+
59
+ @dataclass(frozen=True, slots=True, kw_only=True)
60
+ class CompressionConfig:
61
+ method: Method = Method.AUTO
62
+ input_format: Format = Format.BYTE
63
+ bytearray_dtype: str = "bfloat16"
64
+ is_monotonic: int = 0
65
+ threads: int | None = None
66
+ compression_threshold: float = 0.95
67
+ check_th_after_percent: int = 10
68
+ reorder_signbit: int = 0
69
+ delta_compressed_type: str | None = None
70
+ lossy_compressed_type: Lossy = Lossy.NONE
71
+ lossy_compressed_factor: int = 27
72
+ compression_chunk: int = 256 * 1024
73
+ is_streaming: bool = False
74
+ streaming_chunk: int = 1024 * 1024
75
+ input_file: str | None = None
76
+ compressed_file: str | None = None
77
+ decompressed_file: str | None = None
78
+ zstd_level: int = 3
79
+ lz4_compression_level: int = 0
80
+ codec_menu: list[CodecId] | None = None
81
+ """Restrict per-role trial-encode menus to this set of codecs.
82
+
83
+ None → use the full role-applicable menu. An empty intersection on a
84
+ role raises ``InvalidContainer`` — an Identity fallback would silently
85
+ falsify ablation measurements. Ignored when ``method`` selects a forced-
86
+ codec path (ZSTD / RANS / IDENTITY).
87
+ """
88
+
89
+ @classmethod
90
+ def from_resolved_policy(
91
+ cls,
92
+ rp: ResolvedPolicy,
93
+ *,
94
+ base: CompressionConfig | None = None,
95
+ ) -> CompressionConfig:
96
+ """Build a constrained ``CompressionConfig`` from a ``ResolvedPolicy``.
97
+
98
+ The returned config's ``codec_menu`` is the intersection of the
99
+ resolved policy's ``effective_global`` set with the wire-stable
100
+ built-in ``CodecId`` variants.
101
+
102
+ The resolved policy lists every contribution (built-in or installed
103
+ third-party) that is currently trusted *and* allowed by the policy
104
+ file. Restricting the trial-encode menu to that intersection is how
105
+ an ablation run actually exercises a variant policy: tensors that
106
+ previously round-tripped through a now-excluded codec will fall
107
+ through to the next-best allowed coder instead.
108
+
109
+ ``base`` is the starting config (defaults to a default-constructed
110
+ ``CompressionConfig``); every field other than ``codec_menu`` is
111
+ preserved.
112
+ """
113
+ # Imported locally — `ptwm.codecs` reaches back through
114
+ # `ptwm.preprocessing → ptwm.utils → ptwm._config` at top level,
115
+ # so a module-level import here is a real circular-import bug
116
+ # (not just a ruff style preference).
117
+ from ptwm._rust import builtin_canonical_id # noqa: PLC0415
118
+
119
+ from .codecs import CodecId # noqa: PLC0415
120
+
121
+ # Map every wire-stable built-in codec to the short name used in
122
+ # `crates/ptwm-core/src/extension/builtins.rs`.
123
+ _BUILTIN_CODEC_NAMES: dict[CodecId, str] = {
124
+ CodecId.Identity: "identity",
125
+ CodecId.Huffman: "huffman",
126
+ CodecId.Rans: "rans",
127
+ CodecId.Zstd: "zstd",
128
+ CodecId.PerGroupCodebook: "per_group_codebook",
129
+ CodecId.Order1ScaleAC: "order1_scale_ac",
130
+ }
131
+
132
+ allowed_hex = set(rp.effective_global_ids())
133
+
134
+ # Empty `effective_global` means "no policy applied" (the default
135
+ # `PolicyFile.default_empty()` case): preserve the base's
136
+ # codec_menu rather than collapsing to an empty list, which the
137
+ # Rust dispatcher would treat as "no codecs allowed → error".
138
+ if not allowed_hex:
139
+ return base if base is not None else cls()
140
+
141
+ menu: list[CodecId] = []
142
+ for codec_id, short_name in _BUILTIN_CODEC_NAMES.items():
143
+ if builtin_canonical_id(short_name).hex() in allowed_hex:
144
+ menu.append(codec_id)
145
+
146
+ starting = base if base is not None else cls()
147
+ return replace(starting, codec_menu=menu)
148
+
149
+
150
+ @dataclass(frozen=True, slots=True, kw_only=True)
151
+ class DecompressionConfig:
152
+ # Most properties are inferred from the header; this struct only carries
153
+ # runtime specifics like thread count and the delta reference.
154
+ threads: int | None = None
155
+ delta_second_data: bytes | None = None
156
+ skip_missing: bool = False
157
+ """If ``True``, open containers that reference non-builtin extensions
158
+ that are not installed on this host. Tensors needing those extensions
159
+ will fail when decoded, but the container can still be opened and
160
+ interrogated (e.g. listing tensor names). When ``False`` (the default),
161
+ opening such a container raises ``ValueError``.
162
+ """
Binary file
ptwm/_exceptions.py ADDED
@@ -0,0 +1,26 @@
1
+ import logging
2
+
3
+ # Standard logger for the package
4
+ logger = logging.getLogger("ptwm")
5
+ # Default handler setup could be left to the user, but we can provide a NullHandler
6
+ logger.addHandler(logging.NullHandler())
7
+
8
+
9
+ class Error(Exception):
10
+ """Base exception for all errors originating from the PTWM library."""
11
+
12
+
13
+ class CompressionMethodNotSupportedError(Error):
14
+ """Raised when an unsupported compression method is requested."""
15
+
16
+
17
+ class InvalidDTypeError(Error):
18
+ """Raised when a tensor or numpy array has an unsupported dtype."""
19
+
20
+
21
+ class HeaderParseError(Error):
22
+ """Raised when the binary header is invalid or corrupt."""
23
+
24
+
25
+ class UnsupportedQuantConfigError(ValueError):
26
+ """Raised when ``hf_quant_config.json`` carries an unsupported algo."""
ptwm/_loader.py ADDED
@@ -0,0 +1,48 @@
1
+ """Discover Python-resident extensions via entry points.
2
+
3
+ Entry points under the `ptwm.extensions` group are loaded at PTWM
4
+ startup. Each one is a callable that, when invoked, calls
5
+ ContributionRegistry.register(...) for each contribution it provides.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from collections.abc import Iterable
12
+ from importlib.metadata import EntryPoint, entry_points
13
+
14
+
15
+ def discover_host_extensions() -> Iterable[tuple[str, EntryPoint]]:
16
+ """Yield (entry_point_name, ep) for each ptwm.extensions entry point."""
17
+ eps = entry_points(group="ptwm.extensions")
18
+ for ep in eps:
19
+ yield ep.name, ep
20
+
21
+
22
+ def load_all_host_extensions() -> int:
23
+ """Invoke every discovered entry point.
24
+
25
+ Returns the number of entry points successfully loaded. Errors from
26
+ a single entry point are logged (via print) but don't stop the
27
+ loader — a broken extension shouldn't take down the whole runtime.
28
+ """
29
+ count = 0
30
+ for name, ep in discover_host_extensions():
31
+ try:
32
+ register_fn = ep.load()
33
+ except Exception as exc: # noqa: BLE001
34
+ print( # noqa: T201
35
+ f"warning: ptwm.extensions[{name}] failed to load: {exc}",
36
+ file=sys.stderr,
37
+ )
38
+ continue
39
+ try:
40
+ register_fn()
41
+ except Exception as exc: # noqa: BLE001
42
+ print( # noqa: T201
43
+ f"warning: ptwm.extensions[{name}] failed to register: {exc}",
44
+ file=sys.stderr,
45
+ )
46
+ continue
47
+ count += 1
48
+ return count
ptwm/_rust/__init__.py ADDED
@@ -0,0 +1,313 @@
1
+ """Thin wrapper around the ``weights._core`` PyO3 extension.
2
+
3
+ The ``weights._rust`` package is the ONLY place in the codebase that imports
4
+ ``weights._core``. Every other module goes through the wrappers defined here
5
+ (and its submodules), so the underlying binding can swap (e.g. for a
6
+ subprocess backend) without touching the rest of the codebase.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from ptwm import _core as _ext
12
+
13
+ from ._errors import translate_errors
14
+
15
+ __all__ = [
16
+ "blake3_hash",
17
+ "builtin_canonical_id",
18
+ "codec_id_registry",
19
+ "compress_model",
20
+ "decode_model",
21
+ "decode_tensor",
22
+ "decode_tensor_delta_hash",
23
+ "decode_tensor_info",
24
+ "decode_tensor_shape",
25
+ "delta_float_sub_decode",
26
+ "delta_float_sub_encode",
27
+ "delta_xor_decode",
28
+ "delta_xor_encode",
29
+ "dtype_element_size",
30
+ "explode_ptwm",
31
+ "huffman_decode",
32
+ "huffman_encode",
33
+ "identity_decode",
34
+ "identity_encode",
35
+ "implode_ptwm",
36
+ "list_plane_summaries",
37
+ "list_tensor_names",
38
+ "rans_decode",
39
+ "rans_encode",
40
+ "shannon",
41
+ "translate_errors",
42
+ "xxhash64",
43
+ "zstd_decode",
44
+ "zstd_encode",
45
+ ]
46
+
47
+
48
+ @translate_errors
49
+ def xxhash64(data: bytes | memoryview) -> int:
50
+ """Return the 64-bit xxhash of ``data`` (seed 0)."""
51
+ return _ext.xxhash64(data)
52
+
53
+
54
+ @translate_errors
55
+ def shannon(data: bytes | memoryview) -> float:
56
+ """Return the Shannon entropy of ``data`` in bits/byte."""
57
+ return _ext.shannon(data)
58
+
59
+
60
+ # --- Per-plane codec encode / decode ---------------------------------------
61
+
62
+
63
+ @translate_errors
64
+ def identity_encode(data: bytes | memoryview) -> bytes:
65
+ return bytes(_ext.identity_encode(data))
66
+
67
+
68
+ @translate_errors
69
+ def identity_decode(blob: bytes | memoryview, expected_len: int) -> bytes:
70
+ return bytes(_ext.identity_decode(blob, expected_len))
71
+
72
+
73
+ @translate_errors
74
+ def huffman_encode(data: bytes | memoryview) -> bytes:
75
+ return bytes(_ext.huffman_encode(data))
76
+
77
+
78
+ @translate_errors
79
+ def huffman_decode(blob: bytes | memoryview, expected_len: int) -> bytes:
80
+ return bytes(_ext.huffman_decode(blob, expected_len))
81
+
82
+
83
+ @translate_errors
84
+ def rans_encode(data: bytes | memoryview) -> bytes:
85
+ return bytes(_ext.rans_encode(data))
86
+
87
+
88
+ @translate_errors
89
+ def rans_decode(blob: bytes | memoryview, expected_len: int) -> bytes:
90
+ return bytes(_ext.rans_decode(blob, expected_len))
91
+
92
+
93
+ @translate_errors
94
+ def zstd_encode(data: bytes | memoryview, level: int = 3) -> bytes:
95
+ return bytes(_ext.zstd_encode(data, level))
96
+
97
+
98
+ @translate_errors
99
+ def zstd_decode(blob: bytes | memoryview) -> bytes:
100
+ return bytes(_ext.zstd_decode(blob))
101
+
102
+
103
+ # --- Delta primitives ------------------------------------------------------
104
+
105
+
106
+ @translate_errors
107
+ def delta_xor_encode(raw: bytes | memoryview, reference: bytes | memoryview) -> bytes:
108
+ return bytes(_ext.delta_xor_encode(raw, reference))
109
+
110
+
111
+ @translate_errors
112
+ def delta_xor_decode(
113
+ residual: bytes | memoryview, reference: bytes | memoryview
114
+ ) -> bytes:
115
+ return bytes(_ext.delta_xor_decode(residual, reference))
116
+
117
+
118
+ @translate_errors
119
+ def delta_float_sub_encode(
120
+ raw: bytes | memoryview, reference: bytes | memoryview
121
+ ) -> bytes:
122
+ return bytes(_ext.delta_float_sub_encode(raw, reference))
123
+
124
+
125
+ @translate_errors
126
+ def delta_float_sub_decode(
127
+ residual: bytes | memoryview, reference: bytes | memoryview
128
+ ) -> bytes:
129
+ return bytes(_ext.delta_float_sub_decode(residual, reference))
130
+
131
+
132
+ @translate_errors
133
+ def blake3_hash(data: bytes | memoryview) -> bytes:
134
+ """BLAKE3 digest (32 bytes) of ``data``."""
135
+ return bytes(_ext.blake3_hash(data))
136
+
137
+
138
+ def codec_id_registry() -> list[tuple[str, int]]:
139
+ """Authoritative ``(name, wire_id)`` list from the Rust ``CodecId``.
140
+
141
+ The regression test that locks the Python ``CodecId`` IntEnum to the
142
+ Rust truth consumes this list.
143
+ """
144
+ return [(str(n), int(v)) for n, v in _ext.codec_id_registry()]
145
+
146
+
147
+ def builtin_canonical_id(name: str) -> bytes:
148
+ r"""Return the 32-byte canonical id for the named built-in contribution.
149
+
150
+ ``name`` is the short name used in `builtins.rs` (e.g. ``"huffman"``,
151
+ ``"rans"``, ``"order1_scale_ac"``). The derivation matches
152
+ ``CanonicalId::derive``:
153
+ ``blake3(BUILTIN_PUBKEY || 0x00 || name || 0x00 || CARGO_PKG_VERSION)``
154
+ — the zero separators are part of the derivation so authors
155
+ reproducing IDs in other languages get the same bytes.
156
+ """
157
+ return bytes(_ext.builtin_canonical_id(name))
158
+
159
+
160
+ # --- Dtype lookups (Rust-authoritative) -----------------------------------
161
+
162
+
163
+ @translate_errors
164
+ def dtype_element_size(dtype_code: int) -> int:
165
+ """Bytes per scalar element for ``dtype_code``."""
166
+ return _ext.dtype_element_size(dtype_code)
167
+
168
+
169
+ # --- Container API (PPG chain-based) ------------------------------------
170
+
171
+
172
+ @translate_errors
173
+ def compress_model(
174
+ records: list[
175
+ tuple[
176
+ str,
177
+ int,
178
+ int,
179
+ bytes,
180
+ list[int] | None,
181
+ str | None,
182
+ bytes | None,
183
+ ]
184
+ ],
185
+ chains_per_tensor: list[list[bytes]],
186
+ emit_payload_hash: bool,
187
+ emit_plane_crc: bool,
188
+ method_hint: int = 3,
189
+ streaming_chunk: int | None = None,
190
+ codec_menu: list[int] | None = None,
191
+ ) -> bytes:
192
+ """Compress a list of tensors into a ``.ptwm`` container.
193
+
194
+ Each element of ``records`` is a 7-tuple:
195
+ ``(name, dtype_code, input_format, raw_bytes, shape,
196
+ dtype_name, delta_reference_blake3)``.
197
+ ``chains_per_tensor`` — one list of chain wire-byte blobs per tensor;
198
+ each blob is produced by ``Chain.to_bytes()`` from ``ptwm.preprocessing``.
199
+ Pass a single-element list when no multi-chain trial-encode is needed.
200
+ ``method_hint``: 1=HUFFMAN, 2=ZSTD, 3=MICROSCALE, 4=RANS, 5=IDENTITY.
201
+ ``streaming_chunk``: when set, split each plane into fixed-size chunks.
202
+ ``codec_menu``: optional list of wire-stable ``CodecId`` integers
203
+ restricting the trial-encode menu. ``None`` uses the full menu;
204
+ empty intersection on a plane raises ``InvalidContainer``.
205
+ """
206
+ return bytes(
207
+ _ext.compress_model(
208
+ records,
209
+ chains_per_tensor,
210
+ emit_payload_hash,
211
+ emit_plane_crc,
212
+ method_hint,
213
+ streaming_chunk,
214
+ codec_menu,
215
+ )
216
+ )
217
+
218
+
219
+ @translate_errors
220
+ def decode_tensor(blob: bytes, name: str, skip_missing: bool = False) -> bytes:
221
+ """Decode a single tensor from a ``.ptwm`` container blob.
222
+
223
+ Returns raw decompressed bytes.
224
+ """
225
+ return bytes(_ext.decode_tensor(blob, name, skip_missing))
226
+
227
+
228
+ @translate_errors
229
+ def decode_model(blob: bytes, skip_missing: bool = False) -> list[tuple[str, bytes]]:
230
+ """Decode every tensor from a ``.ptwm`` container blob in parallel.
231
+
232
+ Returns ``(name, raw_bytes)`` per tensor in container index order. Prefer
233
+ this over per-tensor :func:`decode_tensor` for a full-model load — the
234
+ decodes run across the rayon pool with the GIL released.
235
+ """
236
+ return _ext.decode_model(blob, skip_missing)
237
+
238
+
239
+ @translate_errors
240
+ def decode_tensor_shape(blob: bytes, name: str) -> tuple[list[int], str] | None:
241
+ """Return ``(shape, dtype_name)`` for a tensor, or ``None``."""
242
+ return _ext.decode_tensor_shape(blob, name)
243
+
244
+
245
+ @translate_errors
246
+ def decode_tensor_delta_hash(
247
+ blob: bytes, name: str, skip_missing: bool = False
248
+ ) -> bytes | None:
249
+ """Return the 32-byte BLAKE3 hash of the delta reference for ``name``.
250
+
251
+ Returns ``None`` if the tensor has no delta dependency.
252
+ """
253
+ got = _ext.decode_tensor_delta_hash(blob, name, skip_missing)
254
+ return bytes(got) if got is not None else None
255
+
256
+
257
+ @translate_errors
258
+ def list_tensor_names(blob: bytes, skip_missing: bool = False) -> list[str]:
259
+ """Return every tensor name stored in a ``.ptwm`` container."""
260
+ return list(_ext.list_tensor_names(blob, skip_missing))
261
+
262
+
263
+ @translate_errors
264
+ def list_plane_summaries(
265
+ blob: bytes,
266
+ ) -> list[tuple[str, int, int, int, int, int]]:
267
+ """Return per-plane summaries for every tensor in a container.
268
+
269
+ Each entry: ``(tensor_name, plane_index, role, codec_id,
270
+ state_source, payload_len)``.
271
+ """
272
+ return list(_ext.list_plane_summaries(blob))
273
+
274
+
275
+ @translate_errors
276
+ def decode_tensor_info(
277
+ blob: bytes, name: str, skip_missing: bool = False
278
+ ) -> tuple[list[int], str, int, int] | None:
279
+ """Return ``(shape, dtype_name, dtype_code, input_format)`` or ``None``."""
280
+ return _ext.decode_tensor_info(blob, name, skip_missing)
281
+
282
+
283
+ # --- Container ⇄ canonical PTWM-JSON manifest transcoder -------------------
284
+
285
+
286
+ @translate_errors
287
+ def explode_ptwm(
288
+ blob: bytes,
289
+ ) -> tuple[str, list[tuple[str, str, str]], dict[str, bytes]]:
290
+ """Explode a ``.ptwm`` container into its canonical PTWM-JSON projection.
291
+
292
+ Returns ``(registry_json, tensors, members)`` where ``registry_json`` is
293
+ the canonical (JCS) archive-scoped registry manifest, ``tensors`` is a list
294
+ of ``(name, key, tensor_manifest_json)`` in file order, and ``members`` maps
295
+ member name → bytes. ``implode_ptwm`` reproduces ``blob`` byte-for-byte.
296
+
297
+ The extension already returns ``str``/``bytes`` of the correct type, so the
298
+ result is returned without re-wrapping (avoids copying large payloads).
299
+ """
300
+ return _ext.explode_ptwm(blob)
301
+
302
+
303
+ @translate_errors
304
+ def implode_ptwm(
305
+ registry_json: str,
306
+ tensor_jsons: list[str],
307
+ members: dict[str, bytes],
308
+ ) -> bytes:
309
+ """Reconstruct a ``.ptwm`` container from a manifest projection.
310
+
311
+ ``tensor_jsons`` must be in file order. Inverse of :func:`explode_ptwm`.
312
+ """
313
+ return _ext.implode_ptwm(registry_json, tensor_jsons, members)
ptwm/_rust/_errors.py ADDED
@@ -0,0 +1,38 @@
1
+ """Error translation at the PyO3 boundary.
2
+
3
+ The `_core` extension raises stdlib `ValueError`/`RuntimeError`/`MemoryError`/
4
+ `BufferError` subclasses (mapped from `WeightsCoreError` variants on the Rust
5
+ side). The `translate_errors` decorator normalizes those into
6
+ `Error` subclasses so callers can `except Error` without caring
7
+ which Python exception type PyO3 used.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import functools
13
+ from collections.abc import Callable
14
+
15
+ from .._exceptions import Error, HeaderParseError
16
+
17
+
18
+ def translate_errors[**P, R](fn: Callable[P, R]) -> Callable[P, R]:
19
+ """Wrap a callable so `_core` exceptions surface as :class:`Error`.
20
+
21
+ Header-parse errors raised by `_core.parse_header` are specifically mapped
22
+ to :class:`HeaderParseError`; everything else to a plain
23
+ :class:`Error`.
24
+ """
25
+
26
+ @functools.wraps(fn)
27
+ def wrapped(*args: P.args, **kwargs: P.kwargs) -> R:
28
+ try:
29
+ return fn(*args, **kwargs)
30
+ except Error:
31
+ raise
32
+ except (BufferError, MemoryError, ValueError, RuntimeError) as e:
33
+ message = str(e)
34
+ if fn.__name__ == "parse_header":
35
+ raise HeaderParseError(message) from e
36
+ raise Error(message) from e
37
+
38
+ return wrapped
ptwm/_rust/ext.py ADDED
@@ -0,0 +1,19 @@
1
+ """Thin shim exposing the install/discovery surface as ptwm._rust.ext."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ptwm._core import ( # type: ignore[import]
6
+ InstalledContribution,
7
+ ext_install,
8
+ ext_list,
9
+ ext_remove,
10
+ ext_user_dir,
11
+ )
12
+
13
+ __all__ = [
14
+ "InstalledContribution",
15
+ "ext_install",
16
+ "ext_list",
17
+ "ext_remove",
18
+ "ext_user_dir",
19
+ ]
ptwm/_rust/host.py ADDED
@@ -0,0 +1,7 @@
1
+ """Thin shim exposing the host-flavor registry as ptwm._rust.host."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ptwm._core import ContributionRegistry, host_registry_entries
6
+
7
+ __all__ = ["ContributionRegistry", "host_registry_entries"]
ptwm/_rust/policy.py ADDED
@@ -0,0 +1,7 @@
1
+ """Thin shim re-exporting policy types from the Rust extension."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ptwm._core import PolicyFile, ResolvedPolicy # type: ignore[import]
6
+
7
+ __all__ = ["PolicyFile", "ResolvedPolicy"]