invarlock 0.2.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.
Files changed (132) hide show
  1. invarlock/__init__.py +33 -0
  2. invarlock/__main__.py +10 -0
  3. invarlock/_data/runtime/profiles/ci_cpu.yaml +15 -0
  4. invarlock/_data/runtime/profiles/release.yaml +23 -0
  5. invarlock/_data/runtime/tiers.yaml +76 -0
  6. invarlock/adapters/__init__.py +102 -0
  7. invarlock/adapters/_capabilities.py +45 -0
  8. invarlock/adapters/auto.py +99 -0
  9. invarlock/adapters/base.py +530 -0
  10. invarlock/adapters/base_types.py +85 -0
  11. invarlock/adapters/hf_bert.py +852 -0
  12. invarlock/adapters/hf_gpt2.py +403 -0
  13. invarlock/adapters/hf_llama.py +485 -0
  14. invarlock/adapters/hf_mixin.py +383 -0
  15. invarlock/adapters/hf_onnx.py +112 -0
  16. invarlock/adapters/hf_t5.py +137 -0
  17. invarlock/adapters/py.typed +1 -0
  18. invarlock/assurance/__init__.py +43 -0
  19. invarlock/cli/__init__.py +8 -0
  20. invarlock/cli/__main__.py +8 -0
  21. invarlock/cli/_evidence.py +25 -0
  22. invarlock/cli/_json.py +75 -0
  23. invarlock/cli/adapter_auto.py +162 -0
  24. invarlock/cli/app.py +287 -0
  25. invarlock/cli/commands/__init__.py +26 -0
  26. invarlock/cli/commands/certify.py +403 -0
  27. invarlock/cli/commands/doctor.py +1358 -0
  28. invarlock/cli/commands/explain_gates.py +151 -0
  29. invarlock/cli/commands/export_html.py +100 -0
  30. invarlock/cli/commands/plugins.py +1331 -0
  31. invarlock/cli/commands/report.py +354 -0
  32. invarlock/cli/commands/run.py +4146 -0
  33. invarlock/cli/commands/verify.py +1040 -0
  34. invarlock/cli/config.py +396 -0
  35. invarlock/cli/constants.py +68 -0
  36. invarlock/cli/device.py +92 -0
  37. invarlock/cli/doctor_helpers.py +74 -0
  38. invarlock/cli/errors.py +6 -0
  39. invarlock/cli/overhead_utils.py +60 -0
  40. invarlock/cli/provenance.py +66 -0
  41. invarlock/cli/utils.py +41 -0
  42. invarlock/config.py +56 -0
  43. invarlock/core/__init__.py +62 -0
  44. invarlock/core/abi.py +15 -0
  45. invarlock/core/api.py +274 -0
  46. invarlock/core/auto_tuning.py +317 -0
  47. invarlock/core/bootstrap.py +226 -0
  48. invarlock/core/checkpoint.py +221 -0
  49. invarlock/core/contracts.py +73 -0
  50. invarlock/core/error_utils.py +64 -0
  51. invarlock/core/events.py +298 -0
  52. invarlock/core/exceptions.py +95 -0
  53. invarlock/core/registry.py +481 -0
  54. invarlock/core/retry.py +146 -0
  55. invarlock/core/runner.py +2041 -0
  56. invarlock/core/types.py +154 -0
  57. invarlock/edits/__init__.py +12 -0
  58. invarlock/edits/_edit_utils.py +249 -0
  59. invarlock/edits/_external_utils.py +268 -0
  60. invarlock/edits/noop.py +47 -0
  61. invarlock/edits/py.typed +1 -0
  62. invarlock/edits/quant_rtn.py +801 -0
  63. invarlock/edits/registry.py +166 -0
  64. invarlock/eval/__init__.py +23 -0
  65. invarlock/eval/bench.py +1207 -0
  66. invarlock/eval/bootstrap.py +50 -0
  67. invarlock/eval/data.py +2052 -0
  68. invarlock/eval/metrics.py +2167 -0
  69. invarlock/eval/primary_metric.py +767 -0
  70. invarlock/eval/probes/__init__.py +24 -0
  71. invarlock/eval/probes/fft.py +139 -0
  72. invarlock/eval/probes/mi.py +213 -0
  73. invarlock/eval/probes/post_attention.py +323 -0
  74. invarlock/eval/providers/base.py +67 -0
  75. invarlock/eval/providers/seq2seq.py +111 -0
  76. invarlock/eval/providers/text_lm.py +113 -0
  77. invarlock/eval/providers/vision_text.py +93 -0
  78. invarlock/eval/py.typed +1 -0
  79. invarlock/guards/__init__.py +18 -0
  80. invarlock/guards/_contracts.py +9 -0
  81. invarlock/guards/invariants.py +640 -0
  82. invarlock/guards/policies.py +805 -0
  83. invarlock/guards/py.typed +1 -0
  84. invarlock/guards/rmt.py +2097 -0
  85. invarlock/guards/spectral.py +1419 -0
  86. invarlock/guards/tier_config.py +354 -0
  87. invarlock/guards/variance.py +3298 -0
  88. invarlock/guards_ref/__init__.py +15 -0
  89. invarlock/guards_ref/rmt_ref.py +40 -0
  90. invarlock/guards_ref/spectral_ref.py +135 -0
  91. invarlock/guards_ref/variance_ref.py +60 -0
  92. invarlock/model_profile.py +353 -0
  93. invarlock/model_utils.py +221 -0
  94. invarlock/observability/__init__.py +10 -0
  95. invarlock/observability/alerting.py +535 -0
  96. invarlock/observability/core.py +546 -0
  97. invarlock/observability/exporters.py +565 -0
  98. invarlock/observability/health.py +588 -0
  99. invarlock/observability/metrics.py +457 -0
  100. invarlock/observability/py.typed +1 -0
  101. invarlock/observability/utils.py +553 -0
  102. invarlock/plugins/__init__.py +12 -0
  103. invarlock/plugins/hello_guard.py +33 -0
  104. invarlock/plugins/hf_awq_adapter.py +82 -0
  105. invarlock/plugins/hf_bnb_adapter.py +79 -0
  106. invarlock/plugins/hf_gptq_adapter.py +78 -0
  107. invarlock/plugins/py.typed +1 -0
  108. invarlock/py.typed +1 -0
  109. invarlock/reporting/__init__.py +7 -0
  110. invarlock/reporting/certificate.py +3221 -0
  111. invarlock/reporting/certificate_schema.py +244 -0
  112. invarlock/reporting/dataset_hashing.py +215 -0
  113. invarlock/reporting/guards_analysis.py +948 -0
  114. invarlock/reporting/html.py +32 -0
  115. invarlock/reporting/normalizer.py +235 -0
  116. invarlock/reporting/policy_utils.py +517 -0
  117. invarlock/reporting/primary_metric_utils.py +265 -0
  118. invarlock/reporting/render.py +1442 -0
  119. invarlock/reporting/report.py +903 -0
  120. invarlock/reporting/report_types.py +278 -0
  121. invarlock/reporting/utils.py +175 -0
  122. invarlock/reporting/validate.py +631 -0
  123. invarlock/security.py +176 -0
  124. invarlock/sparsity_utils.py +323 -0
  125. invarlock/utils/__init__.py +150 -0
  126. invarlock/utils/digest.py +45 -0
  127. invarlock-0.2.0.dist-info/METADATA +586 -0
  128. invarlock-0.2.0.dist-info/RECORD +132 -0
  129. invarlock-0.2.0.dist-info/WHEEL +5 -0
  130. invarlock-0.2.0.dist-info/entry_points.txt +20 -0
  131. invarlock-0.2.0.dist-info/licenses/LICENSE +201 -0
  132. invarlock-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,221 @@
1
+ """
2
+ InvarLock Model Utilities
3
+ =====================
4
+
5
+ Model-specific utility functions for InvarLock.
6
+ Contains device handling, seeding, and model manipulation helpers.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import contextlib
12
+ import json
13
+ import random
14
+ import time
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import numpy as np
19
+
20
+ try:
21
+ import torch
22
+
23
+ TORCH_AVAILABLE = True
24
+ except ModuleNotFoundError: # utils may be used without torch
25
+ TORCH_AVAILABLE = False
26
+
27
+ __all__ = [
28
+ "set_seed",
29
+ "get_device",
30
+ "time_block",
31
+ "json_save",
32
+ "json_load",
33
+ "dump_df",
34
+ "deterministic",
35
+ "extract_input_ids",
36
+ ]
37
+
38
+
39
+ # ------------------------------------------------------------------#
40
+ # Reproducibility
41
+ # ------------------------------------------------------------------#
42
+
43
+
44
+ def set_seed(seed: int = 42):
45
+ """Deterministic seeds for Python, NumPy and Torch (if present)."""
46
+ random.seed(seed)
47
+ np.random.seed(seed)
48
+ if TORCH_AVAILABLE:
49
+ torch.manual_seed(seed)
50
+ torch.cuda.manual_seed_all(seed)
51
+ torch.backends.cudnn.deterministic = True
52
+ torch.backends.cudnn.benchmark = False
53
+
54
+
55
+ # ------------------------------------------------------------------#
56
+ # Device helper
57
+ # ------------------------------------------------------------------#
58
+
59
+
60
+ def get_device(prefer_gpu: bool = True):
61
+ """Return cuda → mps → cpu in that order of preference."""
62
+ if prefer_gpu and TORCH_AVAILABLE:
63
+ if torch.cuda.is_available():
64
+ return torch.device("cuda")
65
+ if torch.backends.mps.is_available():
66
+ return torch.device("mps")
67
+ return torch.device("cpu") if TORCH_AVAILABLE else "cpu"
68
+
69
+
70
+ # ------------------------------------------------------------------#
71
+ # Timing context manager
72
+ # ------------------------------------------------------------------#
73
+
74
+
75
+ @contextlib.contextmanager
76
+ def time_block(label: str = "", stream=print):
77
+ t0 = time.time()
78
+ yield
79
+ stream(f"[{label}] {time.time() - t0:6.2f}s")
80
+
81
+
82
+ # ------------------------------------------------------------------#
83
+ # Tiny JSON wrappers
84
+ # ------------------------------------------------------------------#
85
+
86
+
87
+ def json_save(obj: dict[str, Any], path: str | Path):
88
+ path = Path(path)
89
+ path.parent.mkdir(parents=True, exist_ok=True)
90
+ with path.open("w") as fh:
91
+ json.dump(obj, fh, indent=2)
92
+
93
+
94
+ def json_load(path: str | Path) -> dict[str, Any]:
95
+ with Path(path).open() as fh:
96
+ return json.load(fh)
97
+
98
+
99
+ # ------------------------------------------------------------------#
100
+ # DataFrame dumping
101
+ # ------------------------------------------------------------------#
102
+
103
+
104
+ def dump_df(df, path: str | Path, fmt: str | None = None):
105
+ """
106
+ Save a Pandas DataFrame; format inferred from extension or `fmt`.
107
+ """
108
+
109
+ path = Path(path)
110
+ fmt = fmt or path.suffix.lstrip(".").lower() or "csv"
111
+ if fmt == "csv":
112
+ df.to_csv(path.with_suffix(".csv"), index=False)
113
+ elif fmt in {"parquet", "pq"}:
114
+ df.to_parquet(path.with_suffix(".parquet"))
115
+ else:
116
+ raise ValueError(f"Unsupported DataFrame format: {fmt}")
117
+
118
+
119
+ # ------------------------------------------------------------------#
120
+ # Deterministic execution context manager
121
+ # ------------------------------------------------------------------#
122
+
123
+
124
+ @contextlib.contextmanager
125
+ def deterministic(seed: int = 13):
126
+ """
127
+ A context manager to ensure deterministic execution for a block of code.
128
+ Sets seeds for torch, random, and numpy, and handles GPU cache clearing.
129
+ """
130
+ # Store original states
131
+ random_state = random.getstate()
132
+ np_state = np.random.get_state()
133
+
134
+ torch_state = None
135
+ cuda_state = None
136
+ if TORCH_AVAILABLE:
137
+ torch_state = torch.get_rng_state()
138
+ if torch.cuda.is_available():
139
+ cuda_state = torch.cuda.get_rng_state_all()
140
+
141
+ # Set new seeds
142
+ random.seed(seed)
143
+ np.random.seed(seed)
144
+ if TORCH_AVAILABLE:
145
+ torch.manual_seed(seed)
146
+ if torch.cuda.is_available():
147
+ torch.cuda.manual_seed_all(seed)
148
+
149
+ try:
150
+ yield
151
+ finally:
152
+ # Restore original states
153
+ random.setstate(random_state)
154
+ np.random.set_state(np_state)
155
+ if TORCH_AVAILABLE and torch_state is not None:
156
+ torch.set_rng_state(torch_state)
157
+ if torch.cuda.is_available() and cuda_state is not None:
158
+ torch.cuda.set_rng_state_all(cuda_state)
159
+
160
+ # Clean up memory
161
+ if hasattr(torch, "mps") and torch.backends.mps.is_available():
162
+ torch.mps.empty_cache()
163
+
164
+
165
+ # ------------------------------------------------------------------#
166
+ # Batch utilities
167
+ # ------------------------------------------------------------------#
168
+
169
+
170
+ def extract_input_ids(batch, device=None, strict=False):
171
+ """
172
+ Extract input_ids from various batch formats.
173
+
174
+ Args:
175
+ batch: Batch data (dict, tensor, or other format)
176
+ device: Target device (optional)
177
+ strict: Whether to be strict about format (optional)
178
+
179
+ Returns:
180
+ torch.Tensor: input_ids tensor or None if not found
181
+ """
182
+ if batch is None:
183
+ return None
184
+
185
+ # Handle dictionary format
186
+ if isinstance(batch, dict):
187
+ if "input_ids" in batch:
188
+ input_ids = batch["input_ids"]
189
+ elif "inputs" in batch:
190
+ input_ids = batch["inputs"]
191
+ else:
192
+ if strict:
193
+ raise ValueError(
194
+ f"No input_ids found in batch dict with keys: {list(batch.keys())}"
195
+ )
196
+ return None
197
+ # Handle tensor format
198
+ elif TORCH_AVAILABLE and isinstance(batch, torch.Tensor):
199
+ input_ids = batch
200
+ # Handle list/tuple format
201
+ elif isinstance(batch, list | tuple) and len(batch) > 0:
202
+ input_ids = batch[0]
203
+ else:
204
+ if strict:
205
+ raise ValueError(f"Unsupported batch format: {type(batch)}")
206
+ return None
207
+
208
+ # Convert to tensor if needed
209
+ if TORCH_AVAILABLE and not isinstance(input_ids, torch.Tensor):
210
+ try:
211
+ input_ids = torch.tensor(input_ids)
212
+ except Exception as e:
213
+ if strict:
214
+ raise ValueError(f"Could not convert to tensor: {e}") from e
215
+ return None
216
+
217
+ # Move to device if specified
218
+ if device is not None and TORCH_AVAILABLE and hasattr(input_ids, "to"):
219
+ input_ids = input_ids.to(device)
220
+
221
+ return input_ids
@@ -0,0 +1,10 @@
1
+ """Observability namespace (`invarlock.observability`)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .alerting import * # noqa: F401,F403
6
+ from .core import * # noqa: F401,F403
7
+ from .exporters import * # noqa: F401,F403
8
+ from .health import * # noqa: F401,F403
9
+ from .metrics import * # noqa: F401,F403
10
+ from .utils import * # noqa: F401,F403