| """The starting weights a cross-device comparison actually shares. |
| |
| `pretrain.py` and `agreement.py` both said, in a docstring, that building on |
| CPU under a fixed seed gives bit-identical weights on any machine, and every |
| number they produced rested on it. For most models it is true — an LSTM built |
| that way hashes identically on macOS and on Linux. |
| |
| For S4 and S4D it is false, and not because of a bug. `hippo.nplr` diagonalises |
| the HiPPO matrix with `torch.linalg.eigh`. Eigen*values* are unique and matched |
| across the two platforms to every digit printed, which is why `A_imag` looked |
| fine. Eigen*vectors* are fixed only up to a phase, and macOS Accelerate and |
| Linux LAPACK are each free to return a different one. `B` and `P` are |
| projections through those vectors, so they inherit it: measured across a Mac |
| Studio and an RTX 5090 box, `B` differed by a relative 1.5 and `P` by 0.53 |
| while `A_imag` was identical. |
| |
| The comparison was therefore reporting a 2.6e-01 output difference for the |
| vendored S4D — unchanged in float64, which reads exactly like a different |
| kernel — when the two machines had simply built two different models. With the |
| weights shared, the same pair agrees at 4e-07. |
| |
| A test on one machine cannot observe a cross-platform difference. What it can |
| do is pin the mechanism that now carries the assumption: that weights written |
| by one run are what a later run gets, exactly, in place of whatever the seed |
| would have produced. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import importlib.util |
| import sys |
| from pathlib import Path |
|
|
| import pytest |
| import torch |
| import torch.nn as nn |
|
|
| import torch_dimensions as td |
|
|
| ROOT = Path(__file__).resolve().parent.parent |
| sys.path.insert(0, str(ROOT / "benchmarks")) |
|
|
| spec = importlib.util.spec_from_file_location( |
| "_td_init_weights", ROOT / "benchmarks" / "init_weights.py" |
| ) |
| init_weights = importlib.util.module_from_spec(spec) |
| sys.modules["_td_init_weights"] = init_weights |
| spec.loader.exec_module(init_weights) |
|
|
|
|
| LAT = td.Lattice(shape=(4, 5), names=("h", "w"), time=True) |
|
|
|
|
| def test_the_first_run_writes_and_the_second_loads(tmp_path): |
| a, b = nn.Linear(8, 8), nn.Linear(8, 8) |
| assert init_weights.sync(a, tmp_path, "m") == "written" |
| assert init_weights.sync(b, tmp_path, "m") == "loaded" |
| for pa, pb in zip(a.parameters(), b.parameters(), strict=True): |
| assert torch.equal(pa, pb) |
|
|
|
|
| def test_loading_overrides_whatever_the_seed_produced(tmp_path): |
| """The point of the mechanism: the loaded values win over construction. |
| |
| If `sync` returned "loaded" while leaving the model on its own weights, |
| every comparison would still be measuring initialisation drift and would |
| still report a plausible-looking number. |
| """ |
| torch.manual_seed(0) |
| reference = nn.Linear(8, 8) |
| init_weights.sync(reference, tmp_path, "m") |
|
|
| torch.manual_seed(999) |
| other = nn.Linear(8, 8) |
| assert not torch.equal(other.weight, reference.weight) |
|
|
| assert init_weights.sync(other, tmp_path, "m") == "loaded" |
| assert torch.equal(other.weight, reference.weight) |
| assert torch.equal(other.bias, reference.bias) |
|
|
|
|
| def test_no_store_means_no_change_and_says_so(tmp_path): |
| """`--init` is opt-in; without it the behaviour is exactly what it was.""" |
| torch.manual_seed(3) |
| model = nn.Linear(8, 8) |
| before = model.weight.detach().clone() |
| assert init_weights.sync(model, None, "m") == "seed" |
| assert torch.equal(model.weight, before) |
|
|
|
|
| def test_every_parameter_and_buffer_round_trips_for_a_real_model(tmp_path): |
| """A `state_dict` is not just parameters. S4's kernel keeps buffers, and a |
| mechanism that restored parameters while leaving buffers to the seed would |
| reintroduce the bug in the exact place it came from.""" |
| pytest.importorskip("einops", reason="the vendored S4 needs the [upstream] extra") |
| pytest.importorskip("hydra", reason="the s4 pipeline needs hydra-core") |
|
|
| torch.manual_seed(0) |
| first = td.S4(32, 2, LAT, d_input=1, d_state=16) |
| init_weights.sync(first, tmp_path, "s4") |
|
|
| torch.manual_seed(1) |
| second = td.S4(32, 2, LAT, d_input=1, d_state=16) |
| assert init_weights.sync(second, tmp_path, "s4") == "loaded" |
|
|
| sd_a, sd_b = first.state_dict(), second.state_dict() |
| assert set(sd_a) == set(sd_b) |
| for key in sd_a: |
| assert torch.equal(sd_a[key], sd_b[key]), f"{key} did not round trip" |
|
|
|
|
| def test_shared_weights_make_two_builds_agree_where_the_seed_would_not(tmp_path): |
| """End to end, in the shape the benchmark uses it: build, sync, and the two |
| models compute the same thing. On one machine the seed would also have |
| achieved this — the value of the test is that it fails loudly if `sync` |
| ever stops applying, which is the failure that hid for a whole run.""" |
| pytest.importorskip("einops", reason="the vendored S4 needs the [upstream] extra") |
| pytest.importorskip("hydra", reason="the s4 pipeline needs hydra-core") |
|
|
| torch.manual_seed(0) |
| a = td.S4D(32, 2, LAT, d_input=1, d_state=16).eval() |
| init_weights.sync(a, tmp_path, "s4d") |
| torch.manual_seed(7) |
| b = td.S4D(32, 2, LAT, d_input=1, d_state=16).eval() |
| init_weights.sync(b, tmp_path, "s4d") |
|
|
| x = torch.randn(2, 3, *LAT.shape, 1) |
| with torch.no_grad(): |
| assert torch.equal(a(x), b(x)) |
|
|
|
|
| def test_the_eigenvalues_are_the_reproducible_part(tmp_path): |
| """Why the bug was invisible: the part of the decomposition that *is* |
| unique agrees, so `A_imag` matched across platforms to twelve decimals and |
| the initialisation looked sound. Pinned here so the diagnosis stays |
| attached to the code it explains.""" |
| pytest.importorskip("einops", reason="the vendored S4 needs the [upstream] extra") |
|
|
| from torch_dimensions._vendor.s4.src.models.hippo.hippo import nplr |
|
|
| w1, p1, b1, v1 = nplr("legs", 32) |
| w2, p2, b2, v2 = nplr("legs", 32) |
| |
| |
| assert torch.allclose(w1, w2) |
| assert w1.is_complex(), "the eigenvalues are the spectrum of the HiPPO matrix" |
|
|