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.
- invarlock/__init__.py +33 -0
- invarlock/__main__.py +10 -0
- invarlock/_data/runtime/profiles/ci_cpu.yaml +15 -0
- invarlock/_data/runtime/profiles/release.yaml +23 -0
- invarlock/_data/runtime/tiers.yaml +76 -0
- invarlock/adapters/__init__.py +102 -0
- invarlock/adapters/_capabilities.py +45 -0
- invarlock/adapters/auto.py +99 -0
- invarlock/adapters/base.py +530 -0
- invarlock/adapters/base_types.py +85 -0
- invarlock/adapters/hf_bert.py +852 -0
- invarlock/adapters/hf_gpt2.py +403 -0
- invarlock/adapters/hf_llama.py +485 -0
- invarlock/adapters/hf_mixin.py +383 -0
- invarlock/adapters/hf_onnx.py +112 -0
- invarlock/adapters/hf_t5.py +137 -0
- invarlock/adapters/py.typed +1 -0
- invarlock/assurance/__init__.py +43 -0
- invarlock/cli/__init__.py +8 -0
- invarlock/cli/__main__.py +8 -0
- invarlock/cli/_evidence.py +25 -0
- invarlock/cli/_json.py +75 -0
- invarlock/cli/adapter_auto.py +162 -0
- invarlock/cli/app.py +287 -0
- invarlock/cli/commands/__init__.py +26 -0
- invarlock/cli/commands/certify.py +403 -0
- invarlock/cli/commands/doctor.py +1358 -0
- invarlock/cli/commands/explain_gates.py +151 -0
- invarlock/cli/commands/export_html.py +100 -0
- invarlock/cli/commands/plugins.py +1331 -0
- invarlock/cli/commands/report.py +354 -0
- invarlock/cli/commands/run.py +4146 -0
- invarlock/cli/commands/verify.py +1040 -0
- invarlock/cli/config.py +396 -0
- invarlock/cli/constants.py +68 -0
- invarlock/cli/device.py +92 -0
- invarlock/cli/doctor_helpers.py +74 -0
- invarlock/cli/errors.py +6 -0
- invarlock/cli/overhead_utils.py +60 -0
- invarlock/cli/provenance.py +66 -0
- invarlock/cli/utils.py +41 -0
- invarlock/config.py +56 -0
- invarlock/core/__init__.py +62 -0
- invarlock/core/abi.py +15 -0
- invarlock/core/api.py +274 -0
- invarlock/core/auto_tuning.py +317 -0
- invarlock/core/bootstrap.py +226 -0
- invarlock/core/checkpoint.py +221 -0
- invarlock/core/contracts.py +73 -0
- invarlock/core/error_utils.py +64 -0
- invarlock/core/events.py +298 -0
- invarlock/core/exceptions.py +95 -0
- invarlock/core/registry.py +481 -0
- invarlock/core/retry.py +146 -0
- invarlock/core/runner.py +2041 -0
- invarlock/core/types.py +154 -0
- invarlock/edits/__init__.py +12 -0
- invarlock/edits/_edit_utils.py +249 -0
- invarlock/edits/_external_utils.py +268 -0
- invarlock/edits/noop.py +47 -0
- invarlock/edits/py.typed +1 -0
- invarlock/edits/quant_rtn.py +801 -0
- invarlock/edits/registry.py +166 -0
- invarlock/eval/__init__.py +23 -0
- invarlock/eval/bench.py +1207 -0
- invarlock/eval/bootstrap.py +50 -0
- invarlock/eval/data.py +2052 -0
- invarlock/eval/metrics.py +2167 -0
- invarlock/eval/primary_metric.py +767 -0
- invarlock/eval/probes/__init__.py +24 -0
- invarlock/eval/probes/fft.py +139 -0
- invarlock/eval/probes/mi.py +213 -0
- invarlock/eval/probes/post_attention.py +323 -0
- invarlock/eval/providers/base.py +67 -0
- invarlock/eval/providers/seq2seq.py +111 -0
- invarlock/eval/providers/text_lm.py +113 -0
- invarlock/eval/providers/vision_text.py +93 -0
- invarlock/eval/py.typed +1 -0
- invarlock/guards/__init__.py +18 -0
- invarlock/guards/_contracts.py +9 -0
- invarlock/guards/invariants.py +640 -0
- invarlock/guards/policies.py +805 -0
- invarlock/guards/py.typed +1 -0
- invarlock/guards/rmt.py +2097 -0
- invarlock/guards/spectral.py +1419 -0
- invarlock/guards/tier_config.py +354 -0
- invarlock/guards/variance.py +3298 -0
- invarlock/guards_ref/__init__.py +15 -0
- invarlock/guards_ref/rmt_ref.py +40 -0
- invarlock/guards_ref/spectral_ref.py +135 -0
- invarlock/guards_ref/variance_ref.py +60 -0
- invarlock/model_profile.py +353 -0
- invarlock/model_utils.py +221 -0
- invarlock/observability/__init__.py +10 -0
- invarlock/observability/alerting.py +535 -0
- invarlock/observability/core.py +546 -0
- invarlock/observability/exporters.py +565 -0
- invarlock/observability/health.py +588 -0
- invarlock/observability/metrics.py +457 -0
- invarlock/observability/py.typed +1 -0
- invarlock/observability/utils.py +553 -0
- invarlock/plugins/__init__.py +12 -0
- invarlock/plugins/hello_guard.py +33 -0
- invarlock/plugins/hf_awq_adapter.py +82 -0
- invarlock/plugins/hf_bnb_adapter.py +79 -0
- invarlock/plugins/hf_gptq_adapter.py +78 -0
- invarlock/plugins/py.typed +1 -0
- invarlock/py.typed +1 -0
- invarlock/reporting/__init__.py +7 -0
- invarlock/reporting/certificate.py +3221 -0
- invarlock/reporting/certificate_schema.py +244 -0
- invarlock/reporting/dataset_hashing.py +215 -0
- invarlock/reporting/guards_analysis.py +948 -0
- invarlock/reporting/html.py +32 -0
- invarlock/reporting/normalizer.py +235 -0
- invarlock/reporting/policy_utils.py +517 -0
- invarlock/reporting/primary_metric_utils.py +265 -0
- invarlock/reporting/render.py +1442 -0
- invarlock/reporting/report.py +903 -0
- invarlock/reporting/report_types.py +278 -0
- invarlock/reporting/utils.py +175 -0
- invarlock/reporting/validate.py +631 -0
- invarlock/security.py +176 -0
- invarlock/sparsity_utils.py +323 -0
- invarlock/utils/__init__.py +150 -0
- invarlock/utils/digest.py +45 -0
- invarlock-0.2.0.dist-info/METADATA +586 -0
- invarlock-0.2.0.dist-info/RECORD +132 -0
- invarlock-0.2.0.dist-info/WHEEL +5 -0
- invarlock-0.2.0.dist-info/entry_points.txt +20 -0
- invarlock-0.2.0.dist-info/licenses/LICENSE +201 -0
- invarlock-0.2.0.dist-info/top_level.txt +1 -0
invarlock/model_utils.py
ADDED
|
@@ -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
|