dendra 0.27.2__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.
Files changed (173) hide show
  1. dendra/__init__.py +257 -0
  2. dendra/__main__.py +5 -0
  3. dendra/_bootstrap.py +217 -0
  4. dendra/cli.py +51 -0
  5. dendra/const.py +34 -0
  6. dendra/data/__init__.py +5 -0
  7. dendra/data/h5.py +145 -0
  8. dendra/diagnostics.py +367 -0
  9. dendra/func/__init__.py +53 -0
  10. dendra/func/_binding.py +116 -0
  11. dendra/func/_callbacks.py +1326 -0
  12. dendra/func/_compiled.py +394 -0
  13. dendra/func/_execution.py +94 -0
  14. dendra/func/_lowered.py +163 -0
  15. dendra/func/_population.py +11505 -0
  16. dendra/func/_runners.py +1257 -0
  17. dendra/func/_scan.py +185 -0
  18. dendra/func/_scan_autograd.py +57 -0
  19. dendra/func/_scan_compat.py +233 -0
  20. dendra/func/_stimuli.py +1574 -0
  21. dendra/func/_types.py +227 -0
  22. dendra/helpers.py +849 -0
  23. dendra/models/__init__.py +31 -0
  24. dendra/models/_class_declarations.py +173 -0
  25. dendra/models/_morphology_scene.py +536 -0
  26. dendra/models/analysis/__init__.py +139 -0
  27. dendra/models/analysis/activation_witness.py +211 -0
  28. dendra/models/analysis/batched_threshold_protocol.py +373 -0
  29. dendra/models/analysis/branch_conditioned_metrics.py +738 -0
  30. dendra/models/analysis/callback_horizon.py +134 -0
  31. dendra/models/analysis/callback_margin.py +262 -0
  32. dendra/models/analysis/descriptors.py +6532 -0
  33. dendra/models/analysis/event_margin_training.py +283 -0
  34. dendra/models/analysis/firing_rate_trajectory.py +179 -0
  35. dendra/models/analysis/hard_threshold_gradient.py +252 -0
  36. dendra/models/analysis/hard_transition_table.py +169 -0
  37. dendra/models/analysis/hard_transition_validation.py +337 -0
  38. dendra/models/analysis/spike_timing.py +300 -0
  39. dendra/models/analysis/threshold_event_root.py +348 -0
  40. dendra/models/analysis/threshold_noise_metrics.py +182 -0
  41. dendra/models/analysis/threshold_protocol.py +182 -0
  42. dendra/models/analysis/threshold_trace_probe.py +429 -0
  43. dendra/models/analysis/threshold_trace_tangent.py +549 -0
  44. dendra/models/analysis/threshold_transition_jets.py +103 -0
  45. dendra/models/analysis/threshold_transition_metrics.py +162 -0
  46. dendra/models/backend.py +78 -0
  47. dendra/models/callbacks.py +1947 -0
  48. dendra/models/core.py +10320 -0
  49. dendra/models/distributions.py +218 -0
  50. dendra/models/extcell.py +311 -0
  51. dendra/models/fields/__init__.py +64 -0
  52. dendra/models/fields/analytic.py +390 -0
  53. dendra/models/fields/line.py +199 -0
  54. dendra/models/fields/precomputed.py +1143 -0
  55. dendra/models/fields/quasipotentials.py +158 -0
  56. dendra/models/fields/spherical_cartesian.py +41 -0
  57. dendra/models/graph.py +163 -0
  58. dendra/models/heterogeneous/__init__.py +5 -0
  59. dendra/models/heterogeneous/compartments.py +141 -0
  60. dendra/models/initialization.py +247 -0
  61. dendra/models/instruments/__init__.py +3 -0
  62. dendra/models/instruments/thresholder.py +1154 -0
  63. dendra/models/integrators/__init__.py +86 -0
  64. dendra/models/integrators/cable.py +108 -0
  65. dendra/models/integrators/core.py +905 -0
  66. dendra/models/integrators/explicit.py +811 -0
  67. dendra/models/integrators/imex.py +369 -0
  68. dendra/models/integrators/implicit.py +1038 -0
  69. dendra/models/integrators/tree.py +1841 -0
  70. dendra/models/integrators/tree_bt.py +600 -0
  71. dendra/models/integrators/tridiag/__init__.py +3 -0
  72. dendra/models/integrators/tridiag/block.py +219 -0
  73. dendra/models/integrators/tridiag/pcr.py +203 -0
  74. dendra/models/integrators/triton/__init__.py +28 -0
  75. dendra/models/integrators/triton/_contracts.py +351 -0
  76. dendra/models/integrators/triton/bt_kernel.py +298 -0
  77. dendra/models/integrators/triton/bt_spd_kernel.py +733 -0
  78. dendra/models/integrators/triton/dhs_kernel.py +654 -0
  79. dendra/models/integrators/triton/dhs_kernel_bt.py +479 -0
  80. dendra/models/integrators/triton/dhs_kernel_multi.py +889 -0
  81. dendra/models/integrators/triton/pcr_kernel_thomas.py +226 -0
  82. dendra/models/integrators/triton/t_kernel_thomas.py +232 -0
  83. dendra/models/intrinsic.py +139 -0
  84. dendra/models/io.py +1238 -0
  85. dendra/models/mechanisms/__init__.py +46 -0
  86. dendra/models/mechanisms/_bufferimplicit.py +420 -0
  87. dendra/models/mechanisms/_cnexp.py +143 -0
  88. dendra/models/mechanisms/_derivimplicit.py +831 -0
  89. dendra/models/mechanisms/_euler_heun.py +199 -0
  90. dendra/models/mechanisms/_euler_maruyama.py +152 -0
  91. dendra/models/mechanisms/_handler.py +2946 -0
  92. dendra/models/mechanisms/_ions.py +316 -0
  93. dendra/models/mechanisms/_kinetic.py +365 -0
  94. dendra/models/mechanisms/_linearimplicit.py +595 -0
  95. dendra/models/mechanisms/_material_process.py +2545 -0
  96. dendra/models/mechanisms/_materials.py +407 -0
  97. dendra/models/mechanisms/_mechanism.py +4340 -0
  98. dendra/models/mechanisms/_numerical.py +431 -0
  99. dendra/models/mechanisms/_rosenbrock.py +456 -0
  100. dendra/models/mechanisms/_solve_utils.py +284 -0
  101. dendra/models/mechanisms/_solvers.py +129 -0
  102. dendra/models/mechanisms/_spatial.py +1799 -0
  103. dendra/models/mechanisms/_state.py +2410 -0
  104. dendra/models/mechanisms/_support.py +760 -0
  105. dendra/models/mechanisms/_support_registry.py +466 -0
  106. dendra/models/mechanisms/_symbolic.py +246 -0
  107. dendra/models/mechanisms/compilers/__init__.py +0 -0
  108. dendra/models/mechanisms/compilers/ast.py +363 -0
  109. dendra/models/mechanisms/compilers/source.py +121 -0
  110. dendra/models/mechanisms/ode.py +515 -0
  111. dendra/models/mechanisms/ops.py +133 -0
  112. dendra/models/mechanisms/validate.py +6 -0
  113. dendra/models/mod/ALPHASYNAPSE.py +63 -0
  114. dendra/models/mod/ALPHASYNAPSE_D.py +77 -0
  115. dendra/models/mod/APCOUNT.py +42 -0
  116. dendra/models/mod/APCOUNT_D.py +132 -0
  117. dendra/models/mod/EXP2SYN.py +138 -0
  118. dendra/models/mod/EXPSYN.py +60 -0
  119. dendra/models/mod/FIRE.py +23 -0
  120. dendra/models/mod/FIRE_D.py +84 -0
  121. dendra/models/mod/FIRE_R.py +51 -0
  122. dendra/models/mod/FIRE_R_D.py +124 -0
  123. dendra/models/mod/GRADED_SYNAPSE.py +414 -0
  124. dendra/models/mod/HH.py +96 -0
  125. dendra/models/mod/PAS.py +25 -0
  126. dendra/models/mod/SPIKEDETECT.py +160 -0
  127. dendra/models/mod/__init__.py +117 -0
  128. dendra/models/modular.py +232 -0
  129. dendra/models/morphology.py +3539 -0
  130. dendra/models/morphology_io.py +606 -0
  131. dendra/models/morphology_visualization.py +3244 -0
  132. dendra/models/multi.py +1049 -0
  133. dendra/models/networks/__init__.py +3 -0
  134. dendra/models/networks/net.py +5125 -0
  135. dendra/models/networks/netcon.py +6063 -0
  136. dendra/models/networks/netcon_bitpack_contracts.py +160 -0
  137. dendra/models/networks/netcon_bitpack_kernel.cpp +149 -0
  138. dendra/models/networks/netcon_bitpack_kernel.cu +228 -0
  139. dendra/models/networks/netcon_bitpack_ops.py +712 -0
  140. dendra/models/networks/netcon_bitpack_ops_triton.py +285 -0
  141. dendra/models/networks/netstim.py +1603 -0
  142. dendra/models/networks/spiking.py +96 -0
  143. dendra/models/networks/utils.py +36 -0
  144. dendra/models/parametric.py +4182 -0
  145. dendra/models/random_parameters.py +423 -0
  146. dendra/models/rng.py +429 -0
  147. dendra/models/slice.py +2445 -0
  148. dendra/models/stim/__init__.py +4 -0
  149. dendra/models/stim/extra.py +200 -0
  150. dendra/models/stim/intra.py +278 -0
  151. dendra/models/stim/waveform/__init__.py +25 -0
  152. dendra/models/stim/waveform/_gates.py +65 -0
  153. dendra/models/stim/waveform/core.py +708 -0
  154. dendra/models/stim/waveform/implementations.py +860 -0
  155. dendra/models/tree.py +1269 -0
  156. dendra/models/utils.py +134 -0
  157. dendra/models/visualization.py +1791 -0
  158. dendra/opt/__init__.py +5 -0
  159. dendra/opt/utils.py +103 -0
  160. dendra/stepping.py +158 -0
  161. dendra/units.py +151 -0
  162. dendra/utils/__init__.py +21 -0
  163. dendra/utils/dynamic_compilation.py +82 -0
  164. dendra/utils/gpu.py +49 -0
  165. dendra/utils/interpolation.py +4418 -0
  166. dendra/utils/neuron.py +151 -0
  167. dendra/utils/tensor_ops.py +287 -0
  168. dendra-0.27.2.dist-info/METADATA +161 -0
  169. dendra-0.27.2.dist-info/RECORD +173 -0
  170. dendra-0.27.2.dist-info/WHEEL +5 -0
  171. dendra-0.27.2.dist-info/entry_points.txt +2 -0
  172. dendra-0.27.2.dist-info/licenses/LICENSE.md +39 -0
  173. dendra-0.27.2.dist-info/top_level.txt +1 -0
dendra/__init__.py ADDED
@@ -0,0 +1,257 @@
1
+ """Dendra: A PyTorch-based framework for simulating and modeling neural dynamics."""
2
+
3
+ import os
4
+
5
+ __version__ = "0.27.2"
6
+
7
+ os.environ.setdefault("OMP_DYNAMIC", "FALSE")
8
+ os.environ.setdefault("MKL_DYNAMIC", "FALSE")
9
+ os.environ["OMP_PROC_BIND"] = "true"
10
+ os.environ["OMP_PLACES"] = "cores"
11
+
12
+ from ._bootstrap import (
13
+ _should_cache_cpu_isa_for_dendra,
14
+ _should_configure_torchinductor_cache_on_import,
15
+ configure_torchinductor_cache_for_dendra,
16
+ )
17
+
18
+ _torchinductor_cache_opt_in = _should_configure_torchinductor_cache_on_import()
19
+ if _torchinductor_cache_opt_in:
20
+ configure_torchinductor_cache_for_dendra()
21
+
22
+ import pickle
23
+ import time
24
+ import warnings
25
+ from pathlib import Path
26
+
27
+ import torch
28
+
29
+ import dendra.func as func
30
+ import dendra.models.callbacks as callbacks
31
+ import dendra.models.mod as mod
32
+
33
+ from .const import FARADAY, PI, TAU, E, R
34
+ from .diagnostics import doctor
35
+ from .helpers import (
36
+ COMPILE_OPTIONS,
37
+ DEVICE,
38
+ DTYPE,
39
+ NATIVE_EXTENSION_POLICY,
40
+ PRESERVE_MECHANISM_POPULATION_AXIS,
41
+ RUNTIME_CONTRACT_VALIDATION,
42
+ TF32,
43
+ allow_tf32,
44
+ ctx,
45
+ current_compile_options,
46
+ current_device,
47
+ current_dtype,
48
+ current_native_extension_policy,
49
+ current_preserve_mechanism_population_axis,
50
+ current_runtime_contract_validation,
51
+ normalize_native_extension_policy,
52
+ normalize_preserve_mechanism_population_axis,
53
+ normalize_runtime_contract_validation,
54
+ set_compile_options,
55
+ set_jit_enabled,
56
+ set_jit_in_network_enabled,
57
+ set_jit_network_ops_enabled,
58
+ set_jit_network_solves_enabled,
59
+ )
60
+ from .models import (
61
+ Axon,
62
+ Cable,
63
+ CompartmentGeometry,
64
+ CompartmentGraph,
65
+ CompartmentMetadata,
66
+ CompartmentTopology,
67
+ DistributionSpec,
68
+ ExtCellAxon,
69
+ ExtCellTree,
70
+ Morphology,
71
+ Myelinated,
72
+ NetStim,
73
+ Network,
74
+ Population,
75
+ RandomParameterSpec,
76
+ RuntimeNoiseSpec,
77
+ Section,
78
+ SectionLocation,
79
+ SingleCompartment,
80
+ SynapseSlots,
81
+ Tree,
82
+ Unmyelinated,
83
+ available_random_distributions,
84
+ concat_models,
85
+ concat_slices,
86
+ connect_morphologies,
87
+ get_random_distribution,
88
+ insert_intrinsic_activity,
89
+ make_random_parameter_spec,
90
+ make_runtime_noise_spec,
91
+ passive_end_nodes_,
92
+ register_random_distribution,
93
+ remove_intrinsic_activity,
94
+ sample_random_parameter,
95
+ sample_runtime_noise,
96
+ )
97
+ from .models.fields import *
98
+ from .models.integrators import (
99
+ bwd_euler_bt,
100
+ bwd_euler_sc,
101
+ bwd_euler_ub,
102
+ df,
103
+ dfh,
104
+ dhs,
105
+ dufort_frankel,
106
+ dufort_frankel_homogeneous,
107
+ euler,
108
+ eulerv1,
109
+ rk1,
110
+ rk2,
111
+ rk4,
112
+ scnv,
113
+ )
114
+ from .models.mod import load_mechanisms
115
+ from .models.stim.waveform import *
116
+ from .stepping import step, step_network, step_population
117
+
118
+ __all__ = [
119
+ "PI",
120
+ "E",
121
+ "TAU",
122
+ "R",
123
+ "FARADAY",
124
+ "ctx",
125
+ "doctor",
126
+ "step",
127
+ "step_population",
128
+ "step_network",
129
+ "DEVICE",
130
+ "DTYPE",
131
+ "NATIVE_EXTENSION_POLICY",
132
+ "PRESERVE_MECHANISM_POPULATION_AXIS",
133
+ "RUNTIME_CONTRACT_VALIDATION",
134
+ "COMPILE_OPTIONS",
135
+ "current_compile_options",
136
+ "current_device",
137
+ "current_dtype",
138
+ "current_native_extension_policy",
139
+ "current_preserve_mechanism_population_axis",
140
+ "current_runtime_contract_validation",
141
+ "normalize_runtime_contract_validation",
142
+ "normalize_native_extension_policy",
143
+ "normalize_preserve_mechanism_population_axis",
144
+ "set_compile_options",
145
+ "set_jit_enabled",
146
+ "set_jit_network_solves_enabled",
147
+ "set_jit_network_ops_enabled",
148
+ "set_jit_in_network_enabled",
149
+ "callbacks",
150
+ "func",
151
+ "mod",
152
+ "load_mechanisms",
153
+ "DistributionSpec",
154
+ "RandomParameterSpec",
155
+ "RuntimeNoiseSpec",
156
+ "available_random_distributions",
157
+ "get_random_distribution",
158
+ "register_random_distribution",
159
+ "make_random_parameter_spec",
160
+ "make_runtime_noise_spec",
161
+ "sample_random_parameter",
162
+ "sample_runtime_noise",
163
+ "insert_intrinsic_activity",
164
+ "remove_intrinsic_activity",
165
+ "passive_end_nodes_",
166
+ "Population",
167
+ "Morphology",
168
+ "Section",
169
+ "SectionLocation",
170
+ "CompartmentTopology",
171
+ "CompartmentGeometry",
172
+ "CompartmentMetadata",
173
+ "CompartmentGraph",
174
+ "SingleCompartment",
175
+ "Cable",
176
+ "Axon",
177
+ "Unmyelinated",
178
+ "Myelinated",
179
+ "Tree",
180
+ "ExtCellAxon",
181
+ "ExtCellTree",
182
+ "NetStim",
183
+ "Network",
184
+ "SynapseSlots",
185
+ "concat_models",
186
+ "concat_slices",
187
+ "connect_morphologies",
188
+ "anisotropic_point",
189
+ "isotropic_point",
190
+ "parametric_efield",
191
+ "PreComputedInterpolate1D",
192
+ "precomputed_interpolate_1d",
193
+ "EfieldInterpolate3DRect",
194
+ "efield_interpolate_3d_rect",
195
+ "PreComputedInterpolate3DRect",
196
+ "precomputed_interpolate_3d_rect",
197
+ "PreComputedInterpolate3DScattered",
198
+ "precomputed_interpolate_3d_scattered",
199
+ "EfieldInterpolate3DScattered",
200
+ "efield_interpolate_3d_scattered",
201
+ "euler",
202
+ "eulerv1",
203
+ "rk1",
204
+ "rk2",
205
+ "rk4",
206
+ "dufort_frankel",
207
+ "dufort_frankel_homogeneous",
208
+ "bwd_euler_sc",
209
+ "bwd_euler_ub",
210
+ "bwd_euler_bt",
211
+ "dhs",
212
+ "df",
213
+ "dfh",
214
+ "scnv",
215
+ ]
216
+
217
+
218
+ def _cache_cpu_isa_list():
219
+ """
220
+ Checks for a cached CPU ISA string. If not found, runs the slow
221
+ detection and caches the result for future runs.
222
+ """
223
+ # Use a standard cache location.
224
+ cache_dir = Path.home() / ".cache" / "dendra"
225
+ cache_file = cache_dir / f"cpu_isa_list_{torch.__version__}"
226
+
227
+ def get_valid_vec_isa_list():
228
+ with open(cache_file, "rb") as f:
229
+ return pickle.load(f)
230
+
231
+ # --- Cache Hit ---
232
+ if cache_file.exists():
233
+ # Check if the PyTorch version matches the one used to create the cache.
234
+ torch._inductor.cpu_vec_isa.valid_vec_isa_list = get_valid_vec_isa_list
235
+ return
236
+
237
+ # --- Cache Miss or Stale Cache ---
238
+ print("Dendra: Performing one-time CPU capability check. This may take a minute...")
239
+ try:
240
+ os.makedirs(cache_dir, exist_ok=True)
241
+ start = time.time()
242
+ valid_vec_isa_list = torch._inductor.cpu_vec_isa.valid_vec_isa_list()
243
+ print(f"Dendra: CPU capability check took {(time.time() - start):.3f}s.")
244
+ with open(cache_file, "wb") as f:
245
+ pickle.dump(valid_vec_isa_list, f)
246
+ except Exception as e:
247
+ warnings.warn(
248
+ f"Dendra: CPU capability check failed: {e}. Falling back to default behavior."
249
+ )
250
+ return
251
+
252
+
253
+ if _torchinductor_cache_opt_in and _should_cache_cpu_isa_for_dendra():
254
+ _cache_cpu_isa_list()
255
+
256
+ # setup environment
257
+ allow_tf32(bool(TF32))
dendra/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Run Dendra's command-line interface with ``python -m dendra``."""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())
dendra/_bootstrap.py ADDED
@@ -0,0 +1,217 @@
1
+ # dendra/_bootstrap.py
2
+
3
+ from __future__ import annotations
4
+
5
+ import atexit
6
+ import getpass
7
+ import os
8
+ import shutil
9
+ import sys
10
+ import tempfile
11
+ import uuid
12
+ import warnings
13
+ from contextlib import contextmanager
14
+ from pathlib import Path
15
+
16
+ _DISABLED_INDUCTOR_CACHE_POLICIES = {
17
+ "0",
18
+ "false",
19
+ "none",
20
+ "off",
21
+ "disable",
22
+ "disabled",
23
+ }
24
+ _PROCESS_INDUCTOR_CACHE_POLICIES = {
25
+ "process",
26
+ "isolated",
27
+ "per_process",
28
+ "per-process",
29
+ }
30
+ _SHARED_INDUCTOR_CACHE_POLICIES = {"shared", "global"}
31
+
32
+
33
+ @contextmanager
34
+ def torch_compiler_warning_context():
35
+ """Filter only known PyTorch compiler deprecations in strict-warning runs."""
36
+ with warnings.catch_warnings():
37
+ warnings.filterwarnings(
38
+ "ignore",
39
+ message=r".*torch\.jit\.script_method.*deprecated.*",
40
+ category=DeprecationWarning,
41
+ module=r"torch\.jit\._script",
42
+ )
43
+ warnings.filterwarnings(
44
+ "ignore",
45
+ message=r".*torch\.jit\.script.*deprecated.*",
46
+ category=DeprecationWarning,
47
+ module=r"torch\.jit\._script",
48
+ )
49
+ warnings.filterwarnings(
50
+ "ignore",
51
+ message=(
52
+ r".*The \.grad attribute of a Tensor that is not a leaf Tensor "
53
+ r"is being accessed.*"
54
+ ),
55
+ category=UserWarning,
56
+ )
57
+ warnings.filterwarnings(
58
+ "ignore",
59
+ message=(
60
+ r".*torch\.autograd\.function\.Function.*should not be "
61
+ r"instantiated\..*Methods on autograd functions.*"
62
+ ),
63
+ category=DeprecationWarning,
64
+ )
65
+ yield
66
+
67
+
68
+ def reset_torch_compiler(*, prefer_public: bool = True) -> None:
69
+ """Reset Torch compiler state without leaking PyTorch-internal warnings.
70
+
71
+ PyTorch 2.13 can emit deprecations for ``torch.jit.script_method`` while
72
+ lazily initializing Inductor and for internal ``autograd.Function``
73
+ handling while tracing custom functions. Dendra still treats every other
74
+ warning as actionable; only those upstream deprecations are filtered around
75
+ compiler operations that trigger them.
76
+ """
77
+ import torch
78
+
79
+ with torch_compiler_warning_context():
80
+ if not prefer_public and hasattr(torch, "_dynamo"):
81
+ torch._dynamo.reset()
82
+ elif hasattr(torch, "compiler") and hasattr(torch.compiler, "reset"):
83
+ torch.compiler.reset()
84
+ elif hasattr(torch, "_dynamo") and hasattr(torch._dynamo, "reset"):
85
+ torch._dynamo.reset()
86
+
87
+
88
+ def _truthy(x: str | None) -> bool:
89
+ return str(x).lower() in {"1", "true", "yes", "on"}
90
+
91
+
92
+ def _should_configure_torchinductor_cache_on_import() -> bool:
93
+ """Return whether importing Dendra should configure TorchInductor's cache."""
94
+ return _truthy(os.environ.get("DENDRA_CONFIGURE_TORCHINDUCTOR_CACHE", "0"))
95
+
96
+
97
+ def _should_cache_cpu_isa_for_dendra() -> bool:
98
+ """Return whether the selected policy enables Dendra's CPU ISA cache."""
99
+ policy = os.environ.get("DENDRA_INDUCTOR_CACHE_POLICY", "process").lower()
100
+ return policy in (
101
+ _PROCESS_INDUCTOR_CACHE_POLICIES | _SHARED_INDUCTOR_CACHE_POLICIES
102
+ )
103
+
104
+
105
+ def _get_notebook_or_process_id() -> str:
106
+ """
107
+ Stable-enough process identifier for interactive notebooks and scripts.
108
+
109
+ We include PID to avoid cross-process cache sharing.
110
+ UUID avoids stale collisions if a PID is reused.
111
+ """
112
+ user = "unknown"
113
+ try:
114
+ user = getpass.getuser()
115
+ except Exception:
116
+ pass
117
+
118
+ return f"{user}_{os.getpid()}_{uuid.uuid4().hex[:8]}"
119
+
120
+
121
+ def configure_torchinductor_cache_for_dendra() -> None:
122
+ """
123
+ Configure TorchInductor cache behavior before importing torch.
124
+
125
+ Environment variables
126
+ ---------------------
127
+ DENDRA_CONFIGURE_TORCHINDUCTOR_CACHE:
128
+ "1" opts ``import dendra`` into Dendra-managed TorchInductor cache
129
+ behavior, including this function and the CPU ISA cache. Default: "0".
130
+ The flag does not affect explicit calls to this function.
131
+
132
+ DENDRA_INDUCTOR_CACHE_POLICY:
133
+ "process" -> per-process cache directory, safest for notebooks
134
+ "shared" -> leave TORCHINDUCTOR_CACHE_DIR alone unless user set it
135
+ "off" -> do nothing
136
+
137
+ DENDRA_INDUCTOR_CACHE_ROOT:
138
+ Parent directory for Dendra-owned process caches.
139
+ Default: tempfile.gettempdir()/dendra_torchinductor
140
+
141
+ DENDRA_INDUCTOR_DISABLE_PCH:
142
+ "1" disables C++ precompiled header caching where supported.
143
+
144
+ DENDRA_INDUCTOR_COMPILE_THREADS:
145
+ Sets TORCHINDUCTOR_COMPILE_THREADS. Default "1" in process mode.
146
+
147
+ DENDRA_INDUCTOR_CLEANUP:
148
+ "1" removes the Dendra-owned per-process cache at interpreter exit.
149
+ """
150
+ policy = os.environ.get("DENDRA_INDUCTOR_CACHE_POLICY", "process").lower()
151
+
152
+ if policy in _DISABLED_INDUCTOR_CACHE_POLICIES:
153
+ return
154
+
155
+ # This is the important limitation. If torch is already imported, some
156
+ # Inductor config/cache state may already be initialized.
157
+ if "torch" in sys.modules:
158
+ warnings.warn(
159
+ "Dendra: torch was imported before Dendra configured TorchInductor. "
160
+ "Dendra cannot reliably isolate TORCHINDUCTOR_CACHE_DIR after torch import. "
161
+ "For concurrent notebook use, import dendra before torch or set "
162
+ "TORCHINDUCTOR_CACHE_DIR before starting Python.",
163
+ RuntimeWarning,
164
+ stacklevel=2,
165
+ )
166
+ return
167
+
168
+ if policy in _PROCESS_INDUCTOR_CACHE_POLICIES:
169
+ root = Path(
170
+ os.environ.get(
171
+ "DENDRA_INDUCTOR_CACHE_ROOT",
172
+ str(Path(tempfile.gettempdir()) / "dendra_torchinductor"),
173
+ )
174
+ ).expanduser()
175
+
176
+ session_id = os.environ.get("DENDRA_INDUCTOR_SESSION_ID")
177
+ if not session_id:
178
+ session_id = _get_notebook_or_process_id()
179
+
180
+ cache_dir = root / session_id
181
+ cache_dir.mkdir(parents=True, exist_ok=True)
182
+
183
+ # Only set if user did not explicitly choose a cache directory.
184
+ os.environ.setdefault("TORCHINDUCTOR_CACHE_DIR", str(cache_dir))
185
+
186
+ # This is the key workaround for the malformed .h/.h.pch failure mode.
187
+ # It is an internal/implementation-dependent knob, so setdefault keeps
188
+ # user choice authoritative.
189
+ if _truthy(os.environ.get("DENDRA_INDUCTOR_DISABLE_PCH", "1")):
190
+ os.environ.setdefault("TORCHINDUCTOR_CPP_CACHE_PRECOMPILE_HEADERS", "0")
191
+
192
+ # Reduces within-process async compile races and makes failures easier
193
+ # to reproduce/debug. Users can override.
194
+ os.environ.setdefault(
195
+ "TORCHINDUCTOR_COMPILE_THREADS",
196
+ os.environ.get("DENDRA_INDUCTOR_COMPILE_THREADS", "1"),
197
+ )
198
+
199
+ if _truthy(os.environ.get("DENDRA_INDUCTOR_CLEANUP", "0")):
200
+
201
+ def _cleanup_cache_dir(path: Path = cache_dir) -> None:
202
+ shutil.rmtree(path, ignore_errors=True)
203
+
204
+ atexit.register(_cleanup_cache_dir)
205
+
206
+ elif policy in _SHARED_INDUCTOR_CACHE_POLICIES:
207
+ # Respect user's chosen shared cache. Do not clean it automatically.
208
+ if _truthy(os.environ.get("DENDRA_INDUCTOR_DISABLE_PCH", "1")):
209
+ os.environ.setdefault("TORCHINDUCTOR_CPP_CACHE_PRECOMPILE_HEADERS", "0")
210
+
211
+ else:
212
+ warnings.warn(
213
+ f"Dendra: unknown DENDRA_INDUCTOR_CACHE_POLICY={policy!r}; "
214
+ "expected 'process', 'shared', or 'off'.",
215
+ RuntimeWarning,
216
+ stacklevel=2,
217
+ )
dendra/cli.py ADDED
@@ -0,0 +1,51 @@
1
+ """Command-line entry points for Dendra deployment diagnostics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from typing import Optional, Sequence
8
+
9
+ from .diagnostics import collect_doctor_report
10
+
11
+
12
+ def _parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(prog="dendra")
14
+ subparsers = parser.add_subparsers(dest="command")
15
+ doctor = subparsers.add_parser(
16
+ "doctor", help="inspect CPU, CUDA, and optional native-extension readiness"
17
+ )
18
+ doctor.add_argument(
19
+ "--require-cuda",
20
+ action="store_true",
21
+ help="treat unavailable CUDA/PyTorch CUDA support as an error",
22
+ )
23
+ doctor.add_argument(
24
+ "--probe-native-bitpack",
25
+ action="store_true",
26
+ help="opt into JIT build/load and tiny native bitpack kernel launches",
27
+ )
28
+ doctor.add_argument(
29
+ "--json",
30
+ action="store_true",
31
+ help="emit the structured report as JSON",
32
+ )
33
+ return parser
34
+
35
+
36
+ def main(argv: Optional[Sequence[str]] = None) -> int:
37
+ parser = _parser()
38
+ args = parser.parse_args(argv)
39
+ if args.command != "doctor":
40
+ parser.print_help()
41
+ return 2
42
+
43
+ report = collect_doctor_report(
44
+ require_cuda=args.require_cuda,
45
+ probe_native_bitpack=args.probe_native_bitpack,
46
+ )
47
+ if args.json:
48
+ print(json.dumps(report.as_dict(), indent=2, sort_keys=True))
49
+ else:
50
+ print(report.to_text())
51
+ return 0 if report.ok else 1
dendra/const.py ADDED
@@ -0,0 +1,34 @@
1
+ """dendra.const
2
+
3
+ Mathematical and physical constants used throughout Dendra.
4
+
5
+ All values are plain Python ``float`` constants (TorchScript-friendly).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+
12
+ import torch
13
+
14
+ __all__ = ["PI", "E", "TAU", "R", "FARADAY"]
15
+
16
+ PI: torch.jit.Final[float] = math.pi
17
+ """:math:`\\pi`, ratio of a circle's circumference to its diameter."""
18
+
19
+ E: torch.jit.Final[float] = math.e
20
+ """Euler's number."""
21
+
22
+ TAU: torch.jit.Final[float] = math.tau
23
+ """:math:`\\tau = 2\\pi`."""
24
+
25
+ R: torch.jit.Final[float] = 8314.46261815324
26
+ """Molar gas constant in Dendra's millivolt convention.
27
+
28
+ The value has units mV·C/(mol·K), so ``R * temperature / FARADAY`` is in mV
29
+ when :data:`FARADAY` is expressed in C/mol. It is numerically equal to the gas
30
+ constant in J/(kmol·K).
31
+ """
32
+
33
+ FARADAY: torch.jit.Final[float] = 96485.33212331001
34
+ """Faraday constant in SI units of C/mol."""
@@ -0,0 +1,5 @@
1
+ from .h5 import H5Reader
2
+
3
+ __all__ = [
4
+ "H5Reader",
5
+ ]