Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from experiments.analyze_stability import balanced_candidate_score | |
| from experiments.analyze_study import _paired_specificity, _safe_spearman | |
| from experiments.collect_activations import _promptwide_max_encoding | |
| from featurelens.sae import SparseEncoding | |
| from featurelens.study import OfflineStudy | |
| def test_promptwide_max_encoding_ignores_padding_and_max_pools() -> None: | |
| encoding = SparseEncoding( | |
| indices=torch.tensor( | |
| [ | |
| [[1, 2], [1, 3], [2, 4]], | |
| [[5, 6], [5, 7], [7, 8]], | |
| ] | |
| ), | |
| values=torch.tensor( | |
| [ | |
| [[1.0, 2.0], [4.0, 3.0], [5.0, 1.0]], | |
| [[9.0, 9.0], [2.0, 4.0], [7.0, 6.0]], | |
| ] | |
| ), | |
| ) | |
| mask = torch.tensor([[1, 1, 1], [0, 1, 1]]) | |
| pooled = _promptwide_max_encoding(encoding, mask) | |
| row0 = dict(zip(pooled[0].indices.tolist(), pooled[0].values.tolist(), strict=True)) | |
| row1 = dict(zip(pooled[1].indices.tolist(), pooled[1].values.tolist(), strict=True)) | |
| assert row0 == {1: 4.0, 2: 5.0, 3: 3.0, 4: 1.0} | |
| assert row1 == {5: 2.0, 7: 7.0, 8: 6.0} | |
| def test_balanced_candidate_score_rewards_selectivity_not_raw_scale() -> None: | |
| target = np.array([30.0, 1000.0]) | |
| other = np.array([0.0, 950.0]) | |
| rate = np.array([1.0, 1.0]) | |
| score = balanced_candidate_score(target, other, rate) | |
| assert score[0] > score[1] | |
| def test_paired_specificity_uses_random_ensemble_mean_per_task() -> None: | |
| frame = pd.DataFrame( | |
| [ | |
| {'task_id': 'a', 'condition': 'sae_feature', 'target_mean_logprob_delta': 0.4}, | |
| {'task_id': 'a', 'condition': 'random_norm_matched', 'target_mean_logprob_delta': 0.1}, | |
| {'task_id': 'a', 'condition': 'random_norm_matched', 'target_mean_logprob_delta': -0.1}, | |
| {'task_id': 'b', 'condition': 'sae_feature', 'target_mean_logprob_delta': -0.2}, | |
| {'task_id': 'b', 'condition': 'random_norm_matched', 'target_mean_logprob_delta': 0.05}, | |
| {'task_id': 'b', 'condition': 'random_norm_matched', 'target_mean_logprob_delta': -0.15}, | |
| ] | |
| ) | |
| result = _paired_specificity( | |
| frame, | |
| effect_column='target_mean_logprob_delta', | |
| seed=7, | |
| ) | |
| assert np.isclose(result['sae_abs_mean'], 0.3) | |
| assert np.isclose(result['random_abs_mean'], 0.1) | |
| assert np.isclose(result['specificity_ratio'], 3.0) | |
| assert result['n_tasks'] == 2 | |
| def test_safe_spearman_handles_small_samples() -> None: | |
| small = _safe_spearman(pd.Series([1.0, 2.0]), pd.Series([2.0, 1.0])) | |
| assert small['n'] == 2 | |
| assert np.isnan(small['rho']) | |
| enough = _safe_spearman(pd.Series([1.0, 2.0, 3.0]), pd.Series([3.0, 2.0, 1.0])) | |
| assert enough['n'] == 3 | |
| assert np.isclose(enough['rho'], -1.0) | |
| def test_offline_study_reports_missing_and_complete(tmp_path: Path) -> None: | |
| study = OfflineStudy(tmp_path) | |
| assert not study.complete | |
| assert 'not materialized yet' in study.overview_markdown() | |
| for name in OfflineStudy.REQUIRED: | |
| path = tmp_path / name | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| if name.endswith('.json'): | |
| if name == 'study_summary.json': | |
| payload = { | |
| 'median_selected_feature_resample_support': 0.8, | |
| 'correlations': { | |
| 'heldout_auroc_vs_target_specificity': {'rho': -0.2, 'n': 7}, | |
| 'heldout_auroc_vs_js_specificity': {'rho': 0.4, 'n': 7}, | |
| }, | |
| } | |
| else: | |
| payload = {'headline': 'Synthetic headline.', 'interpretation': 'Synthetic interpretation.'} | |
| path.write_text(json.dumps(payload), encoding='utf-8') | |
| elif name.endswith('.csv'): | |
| path.write_text('x\n1\n', encoding='utf-8') | |
| else: | |
| path.write_text('# report\n', encoding='utf-8') | |
| study = OfflineStudy(tmp_path) | |
| assert study.complete | |
| text = study.overview_markdown() | |
| assert 'Synthetic headline.' in text | |
| assert '80.0%' in text | |