File size: 2,269 Bytes
9d24374
 
b3d11b8
9d24374
 
b3d11b8
 
 
 
 
 
9d24374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3d11b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d24374
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
from __future__ import annotations

import pytest
import torch

from featurelens.interventions import (
    InterventionSpec,
    joint_residual_delta,
    normalized_random_control,
    residual_delta,
)


def test_ablation_delta() -> None:
    spec = InterventionSpec('ablate', 0.0)
    assert spec.delta_activation(3.5) == -3.5


def test_scale_delta() -> None:
    spec = InterventionSpec('scale', 2.0)
    assert spec.delta_activation(3.5) == 3.5
    assert InterventionSpec('scale', 0.0).delta_activation(3.5) == -3.5


def test_injection_delta() -> None:
    spec = InterventionSpec('inject', -4.0)
    assert spec.delta_activation(123.0) == -4.0


def test_residual_delta_is_decoder_direction_times_coefficient_delta() -> None:
    direction = torch.tensor([1.0, 2.0, -1.0])
    delta = residual_delta(direction, 3.0, InterventionSpec('ablate', 0.0))
    assert torch.allclose(delta, torch.tensor([-3.0, -6.0, 3.0]))


def test_joint_ablation_sums_feature_deltas() -> None:
    directions = torch.tensor(
        [
            [1.0, 0.0, 2.0],
            [0.0, 1.0, -1.0],
        ]
    )
    delta, coefficient_deltas = joint_residual_delta(
        directions,
        [2.0, 3.0],
        InterventionSpec('ablate', 0.0),
    )
    assert coefficient_deltas == [-2.0, -3.0]
    assert torch.allclose(delta, torch.tensor([-2.0, -3.0, -1.0]))


def test_joint_scale_uses_same_multiplier_per_feature() -> None:
    directions = torch.eye(2)
    delta, coefficient_deltas = joint_residual_delta(
        directions,
        [2.0, 4.0],
        InterventionSpec('scale', 1.5),
    )
    assert coefficient_deltas == [1.0, 2.0]
    assert torch.allclose(delta, torch.tensor([1.0, 2.0]))


def test_joint_injection_is_rejected() -> None:
    with pytest.raises(ValueError, match='only'):
        joint_residual_delta(
            torch.eye(2),
            [1.0, 1.0],
            InterventionSpec('inject', 2.0),
        )


def test_random_control_matches_norm_and_is_deterministic() -> None:
    delta = torch.tensor([3.0, 4.0, 0.0])
    a = normalized_random_control(delta, seed=7)
    b = normalized_random_control(delta, seed=7)
    assert torch.allclose(a, b)
    assert torch.allclose(torch.linalg.vector_norm(a), torch.linalg.vector_norm(delta), atol=1e-5)