physiotrace 0.1.0a1__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.
- physiotrace/__init__.py +67 -0
- physiotrace/_validation.py +54 -0
- physiotrace/audit.py +627 -0
- physiotrace/bootstrap.py +87 -0
- physiotrace/diagnostics.py +188 -0
- physiotrace/heads.py +65 -0
- physiotrace/intervene.py +239 -0
- physiotrace/metrics.py +55 -0
- physiotrace/recover.py +278 -0
- physiotrace/report.py +123 -0
- physiotrace/stress.py +57 -0
- physiotrace/synthetic.py +138 -0
- physiotrace/verify.py +77 -0
- physiotrace/weights.py +238 -0
- physiotrace-0.1.0a1.dist-info/METADATA +211 -0
- physiotrace-0.1.0a1.dist-info/RECORD +19 -0
- physiotrace-0.1.0a1.dist-info/WHEEL +5 -0
- physiotrace-0.1.0a1.dist-info/licenses/LICENSE +21 -0
- physiotrace-0.1.0a1.dist-info/top_level.txt +1 -0
physiotrace/__init__.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""PhysioTRACE: behavioral audit of provenance reliance in frozen-encoder task heads.
|
|
2
|
+
|
|
3
|
+
Recover how decodable provenance is, Stress fixed heads by reversing only the
|
|
4
|
+
provenance--target association, Intervene by removing a train-localized
|
|
5
|
+
provenance component, and Verify the remedy against matched random controls
|
|
6
|
+
within a declared utility margin.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import bootstrap, intervene, metrics, recover, stress, synthetic, weights
|
|
10
|
+
from .audit import PairedSplit, Split, audit_observational, audit_observational_crossfit, audit_paired
|
|
11
|
+
from .diagnostics import Finding, Gates, PhysioTraceWarning
|
|
12
|
+
from .intervene import Subspace, Whitening, conditional_direction, paired_subspace, random_subspaces
|
|
13
|
+
from .metrics import weighted_auroc
|
|
14
|
+
from .recover import LPIResult, fit_calibrated_probe, lpi
|
|
15
|
+
from .report import AuditReport
|
|
16
|
+
from .stress import excess_vulnerability, regime_aurocs
|
|
17
|
+
from .verify import Verdict
|
|
18
|
+
from .weights import (
|
|
19
|
+
cell_weights,
|
|
20
|
+
designed_coupling_table,
|
|
21
|
+
fixed_marginal_table,
|
|
22
|
+
independence_weights,
|
|
23
|
+
observational_regimes,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from importlib.metadata import PackageNotFoundError, version as _version
|
|
28
|
+
|
|
29
|
+
__version__ = _version("physiotrace")
|
|
30
|
+
except PackageNotFoundError: # running from a source tree without installation
|
|
31
|
+
__version__ = "0+unknown"
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"AuditReport",
|
|
35
|
+
"Finding",
|
|
36
|
+
"Gates",
|
|
37
|
+
"PhysioTraceWarning",
|
|
38
|
+
"Split",
|
|
39
|
+
"audit_observational_crossfit",
|
|
40
|
+
"LPIResult",
|
|
41
|
+
"PairedSplit",
|
|
42
|
+
"Subspace",
|
|
43
|
+
"Verdict",
|
|
44
|
+
"Whitening",
|
|
45
|
+
"audit_observational",
|
|
46
|
+
"audit_paired",
|
|
47
|
+
"bootstrap",
|
|
48
|
+
"cell_weights",
|
|
49
|
+
"conditional_direction",
|
|
50
|
+
"designed_coupling_table",
|
|
51
|
+
"excess_vulnerability",
|
|
52
|
+
"fit_calibrated_probe",
|
|
53
|
+
"fixed_marginal_table",
|
|
54
|
+
"independence_weights",
|
|
55
|
+
"intervene",
|
|
56
|
+
"lpi",
|
|
57
|
+
"metrics",
|
|
58
|
+
"observational_regimes",
|
|
59
|
+
"paired_subspace",
|
|
60
|
+
"random_subspaces",
|
|
61
|
+
"recover",
|
|
62
|
+
"regime_aurocs",
|
|
63
|
+
"stress",
|
|
64
|
+
"synthetic",
|
|
65
|
+
"weighted_auroc",
|
|
66
|
+
"weights",
|
|
67
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Small input checks shared across modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def as_matrix(values, *, name: str) -> np.ndarray:
|
|
9
|
+
array = np.asarray(values, dtype=np.float64)
|
|
10
|
+
if array.ndim != 2:
|
|
11
|
+
raise ValueError(f"{name} must be a two-dimensional array, got shape {array.shape}.")
|
|
12
|
+
if not np.isfinite(array).all():
|
|
13
|
+
raise ValueError(f"{name} contains non-finite values.")
|
|
14
|
+
return array
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def as_codes(values, *, name: str, n_levels: int | None = None) -> np.ndarray:
|
|
18
|
+
"""Return non-negative integer codes; values must already be encoded."""
|
|
19
|
+
|
|
20
|
+
array = np.asarray(values)
|
|
21
|
+
if array.ndim != 1:
|
|
22
|
+
raise ValueError(f"{name} must be one-dimensional.")
|
|
23
|
+
if not np.issubdtype(array.dtype, np.integer):
|
|
24
|
+
if not np.issubdtype(array.dtype, np.number) or not np.all(np.equal(array, np.floor(array))):
|
|
25
|
+
raise ValueError(f"{name} must contain integer codes.")
|
|
26
|
+
codes = array.astype(np.int64)
|
|
27
|
+
if np.any(codes < 0):
|
|
28
|
+
raise ValueError(f"{name} must contain non-negative codes.")
|
|
29
|
+
if n_levels is not None and np.any(codes >= n_levels):
|
|
30
|
+
raise ValueError(f"{name} contains a code outside [0, {n_levels}).")
|
|
31
|
+
return codes
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def encode(values, levels=None) -> tuple[np.ndarray, tuple]:
|
|
35
|
+
"""Encode arbitrary labels as integer codes in the order of ``levels``."""
|
|
36
|
+
|
|
37
|
+
array = np.asarray(values)
|
|
38
|
+
if levels is None:
|
|
39
|
+
levels = tuple(np.unique(array).tolist())
|
|
40
|
+
else:
|
|
41
|
+
levels = tuple(levels)
|
|
42
|
+
lookup = {level: code for code, level in enumerate(levels)}
|
|
43
|
+
try:
|
|
44
|
+
codes = np.fromiter((lookup[value] for value in array.tolist()), dtype=np.int64, count=len(array))
|
|
45
|
+
except KeyError as error:
|
|
46
|
+
raise ValueError(f"Value {error.args[0]!r} is not among the declared levels {levels}.") from None
|
|
47
|
+
return codes, levels
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def same_length(**arrays) -> int:
|
|
51
|
+
lengths = {name: len(value) for name, value in arrays.items()}
|
|
52
|
+
if len(set(lengths.values())) != 1:
|
|
53
|
+
raise ValueError(f"Inputs must have the same length, got {lengths}.")
|
|
54
|
+
return next(iter(lengths.values()))
|