Module neuralops.tests.test_weight_copy
Unit tests for weight copying, QKV slicing, and projection detection.
Functions
def set_random_seed()-
Expand source code
@pytest.fixture(autouse=True) def set_random_seed(): torch.manual_seed(42)
Classes
class TestCopyProjectionWeights-
Expand source code
class TestCopyProjectionWeights: """Verify _copy_projection_weights across all module conventions and safeguards.""" @pytest.mark.parametrize("proj_name", [ "q_proj", "q_lin", "query", "k_proj", "k_lin", "key", "v_proj", "v_lin", "value", "out_proj", "out_lin", "o_proj", "dense", ]) def test_standard_linear_copy(self, proj_name): class Mod(nn.Module): def __init__(self): super().__init__() setattr(self, proj_name, nn.Linear(32, 32)) mod = Mod() src_w = torch.randn(32, 32) src_b = torch.randn(32) _copy_projection_weights(getattr(mod, proj_name), src_w, src_b) target = getattr(mod, proj_name) assert torch.allclose(target.weight, src_w, atol=1e-6) assert torch.allclose(target.bias, src_b, atol=1e-6) err = compute_reconstruction_error(src_w, target) assert err < 1e-5 def test_property_target_protection(self): """Verify that layers with @property weight are safely handled without crash.""" class FactorizedPropertyMock(nn.Module): def __init__(self): super().__init__() self.U = nn.Parameter(torch.randn(32, 8)) self.V = nn.Parameter(torch.randn(8, 32)) self.bias = nn.Parameter(torch.zeros(32)) @property def weight(self): return self.U @ self.V mod = FactorizedPropertyMock() src_w = torch.randn(32, 32) src_b = torch.randn(32) # Must not raise AttributeError: can't set attribute _copy_projection_weights(mod, src_w, src_b) def test_graceful_missing_projection_fallback(self): """Target module with unexpected structure logs warning and does not crash.""" class CustomEmpty(nn.Module): pass mod = CustomEmpty() src_w = torch.randn(32, 32) _copy_projection_weights(mod, src_w, None)Verify _copy_projection_weights across all module conventions and safeguards.
Methods
def test_graceful_missing_projection_fallback(self)-
Expand source code
def test_graceful_missing_projection_fallback(self): """Target module with unexpected structure logs warning and does not crash.""" class CustomEmpty(nn.Module): pass mod = CustomEmpty() src_w = torch.randn(32, 32) _copy_projection_weights(mod, src_w, None)Target module with unexpected structure logs warning and does not crash.
def test_property_target_protection(self)-
Expand source code
def test_property_target_protection(self): """Verify that layers with @property weight are safely handled without crash.""" class FactorizedPropertyMock(nn.Module): def __init__(self): super().__init__() self.U = nn.Parameter(torch.randn(32, 8)) self.V = nn.Parameter(torch.randn(8, 32)) self.bias = nn.Parameter(torch.zeros(32)) @property def weight(self): return self.U @ self.V mod = FactorizedPropertyMock() src_w = torch.randn(32, 32) src_b = torch.randn(32) # Must not raise AttributeError: can't set attribute _copy_projection_weights(mod, src_w, src_b)Verify that layers with @property weight are safely handled without crash.
def test_standard_linear_copy(self, proj_name)-
Expand source code
@pytest.mark.parametrize("proj_name", [ "q_proj", "q_lin", "query", "k_proj", "k_lin", "key", "v_proj", "v_lin", "value", "out_proj", "out_lin", "o_proj", "dense", ]) def test_standard_linear_copy(self, proj_name): class Mod(nn.Module): def __init__(self): super().__init__() setattr(self, proj_name, nn.Linear(32, 32)) mod = Mod() src_w = torch.randn(32, 32) src_b = torch.randn(32) _copy_projection_weights(getattr(mod, proj_name), src_w, src_b) target = getattr(mod, proj_name) assert torch.allclose(target.weight, src_w, atol=1e-6) assert torch.allclose(target.bias, src_b, atol=1e-6) err = compute_reconstruction_error(src_w, target) assert err < 1e-5
class TestSliceFusedQKVWeights-
Expand source code
class TestSliceFusedQKVWeights: """Verify _slice_fused_qkv_weights handles Conv1D and Linear fused QKV projections.""" def test_conv1d_transposition_and_slicing(self): # GPT-2 Conv1D weight: shape [in_features, 3 * out_features] -> [32, 96] in_dim = 32 out_dim = 32 weight = torch.randn(in_dim, 3 * out_dim) bias = torch.randn(3 * out_dim) sliced = _slice_fused_qkv_weights(weight, bias, num_heads=2) q_w, q_b = sliced["q"] k_w, k_b = sliced["k"] v_w, v_b = sliced["v"] # Linear expects [out_features, in_features] -> [32, 32] assert q_w.shape == (out_dim, in_dim) assert k_w.shape == (out_dim, in_dim) assert v_w.shape == (out_dim, in_dim) assert q_b.shape == (out_dim,) assert k_b.shape == (out_dim,) assert v_b.shape == (out_dim,) # Exact values check expected_q = weight[:, :out_dim].t().contiguous() assert torch.allclose(q_w, expected_q, atol=1e-6) def test_standard_linear_fused_slicing(self): # Standard Linear fused QKV: shape [3 * out_features, in_features] -> [96, 32] in_dim = 32 out_dim = 32 weight = torch.randn(3 * out_dim, in_dim) bias = torch.randn(3 * out_dim) sliced = _slice_fused_qkv_weights(weight, bias, num_heads=2) q_w, q_b = sliced["q"] assert q_w.shape == (out_dim, in_dim) assert torch.allclose(q_w, weight[:out_dim, :], atol=1e-6) assert torch.allclose(q_b, bias[:out_dim], atol=1e-6) def test_missing_bias_handling(self): weight = torch.randn(32, 96) sliced = _slice_fused_qkv_weights(weight, None, num_heads=2) assert sliced["q"][1] is None assert sliced["k"][1] is None assert sliced["v"][1] is NoneVerify _slice_fused_qkv_weights handles Conv1D and Linear fused QKV projections.
Methods
def test_conv1d_transposition_and_slicing(self)-
Expand source code
def test_conv1d_transposition_and_slicing(self): # GPT-2 Conv1D weight: shape [in_features, 3 * out_features] -> [32, 96] in_dim = 32 out_dim = 32 weight = torch.randn(in_dim, 3 * out_dim) bias = torch.randn(3 * out_dim) sliced = _slice_fused_qkv_weights(weight, bias, num_heads=2) q_w, q_b = sliced["q"] k_w, k_b = sliced["k"] v_w, v_b = sliced["v"] # Linear expects [out_features, in_features] -> [32, 32] assert q_w.shape == (out_dim, in_dim) assert k_w.shape == (out_dim, in_dim) assert v_w.shape == (out_dim, in_dim) assert q_b.shape == (out_dim,) assert k_b.shape == (out_dim,) assert v_b.shape == (out_dim,) # Exact values check expected_q = weight[:, :out_dim].t().contiguous() assert torch.allclose(q_w, expected_q, atol=1e-6) def test_missing_bias_handling(self)-
Expand source code
def test_missing_bias_handling(self): weight = torch.randn(32, 96) sliced = _slice_fused_qkv_weights(weight, None, num_heads=2) assert sliced["q"][1] is None assert sliced["k"][1] is None assert sliced["v"][1] is None def test_standard_linear_fused_slicing(self)-
Expand source code
def test_standard_linear_fused_slicing(self): # Standard Linear fused QKV: shape [3 * out_features, in_features] -> [96, 32] in_dim = 32 out_dim = 32 weight = torch.randn(3 * out_dim, in_dim) bias = torch.randn(3 * out_dim) sliced = _slice_fused_qkv_weights(weight, bias, num_heads=2) q_w, q_b = sliced["q"] assert q_w.shape == (out_dim, in_dim) assert torch.allclose(q_w, weight[:out_dim, :], atol=1e-6) assert torch.allclose(q_b, bias[:out_dim], atol=1e-6)
class TestUniversalAdapterAndModelAdapter-
Expand source code
class TestUniversalAdapterAndModelAdapter: """Verify get_weight_bias retrieves weights correctly across naming conventions.""" def test_universal_adapter_linear(self): class AttentionBlock(nn.Module): def __init__(self): super().__init__() self.query = nn.Linear(32, 32) self.key = nn.Linear(32, 32) self.value = nn.Linear(32, 32) self.dense = nn.Linear(32, 32, bias=False) block = AttentionBlock() q_w, q_b = UniversalAdapter.get_weight_bias(block, "q") assert q_w.shape == (32, 32) assert q_b is not None out_w, out_b = UniversalAdapter.get_weight_bias(block, "o") assert out_w.shape == (32, 32) assert out_b is None def test_canonical_aliases_coverage(self): for role in ("q", "k", "v", "o"): assert role in CANONICAL_PROJECTION_ALIASES aliases = CANONICAL_PROJECTION_ALIASES[role] assert len(aliases) >= 3Verify get_weight_bias retrieves weights correctly across naming conventions.
Methods
def test_canonical_aliases_coverage(self)-
Expand source code
def test_canonical_aliases_coverage(self): for role in ("q", "k", "v", "o"): assert role in CANONICAL_PROJECTION_ALIASES aliases = CANONICAL_PROJECTION_ALIASES[role] assert len(aliases) >= 3 def test_universal_adapter_linear(self)-
Expand source code
def test_universal_adapter_linear(self): class AttentionBlock(nn.Module): def __init__(self): super().__init__() self.query = nn.Linear(32, 32) self.key = nn.Linear(32, 32) self.value = nn.Linear(32, 32) self.dense = nn.Linear(32, 32, bias=False) block = AttentionBlock() q_w, q_b = UniversalAdapter.get_weight_bias(block, "q") assert q_w.shape == (32, 32) assert q_b is not None out_w, out_b = UniversalAdapter.get_weight_bias(block, "o") assert out_w.shape == (32, 32) assert out_b is None