11"""Tests for :class:`OLMoDDPTrainModule` config and construction."""
22
3+ import contextlib
34from types import SimpleNamespace
4- from typing import Optional
5+ from typing import Any , Optional , cast
56
67import pytest
78import torch
89import torch .distributed as dist
910
1011from olmo_core .config import DType
11- from olmo_core .distributed .parallel import DataParallelType
12+ from olmo_core .distributed .parallel import DataParallelType , PipelineScheduleType
1213from olmo_core .exceptions import OLMoConfigurationError
1314from olmo_core .nn .attention import AttentionConfig , AttentionType
1415from olmo_core .nn .ddp .block import OLMoDDPTransformerBlockConfig
@@ -58,6 +59,22 @@ def test_moe_v2_train_module_config_roundtrips_with_parallelism():
5859 assert restored .pp_config is not None and restored .pp_config .degree == 2
5960
6061
62+ def test_olmo_ddp_pipeline_parallelism_requires_custom_schedule ():
63+ model = _tiny_model_config ().build (init_device = "cpu" )
64+ config = OLMoDDPTrainModuleConfig (
65+ rank_microbatch_size = 512 ,
66+ max_sequence_length = 512 ,
67+ optim = OLMoDDPOptimizerConfig (lr = 1e-3 ),
68+ pp_config = TransformerPipelineParallelConfig (
69+ degree = 2 ,
70+ schedule = PipelineScheduleType .interleaved_1F1B ,
71+ ),
72+ )
73+
74+ with pytest .raises (OLMoConfigurationError , match = "requires a custom pipeline schedule" ):
75+ config .build (model , device = torch .device ("cpu" ), eval_only = True )
76+
77+
6178def _tiny_model_config (
6279 * ,
6380 d_model : int = 64 ,
@@ -345,12 +362,13 @@ def __init__(self, **kwargs):
345362 built_with .update (kwargs )
346363
347364 train_module = OLMoDDPTrainModule .__new__ (OLMoDDPTrainModule )
348- train_module ._trainer = SimpleNamespace (dp_process_group = None )
349- train_module ._pp_config = SimpleNamespace (
350- schedule = "schedule" , forward_pull_ahead_extra_activations = None
365+ train_module ._trainer = cast (Any , SimpleNamespace (dp_process_group = None ))
366+ train_module ._pp_config = TransformerPipelineParallelConfig (
367+ degree = 2 ,
368+ schedule = PipelineScheduleType .custom_interleaved_1F1B ,
351369 )
352- train_module ._pp_stages = [object ()]
353- train_module .model_parts = [object ()]
370+ train_module ._pp_stages = cast ( Any , [object ()])
371+ train_module .model_parts = cast ( Any , [object ()])
354372 train_module .rank_microbatch_size = 4
355373 train_module .world_mesh = {"dense" : {"pp" : object ()}}
356374 monkeypatch .setattr (
@@ -373,7 +391,7 @@ def test_broadcast_pp_eval_output_reconstructs_on_non_final_rank(monkeypatch):
373391 None ,
374392 ]
375393 train_module = OLMoDDPTrainModule .__new__ (OLMoDDPTrainModule )
376- train_module .pp_group = object ()
394+ train_module .pp_group = cast ( Any , object () )
377395 train_module .pp_group_rank = 0
378396 train_module .pp_final_stage_rank = 1
379397 train_module .device = torch .device ("cpu" )
@@ -405,6 +423,47 @@ def fake_broadcast(tensor, **kwargs):
405423 assert torch .equal (actual , wanted )
406424
407425
426+ def test_pipeline_eval_does_not_forward_training_only_loss_kwarg (monkeypatch ):
427+ train_module = OLMoDDPTrainModule .__new__ (OLMoDDPTrainModule )
428+ train_module ._cp_config = None
429+ train_module ._tp_config = None
430+ train_module ._pp_config = TransformerPipelineParallelConfig (
431+ degree = 2 ,
432+ schedule = PipelineScheduleType .custom_interleaved_1F1B ,
433+ )
434+ train_module .model_parts = cast (Any , [torch .nn .Identity ()])
435+ train_module .label_ignore_index = - 100
436+ input_ids = torch .ones ((2 , 3 ), dtype = torch .long )
437+ labels = input_ids .clone ()
438+ microbatch_output = LMOutputWithLoss (
439+ logits = torch .ones ((2 , 3 , 4 )),
440+ loss = torch .ones ((2 , 3 )),
441+ ce_loss = torch .ones ((2 , 3 )),
442+ z_loss = None ,
443+ )
444+ forwarded_kwargs = {}
445+
446+ monkeypatch .setattr (
447+ train_module ,
448+ "_prepare_batch" ,
449+ lambda batch , labels : (input_ids , labels , {}),
450+ )
451+ monkeypatch .setattr (train_module , "_eval_batch_context" , contextlib .nullcontext )
452+
453+ def fake_run_pipeline_eval (input_ids , labels , ** kwargs ):
454+ del input_ids , labels
455+ forwarded_kwargs .update (kwargs )
456+ return [[microbatch_output ]]
457+
458+ monkeypatch .setattr (train_module , "run_pipeline_eval" , fake_run_pipeline_eval )
459+ monkeypatch .setattr (train_module , "_broadcast_pp_eval_output" , lambda output : output )
460+
461+ output = train_module .eval_batch ({}, labels )
462+
463+ assert output is not None
464+ assert "batch_num_tokens_for_loss" not in forwarded_kwargs
465+
466+
408467def _run_global_lb_group_is_stage_local ():
409468 train_module = OLMoDDPTrainModule .__new__ (OLMoDDPTrainModule )
410469 train_module .world_mesh = {}
0 commit comments