tests/dynamic/test_write_rule.py
from dataclasses import replace
import pytest
import torch
from mcss.dynamic.config import CarrierConfig, WriteConfig
from mcss.dynamic.types import Action, FastWeights, WriteTrace
from mcss.dynamic.write_rule import DirectWriteRule, commit_write
def make_trace(fast, support=True):
return WriteTrace(
"episode",
3,
0,
torch.randn(5, 16),
torch.randn(5, 16),
torch.randn(5, 23),
torch.ones(5) if support else torch.zeros(5),
torch.arange(5),
branch_id=fast.branch_id,
source_state_id=fast.state_id,
)
def test_writes_update_selected_locations_without_mutating_source_and_keep_gradients():
torch.manual_seed(9)
rule = DirectWriteRule(CarrierConfig(), WriteConfig(max_update_norm=0.01))
fast = FastWeights("episode", torch.zeros(8, 16), torch.zeros(8, 16))
proposal = rule.propose(make_trace(fast))
updated = commit_write(fast, proposal, Action.FUSE, cache_revision=3)
assert torch.count_nonzero(updated.delta_fuse) > 0
assert torch.equal(updated.delta_complete, fast.delta_complete)
assert torch.count_nonzero(fast.delta_fuse) == 0
assert proposal.delta_fuse.norm() <= 0.010001
updated.delta_fuse.square().sum().backward()
assert any(p.grad is not None and torch.isfinite(p.grad).all() for p in rule.parameters())
both = commit_write(fast, proposal, Action.ALL, cache_revision=3)
assert torch.equal(both.delta_fuse, updated.delta_fuse)
assert torch.equal(both.delta_complete, proposal.delta_complete)
with pytest.raises(ValueError, match="version"):
commit_write(both, proposal, Action.ALL, cache_revision=3)
with pytest.raises(ValueError, match="episode"):
commit_write(
FastWeights("other", torch.zeros(8, 16), torch.zeros(8, 16)),
proposal,
Action.ALL,
cache_revision=3,
)
def test_no_supported_locations_cannot_write_and_off_ignores_increment():
rule = DirectWriteRule(CarrierConfig(), WriteConfig())
fast = FastWeights("episode", torch.zeros(8, 16), torch.zeros(8, 16))
unsupported = rule.propose(make_trace(fast, False))
assert torch.count_nonzero(unsupported.delta_fuse) == 0
assert torch.count_nonzero(unsupported.delta_complete) == 0
proposal = rule.propose(make_trace(fast))
off = commit_write(fast, proposal, Action.OFF, cache_revision=3)
assert torch.equal(off.delta_fuse, fast.delta_fuse)
assert torch.equal(off.delta_complete, fast.delta_complete)
def test_proposals_cannot_cross_same_episode_branches_or_resets():
rule = DirectWriteRule(CarrierConfig(), WriteConfig())
fast = FastWeights("episode", torch.zeros(8, 16), torch.zeros(8, 16))
proposal = rule.propose(make_trace(fast))
fork = fast.fork()
fresh = FastWeights("episode", torch.zeros(8, 16), torch.zeros(8, 16))
for other in (fork, fresh):
assert other.branch_id != fast.branch_id
with pytest.raises(ValueError, match="branch"):
commit_write(other, proposal, Action.ALL, cache_revision=3)
fork.delta_fuse.add_(1)
assert torch.count_nonzero(fast.delta_fuse) == 0
updated = commit_write(fast, proposal, Action.ALL, cache_revision=3)
assert updated.branch_id == fast.branch_id
def test_fork_preserves_offline_gradient_path():
source = torch.ones(8, 16, requires_grad=True)
fast = FastWeights("episode", source, source * 2)
fork = fast.fork()
(fork.delta_fuse.sum() + fork.delta_complete.sum()).backward()
assert torch.equal(source.grad, torch.full_like(source, 3))
def test_same_source_commits_cannot_cross_their_divergent_successors():
rule = DirectWriteRule(CarrierConfig(), WriteConfig())
fast = FastWeights("episode", torch.zeros(8, 16), torch.zeros(8, 16))
proposal = rule.propose(make_trace(fast))
fuse_only = commit_write(fast, proposal, Action.FUSE, cache_revision=3)
complete_only = commit_write(fast, proposal, Action.COMPLETE, cache_revision=3)
later = rule.propose(replace(make_trace(fuse_only), fast_step=1, cache_revision=4))
commit_write(fuse_only, later, Action.ALL, cache_revision=4)
with pytest.raises(ValueError, match="state"):
commit_write(complete_only, later, Action.ALL, cache_revision=4)
assert not torch.equal(fuse_only.delta_fuse, complete_only.delta_fuse)