from __future__ import annotations import pytest mlx = pytest.importorskip("mlx.core") from sgjm.training.config import TrainingConfig from sgjm.training.data import ByteDataset, synthetic_corpus def test_baseline_param_count_within_10pct_of_sgjm(): """Baseline parameter count must be within 10% of SGJM total.""" from sgjm.training.mlx_backend.baseline import BaselineLM from sgjm.training.mlx_backend.model import SGJM cfg = TrainingConfig.sgjm_25m() sgjm = SGJM(cfg.model) baseline = BaselineLM(cfg.model) sgjm_n = sgjm.num_parameters() base_n = baseline.num_parameters() assert 0.9 * sgjm_n <= base_n <= 1.1 * sgjm_n, ( f"baseline {base_n/1e6:.2f}M vs sgjm {sgjm_n/1e6:.2f}M — more than 10% apart" ) def test_baseline_forward_returns_correct_shapes(): """BaselineLM.__call__ returns (hidden, logits) with correct shapes.""" from sgjm.training.mlx_backend.baseline import BaselineLM cfg = TrainingConfig.smoke() model = BaselineLM(cfg.model) import mlx.core as mx B, T = 2, cfg.optim.seq_len idx = mx.zeros((B, T), dtype=mx.int32) hidden, logits = model(idx) assert hidden.shape == (B, T, cfg.model.d_model) assert logits.shape == (B, T, cfg.model.vocab_size) def test_mlx_baseline_num_parameters_positive(): """num_parameters returns a positive integer.""" from sgjm.training.mlx_backend.baseline import BaselineLM cfg = TrainingConfig.smoke() model = BaselineLM(cfg.model) n = model.num_parameters() assert isinstance(n, int) assert n > 0 def test_mlx_baseline_smoke_train(tmp_path): """Baseline training with arch='baseline' completes without error.""" cfg = TrainingConfig.smoke() cfg.arch = "baseline" cfg.checkpoint_dir = str(tmp_path / "baseline") from sgjm.training.mlx_backend.trainer import train result = train(cfg, "mlx") assert result.final_step == cfg.optim.max_steps - 1 assert result.checkpoint_path is not None