Coverage for tests/simulator/test_helix.py: 100%
74 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1"""
2Tests for the Helix allocator.
4Helix optimizes models one-by-one following MODEL_ORDER using per-model MILP.
5"""
7import sys
8import os
10# Add current path
11sys.path.append(os.getcwd())
13from tests.test_utils import temp_sys_path
15with temp_sys_path("simulator", "streamwise"):
16 from constants import DEFAULT_WORKFLOW_CONFIG
17 from sim_types import GPUType
18 from sim_types import Model
19 from sim_types import MODEL_ORDER
20 from sim_types import Solver
21 from data_loading import load_latency_data
22 from data_loading import load_power_data
23 from model_provisioner.helix import HelixAllocator
24 from model_provisioner.policies import HELIX_POLICY
27def test_get_model_order() -> None:
28 """Test that get_model_order() returns models sorted by MODEL_ORDER."""
29 order = DEFAULT_WORKFLOW_CONFIG.get_model_order()
30 assert len(order) > 0
31 # Check ordering is consistent with MODEL_ORDER
32 for i in range(len(order) - 1):
33 assert MODEL_ORDER[order[i]] < MODEL_ORDER[order[i + 1]]
34 # All models in order should be in the workflow
35 for m in order:
36 assert m in DEFAULT_WORKFLOW_CONFIG.models
39def test_helix_policy_solver() -> None:
40 """Verify HELIX_POLICY uses Solver.HELIX."""
41 assert HELIX_POLICY.solver == Solver.HELIX
44def test_produces_valid_result() -> None:
45 """Verify the Helix result has all required fields populated correctly."""
46 latency_data = load_latency_data("simulator/data/")
47 allocator = HelixAllocator(
48 workflow=DEFAULT_WORKFLOW_CONFIG,
49 latency_data=latency_data,
50 )
51 result = allocator.allocate(
52 num_gpus={GPUType.A100: 32},
53 milp_solver=Solver.HIGHS,
54 )
55 assert result.total_time_s > 0
56 assert result.ttff_s > 0
57 assert result.tbf_s >= 0
58 assert result.cost >= 0
59 assert result.total_energy >= 0
60 assert result.models is not None
61 assert len(result.models) > 0
64def test_produces_valid_result_verbose() -> None:
65 """Verify verbose mode works without errors."""
66 latency_data = load_latency_data("simulator/data/")
67 allocator = HelixAllocator(
68 workflow=DEFAULT_WORKFLOW_CONFIG,
69 latency_data=latency_data,
70 )
71 result = allocator.allocate(
72 num_gpus={GPUType.A100: 32},
73 verbose=True,
74 milp_solver=Solver.HIGHS,
75 )
76 assert result.total_time_s > 0
77 assert result.models is not None
80def test_gpu_budget_respected() -> None:
81 """Verify total GPUs used does not exceed the budget."""
82 latency_data = load_latency_data("simulator/data/")
83 allocator = HelixAllocator(
84 workflow=DEFAULT_WORKFLOW_CONFIG,
85 latency_data=latency_data,
86 )
87 budget = {GPUType.A100: 16}
88 result = allocator.allocate(
89 num_gpus=budget,
90 milp_solver=Solver.HIGHS,
91 )
92 for gpu_type, used in result.gpus_used.items():
93 assert used <= budget.get(gpu_type, 0), \
94 f"{gpu_type.value}: used {used} > budget {budget.get(gpu_type, 0)}"
97def test_all_workflow_models_allocated() -> None:
98 """Verify models are allocated following MODEL_ORDER until GPUs are exhausted.
100 Helix allocates sequentially, so later models may be skipped if earlier
101 models consume the whole budget. With a large GPU pool the first several
102 models should still be present.
103 """
104 latency_data = load_latency_data("simulator/data/")
105 allocator = HelixAllocator(
106 workflow=DEFAULT_WORKFLOW_CONFIG,
107 latency_data=latency_data,
108 )
109 result = allocator.allocate(
110 num_gpus={GPUType.A100: 64},
111 milp_solver=Solver.HIGHS,
112 )
113 # Collect all models with non-empty allocations
114 allocated_models: set[Model] = set()
115 for gpu_type, model_dict in result.models.items():
116 for model, allocs in model_dict.items():
117 if allocs:
118 allocated_models.add(model)
120 # At least the first models in MODEL_ORDER (GEMMA, FLUX, OTHERS) should
121 # always be allocated since they require few GPUs.
122 model_order = DEFAULT_WORKFLOW_CONFIG.get_model_order()
123 for model in model_order[:3]:
124 if DEFAULT_WORKFLOW_CONFIG.model_work.get(model, 0) > 0:
125 assert model in allocated_models, \
126 f"Model {model.value} has work but was not allocated"
128 # At least one model should be allocated
129 assert len(allocated_models) >= 1
132def test_with_power_data() -> None:
133 """Verify allocation works with power data provided."""
134 latency_data = load_latency_data("simulator/data/")
135 power_data = load_power_data("simulator/data/")
136 allocator = HelixAllocator(
137 workflow=DEFAULT_WORKFLOW_CONFIG,
138 latency_data=latency_data,
139 power_data=power_data,
140 )
141 result = allocator.allocate(
142 num_gpus={GPUType.A100: 32},
143 milp_solver=Solver.HIGHS,
144 )
145 assert result.total_time_s > 0
146 assert result.total_energy >= 0
147 assert result.cost >= 0
150def test_custom_per_model_time_limit() -> None:
151 """Verify custom per-model time limit is accepted."""
152 latency_data = load_latency_data("simulator/data/")
153 allocator = HelixAllocator(
154 workflow=DEFAULT_WORKFLOW_CONFIG,
155 latency_data=latency_data,
156 )
157 result = allocator.allocate(
158 num_gpus={GPUType.A100: 16},
159 per_model_time_limit=10,
160 milp_solver=Solver.HIGHS,
161 )
162 assert result.total_time_s > 0