nervecode 0.1.0__py3-none-any.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.
@@ -0,0 +1,161 @@
1
+ """Seeding helpers to make runs reproducible across local and CI.
2
+
3
+ These utilities intentionally keep a small surface and no hard dependency on
4
+ NumPy. If NumPy is available, its global RNG is also seeded and restored.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import contextlib
10
+ import importlib
11
+ import os
12
+ import random
13
+ import types
14
+ import typing
15
+
16
+
17
+ def _maybe_import_numpy() -> typing.Any | None:
18
+ """Return NumPy module if available, else None."""
19
+ try:
20
+ return importlib.import_module("numpy")
21
+ except Exception: # pragma: no cover - optional dependency
22
+ return None
23
+
24
+
25
+ def _maybe_import_torch() -> typing.Any | None:
26
+ """Return a torch-like module if available with required attributes, else None."""
27
+ try:
28
+ t = importlib.import_module("torch")
29
+ except Exception: # pragma: no cover - torch missing
30
+ return None
31
+ required = all(hasattr(t, name) for name in ("manual_seed", "get_rng_state", "set_rng_state"))
32
+ return t if required else None
33
+
34
+
35
+ def seed_from_env(
36
+ env_var: str = "NERVECODE_SEED",
37
+ default: int = 1234,
38
+ *,
39
+ set_env_if_missing: bool = False,
40
+ ) -> int:
41
+ """Return an integer seed sourced from an environment variable.
42
+
43
+ - If ``env_var`` is set and parseable as an int, return its value.
44
+ - Otherwise, return ``default``. If ``set_env_if_missing`` is True, also set
45
+ ``os.environ[env_var]`` to the default value for downstream tools.
46
+ """
47
+ raw = os.environ.get(env_var)
48
+ if raw is not None:
49
+ try:
50
+ return int(raw)
51
+ except ValueError: # Fall back to default on malformed input
52
+ pass
53
+ if set_env_if_missing:
54
+ os.environ[env_var] = str(default)
55
+ return default
56
+
57
+
58
+ def seed_everything(seed: int, *, deterministic: bool = True) -> None:
59
+ """Seed Python, optional NumPy, and PyTorch RNGs.
60
+
61
+ Also toggles PyTorch deterministic settings when ``deterministic`` is True to
62
+ minimize nondeterministic kernels. On CUDA setups, call this as early as
63
+ possible in process startup for best effect.
64
+ """
65
+ # Hash-based ops reproducibility (effective at interpreter start;
66
+ # set anyway for child processes invoked by test runners)
67
+ os.environ["PYTHONHASHSEED"] = str(seed)
68
+
69
+ random.seed(seed)
70
+
71
+ np = _maybe_import_numpy()
72
+ if np is not None: # pragma: no branch - trivial branch
73
+ np.random.seed(seed)
74
+
75
+ t = _maybe_import_torch()
76
+ if t is not None:
77
+ t.manual_seed(seed)
78
+ if hasattr(t, "cuda") and t.cuda.is_available(): # pragma: no cover
79
+ t.cuda.manual_seed_all(seed)
80
+
81
+ # Configure PyTorch deterministic behavior as far as feasible.
82
+ t = _maybe_import_torch()
83
+ if t is not None and hasattr(t, "use_deterministic_algorithms"):
84
+ # warn_only=True avoids raising on ops without deterministic kernels
85
+ with contextlib.suppress(Exception): # pragma: no cover - older/newer API differences
86
+ t.use_deterministic_algorithms(bool(deterministic), warn_only=True)
87
+
88
+ # cuDNN knobs (safe on CPU-only builds; no-ops)
89
+ # cuDNN knobs (safe on CPU-only builds; no-ops)
90
+ try:
91
+ cudnn = importlib.import_module("torch.backends.cudnn")
92
+ except Exception: # pragma: no cover - backend not available
93
+ cudnn = None
94
+ if cudnn is not None: # pragma: no branch
95
+ cudnn_mod: typing.Any = cudnn
96
+ try:
97
+ cudnn_mod.deterministic = bool(deterministic)
98
+ cudnn_mod.benchmark = not bool(deterministic)
99
+ except Exception: # pragma: no cover - attributes may be missing
100
+ pass
101
+
102
+
103
+ class temp_seed(contextlib.AbstractContextManager[None]):
104
+ """Temporarily set RNG seeds and restore previous states on exit.
105
+
106
+ This context manager saves Python's ``random`` state, optional NumPy's global
107
+ RNG state, and PyTorch CPU/CUDA RNG states. It then calls ``seed_everything``
108
+ with the provided seed and restores the saved states when leaving the context.
109
+ """
110
+
111
+ def __init__(self, seed: int, *, deterministic: bool = True) -> None:
112
+ self._seed = int(seed)
113
+ self._deterministic = bool(deterministic)
114
+
115
+ self._py_state: tuple[typing.Any, ...] | None = None
116
+ self._np_state: typing.Any | None = None
117
+ self._torch_cpu_state: typing.Any | None = None
118
+ self._torch_cuda_states: typing.Any | None = None
119
+
120
+ def __enter__(self) -> None:
121
+ # Save current states
122
+ self._py_state = random.getstate()
123
+ np = _maybe_import_numpy()
124
+ if np is not None: # pragma: no branch
125
+ self._np_state = np.random.get_state()
126
+ t = _maybe_import_torch()
127
+ if t is not None:
128
+ self._torch_cpu_state = t.get_rng_state()
129
+ if hasattr(t, "cuda") and t.cuda.is_available(): # pragma: no cover
130
+ self._torch_cuda_states = t.cuda.get_rng_state_all()
131
+ # Apply temporary seed
132
+ seed_everything(self._seed, deterministic=self._deterministic)
133
+ return None
134
+
135
+ def __exit__(
136
+ self,
137
+ exc_type: type[BaseException] | None,
138
+ exc: BaseException | None,
139
+ tb: types.TracebackType | None,
140
+ ) -> None:
141
+ # Restore saved states
142
+ if self._py_state is not None:
143
+ random.setstate(self._py_state)
144
+ np = _maybe_import_numpy()
145
+ if np is not None and self._np_state is not None: # pragma: no branch
146
+ np.random.set_state(self._np_state)
147
+ t = _maybe_import_torch()
148
+ if t is not None and self._torch_cpu_state is not None:
149
+ t.set_rng_state(self._torch_cpu_state)
150
+ if (
151
+ hasattr(t, "cuda") and self._torch_cuda_states is not None and t.cuda.is_available()
152
+ ): # pragma: no cover
153
+ t.cuda.set_rng_state_all(self._torch_cuda_states)
154
+ return None
155
+
156
+
157
+ __all__ = [
158
+ "seed_everything",
159
+ "seed_from_env",
160
+ "temp_seed",
161
+ ]
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: nervecode
3
+ Version: 0.1.0
4
+ Summary: Intrinsic surprise scoring for PyTorch via statistical coding.
5
+ Project-URL: Homepage, https://gitlab.com/domezsolt/nervecode
6
+ Project-URL: Repository, https://gitlab.com/domezsolt/nervecode
7
+ Author: Zsolt Döme
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 Nervecode Maintainers
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+
30
+ License-File: LICENSE
31
+ Keywords: coding,ml,pytorch,research,surprise
32
+ Requires-Python: >=3.10
33
+ Requires-Dist: torch>=2.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.8; extra == 'dev'
36
+ Requires-Dist: pre-commit>=3.6; extra == 'dev'
37
+ Requires-Dist: pytest>=7; extra == 'dev'
38
+ Requires-Dist: ruff>=0.3; extra == 'dev'
39
+ Provides-Extra: docs
40
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
41
+ Requires-Dist: mkdocs>=1.5; extra == 'docs'
42
+ Provides-Extra: logging
43
+ Requires-Dist: loguru>=0.7; extra == 'logging'
44
+ Requires-Dist: rich>=13; extra == 'logging'
45
+ Provides-Extra: viz
46
+ Requires-Dist: matplotlib>=3.7; extra == 'viz'
47
+ Requires-Dist: seaborn>=0.13; extra == 'viz'
48
+ Description-Content-Type: text/markdown
49
+
50
+ # Nervecode Docs
51
+
52
+ This directory will contain documentation for the Nervecode project.
53
+
54
+ ## Developer Setup
55
+
56
+ - Install dev tools (in your virtualenv): `pip install -e .[dev]`
57
+ - Install git hooks: `pre-commit install`
58
+ - Run on all files once: `pre-commit run --all-files`
59
+
60
+ Included hooks:
61
+ - Formatting: `ruff-format`
62
+ - Linting and import sorting: `ruff` (with `--fix`)
63
+ - Type-checking: `mypy`
64
+
65
+ ## Tests
66
+
67
+ - Unit tests: `pytest tests/unit` (fast correctness checks)
68
+ - Integration tests: `pytest tests/integration` (heavier, cross-module)
69
+ - Smoke tests: `pytest tests/smoke` (repo wiring and API surface)
70
+
71
+ ### Deterministic runs
72
+
73
+ - Tests auto-seed RNGs per test using `NERVECODE_SEED` (default: `1234`).
74
+ - You can control reproducibility in your own scripts via:
75
+ ```python
76
+ from nervecode.utils.seed import seed_everything, seed_from_env
77
+ seed_everything(seed_from_env())
78
+ ```
79
+
80
+ ## Performance Notes
81
+
82
+ - Overhead estimates and guidance for pooled Conv2d coding: see `docs/overhead.md`.
83
+ - Scaling and tradeoffs for layer selection and coding dimension: see `docs/scaling.md`.
@@ -0,0 +1,31 @@
1
+ nervecode/__init__.py,sha256=xPUsGwV4JD71a5lClC6TqiOzEVWkTdwh8RRtaz53kNE,17734
2
+ nervecode/_version.py,sha256=MtrcXrZW_diNtg4lJtIVD5xrp0F8fhI_Q-Y-TLVm3ws,280
3
+ nervecode/core/__init__.py,sha256=MG6Q-13HOQ8HeN6CsisffNqUOsnCDywiUFHKzgJHFVQ,497
4
+ nervecode/core/assignment.py,sha256=wS8_I9Exb63dg1VAm6OP8QgUhDxRT2hWr_aHhp81z54,7080
5
+ nervecode/core/codebook.py,sha256=M6KaE71w5lQE3QdgCzwnRa6_trLebrDppnYQFP1ih6Y,7327
6
+ nervecode/core/shapes.py,sha256=wBjGh_Gk4Xt6nITsYJS0wf-v-WR7YFSkzI-lhUk2OL0,3612
7
+ nervecode/core/temperature.py,sha256=kBL99jxRFjZe_oiCL-NzveTVCxHoDBmX8JfvtHjePIY,7930
8
+ nervecode/core/trace.py,sha256=EyTlthcaCDDKamtLhcC35sESHt8LauUKZ2exquAmwkE,6936
9
+ nervecode/core/types.py,sha256=YJylBQiqICQCjU4Y_DfmVbvIl8yCXtLa_Dcedmne7cM,4805
10
+ nervecode/integration/__init__.py,sha256=eDP_VIlczERiMmIxaggtzvsDsbamI5WZWIlG-R0VJJM,243
11
+ nervecode/layers/__init__.py,sha256=DRYdTi2uSlp9m1wq8-Zbphm6c0PgwDaIsZWsCHdRPeM,374
12
+ nervecode/layers/base.py,sha256=9QOqCpa926EdotVhRp6apLZyrBApGDtlik_d4TVf4tg,12161
13
+ nervecode/layers/conv.py,sha256=ROKyq1bMbjwmltRwHN5oMh3ljnO_CsIs4rPU9c-QteI,7095
14
+ nervecode/layers/linear.py,sha256=YXX-0iNQ0r05ExD4JPkR3R2FJZsqPHJtfDB5SFwKLsQ,7383
15
+ nervecode/layers/reducers.py,sha256=7PN2ZO2hsJr5aXQye3d03nvM0fXY40uYd5TCMYwg53A,2879
16
+ nervecode/layers/wrap.py,sha256=UEhkFLw8U54ZLl0BjrfsZiMH1_7nlQQLAcWhhbEIHb0,7928
17
+ nervecode/scoring/__init__.py,sha256=ULUQbzb51OpakpLC_o8Ra6DC6CflmuxM-t3GmI0IWuU,583
18
+ nervecode/scoring/aggregator.py,sha256=cSv8vfpgRWfWExJIQ6bPqHbjMu_uCJCnNnW2Gd8Cb8Y,13297
19
+ nervecode/scoring/calibrator.py,sha256=-Wm6te9XzpTYX0uLV_qBgEHSfcM403Ilw_4gmaWiRlg,16326
20
+ nervecode/scoring/types.py,sha256=EmK0NO0muvjb5Ah272-g_y6Iuzc7WkCoZMolbSrvbxM,1087
21
+ nervecode/training/__init__.py,sha256=_YW_FlGgsTHNFaKm7frK9XemV7a-XWoeBcRSrePzadI,580
22
+ nervecode/training/diagnostics.py,sha256=0Peh4pOTnPSvJ4_UNXHKVPwf_dunOpq_vg7EjcCijRc,6606
23
+ nervecode/training/loss.py,sha256=rxreNiBu9LlxlHHdXhcH207o68VSGZRuBqQ6-xwTFwU,7278
24
+ nervecode/training/updaters.py,sha256=pSzbvjAacN8wTWXUboBRKrlgjoX4rX-3KquMAB7D1s4,7072
25
+ nervecode/utils/__init__.py,sha256=KUAQvrOQuAaTlPRmy3UuebKSO7FJZd5dF65s0fsfKRw,309
26
+ nervecode/utils/overhead.py,sha256=3hr3JwNKkDphPMOAs1Vkrq7CTRxR6X_qwfUx5nUDlxI,6606
27
+ nervecode/utils/seed.py,sha256=VMj2Vtr8OYEz0Gc0PJWdxow4He6Wv8tEoq9MgGb0mZk,5867
28
+ nervecode-0.1.0.dist-info/METADATA,sha256=oM2aV6Vsau0cjAq6XRzxilZ7c943q7zAocikHVbiPaQ,3294
29
+ nervecode-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
30
+ nervecode-0.1.0.dist-info/licenses/LICENSE,sha256=WP83ux1S7U3IyieW0mPA_Q1mpbJHK5ipX3_bniUAgYM,1079
31
+ nervecode-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nervecode Maintainers
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.
22
+