anhtld commited on
Commit
04f12e2
·
verified ·
1 Parent(s): f80d844

ctt train-calibration hygiene 2026-07-03T16:31:19Z: tests/test_ctt.py

Browse files
Files changed (1) hide show
  1. tests/test_ctt.py +92 -0
tests/test_ctt.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ import numpy as np
3
+ from pathlib import Path
4
+
5
+ torch = pytest.importorskip("torch")
6
+
7
+ from cil.models import CTTConfig, CausalTangentTransport, ChartEncoder, TangentNormalizer
8
+ from cil.models.ctt import chamfer_to_target_set, diversity_loss, negative_boundary_loss
9
+ from scripts.eval_ctt_generated_rollout import ChartItem, _source_pool_for_target
10
+
11
+
12
+ def test_ctt_variants_preserve_tangent_shape():
13
+ z_source = torch.randn(3, 8)
14
+ z_target = torch.randn(3, 8)
15
+ xi_source = torch.randn(3, 5)
16
+
17
+ for variant in ("residual", "gated_residual"):
18
+ model = CausalTangentTransport(
19
+ CTTConfig(chart_feature_dim=10, chart_dim=8, tangent_dim=5, hidden_dim=16, variant=variant)
20
+ )
21
+ output = model(z_source, z_target, xi_source)
22
+ assert output.shape == xi_source.shape
23
+ assert torch.isfinite(output).all()
24
+
25
+
26
+ def test_chart_encoder_and_losses_are_finite():
27
+ encoder = ChartEncoder(input_dim=6, hidden_dim=12, output_dim=8)
28
+ z = encoder(torch.randn(4, 6))
29
+ assert z.shape == (4, 8)
30
+
31
+ predicted = torch.randn(4, 5)
32
+ positives = predicted + 0.1
33
+ negatives = predicted + 10.0
34
+
35
+ assert torch.isfinite(chamfer_to_target_set(predicted, positives))
36
+ assert negative_boundary_loss(predicted, negatives, margin=0.2).item() == pytest.approx(0.0)
37
+ assert diversity_loss(predicted).item() >= 0.0
38
+
39
+
40
+ def test_gated_residual_matches_documented_formula():
41
+ model = CausalTangentTransport(
42
+ CTTConfig(chart_feature_dim=10, chart_dim=2, tangent_dim=2, hidden_dim=4, variant="gated_residual")
43
+ )
44
+ for parameter in model.delta.parameters():
45
+ parameter.data.zero_()
46
+ for parameter in model.gate.parameters():
47
+ parameter.data.zero_()
48
+ model.delta[-1].bias.data[:] = torch.tensor([4.0, -2.0])
49
+ model.gate[2].bias.data[:] = torch.tensor([0.0, 0.0])
50
+
51
+ xi = torch.tensor([[2.0, 6.0]])
52
+ output = model(torch.zeros(1, 2), torch.zeros(1, 2), xi)
53
+
54
+ assert torch.allclose(output, torch.tensor([[3.0, 2.0]]), atol=1.0e-6)
55
+
56
+
57
+ def test_tangent_normalizer_round_trips():
58
+ values = torch.randn(8, 5)
59
+ normalizer = TangentNormalizer.fit(values)
60
+ restored = normalizer.inverse_transform(normalizer.transform(values))
61
+ assert torch.allclose(values, restored, atol=1.0e-5)
62
+
63
+
64
+ def test_rollout_source_pool_excludes_target_chart_and_state_hash():
65
+ def chart(chart_id: str, state_hash: str) -> ChartItem:
66
+ return ChartItem(
67
+ chart_id=chart_id,
68
+ task_id="task",
69
+ seed="0",
70
+ state_hash=state_hash,
71
+ instruction="",
72
+ source_dataset=Path("."),
73
+ base_action=np.zeros((2, 7), dtype=np.float32),
74
+ feature=np.zeros(4, dtype=np.float32),
75
+ positive_tangents=np.zeros((1, 21), dtype=np.float32),
76
+ negative_tangents=np.zeros((0, 21), dtype=np.float32),
77
+ hidden_utilities=[],
78
+ hidden_candidate_types=[],
79
+ stored_base_utility=None,
80
+ )
81
+
82
+ target = chart("chart_a", "state_a")
83
+ sources = [chart("chart_a", "state_a"), chart("chart_b", "state_a"), chart("chart_c", "state_c")]
84
+
85
+ pool = _source_pool_for_target(
86
+ target,
87
+ task_pool=sources,
88
+ source_charts=sources,
89
+ exclude_self_source=True,
90
+ )
91
+
92
+ assert [item.chart_id for item in pool] == ["chart_c"]