CAFF / tests /test_dbm.py
MrDhifallah's picture
Upload folder using huggingface_hub
634ebe8 verified
Raw
History Blame Contribute Delete
3.15 kB
"""
tests/test_dbm.py
=================
Verify DBM (Eqs. 16-17) properties:
• Gate output ∈ (0, 1)^ρ (Eq. 16, sigmoid)
• Δ shape is (B, d, d) (Eq. 17)
• rank(Δ) ≤ ρ (Proposition 4)
• z=0 → fixed Δ (graceful degradation) (paper §6.3)
"""
from __future__ import annotations
import pytest
import torch
from caff.dbm import DBM, ContextGate, DBMBlock
def test_gate_sigmoid_output_range():
"""Eq. 16: g_ℓ ∈ (0, 1)^ρ under sigmoid activation."""
gate = ContextGate(d=64, rho=8, activation="sigmoid")
z = torch.randn(4, 64)
g = gate(z)
assert g.shape == (4, 8)
assert (g > 0).all() and (g < 1).all()
def test_gate_relu_ablation():
"""Ablation §10.1: ReLU gate produces non-negative output."""
gate = ContextGate(d=64, rho=8, activation="relu")
z = torch.randn(4, 64)
g = gate(z)
assert (g >= 0).all()
def test_dbm_shape():
"""Eq. 17: Δ_ℓ ∈ ℝ^{d×d} per batch item."""
dbm = DBM(d=64, rho=8)
g = torch.rand(4, 8)
delta = dbm(g)
assert delta.shape == (4, 64, 64)
def test_dbm_rank_bound():
"""Proposition 4: rank(Δ_ℓ) ≤ ρ.
With randomly initialized U, V and a strictly-positive gate
(sigmoid output is in (0,1)), rank should be exactly ρ
almost surely.
"""
rho = 8
dbm = DBM(d=64, rho=rho)
g = torch.rand(2, rho) * 0.5 + 0.25 # bounded away from 0
delta = dbm(g)
for b in range(delta.shape[0]):
rank = torch.linalg.matrix_rank(delta[b]).item()
assert rank <= rho, f"rank(Δ) = {rank} exceeds ρ = {rho}"
def test_dbm_graceful_degradation_at_z_zero():
"""Paper §6.3: when z=0, gate becomes σ(b^g_ℓ) — a constant —
and Δ_ℓ collapses to a fixed rank-ρ increment, recovering
DepthBilinear."""
block = DBMBlock(d=64, rho=8)
z_zero_a = torch.zeros(2, 64)
z_zero_b = torch.zeros(2, 64)
delta_a = block(z_zero_a)
delta_b = block(z_zero_b)
# Same input ⟹ same output (deterministic)
assert torch.allclose(delta_a, delta_b)
# Both items in the batch produce identical Δ since z is identical
assert torch.allclose(delta_a[0], delta_a[1])
def test_dbm_context_sensitivity():
"""Different z values produce different Δ matrices."""
block = DBMBlock(d=64, rho=8)
z1 = torch.randn(1, 64)
z2 = torch.randn(1, 64)
delta1 = block(z1)
delta2 = block(z2)
assert not torch.allclose(delta1, delta2)
def test_dbm_implements_u_diag_g_v_t():
"""Verify literal Eq. 17: Δ = U · diag(g) · V^T (not element-wise gating).
This guards Failure Mode F1 from the build contract.
"""
d, rho = 16, 4
dbm = DBM(d=d, rho=rho)
g = torch.tensor([[0.5, 0.5, 0.5, 0.5]]) # (1, rho)
delta_module = dbm(g).squeeze(0) # (d, d)
# Reference computation: U @ diag(g) @ V^T
delta_ref = dbm.U @ torch.diag(g.squeeze(0)) @ dbm.V.t()
assert torch.allclose(delta_module, delta_ref, atol=1e-5)