|
| 1 | +""" |
| 2 | +Automated Physics Verification Suite |
| 3 | +------------------------------------ |
| 4 | +This test suite "bulletproofs" the mlsysim Silicon Zoo. |
| 5 | +It iterates over every registered hardware node and ensures that its |
| 6 | +specifications obey known laws of physics and sensible bounds. |
| 7 | +This prevents contributors from accidentally adding a chip with |
| 8 | +"80 TB/s" of bandwidth when they meant "80 GB/s". |
| 9 | +""" |
| 10 | +import pytest |
| 11 | +from mlsysim.hardware.registry import Hardware |
| 12 | +from mlsysim.core.constants import ureg |
| 13 | + |
| 14 | +def get_all_hardware(): |
| 15 | + """Extracts all instantiated HardwareNode objects from the registry.""" |
| 16 | + nodes = [] |
| 17 | + # Collect from all sub-registries |
| 18 | + for registry in [Hardware.Cloud, Hardware.Workstation, Hardware.Mobile, Hardware.Edge, Hardware.Tiny]: |
| 19 | + for attr_name in dir(registry): |
| 20 | + if not attr_name.startswith('_'): |
| 21 | + attr = getattr(registry, attr_name) |
| 22 | + # Check if it's a HardwareNode |
| 23 | + if hasattr(attr, 'compute') and hasattr(attr, 'memory'): |
| 24 | + nodes.append(attr) |
| 25 | + return nodes |
| 26 | + |
| 27 | +@pytest.mark.parametrize("node", get_all_hardware(), ids=lambda n: n.name) |
| 28 | +def test_physics_arithmetic_intensity(node): |
| 29 | + """ |
| 30 | + Ridge point (Arithmetic Intensity) must be positive and within |
| 31 | + historical/physical bounds (typically between 1 and 2000 FLOP/byte). |
| 32 | + """ |
| 33 | + ridge = node.ridge_point() |
| 34 | + |
| 35 | + # Must be strictly positive |
| 36 | + assert ridge.magnitude > 0, f"{node.name} has zero or negative ridge point: {ridge}" |
| 37 | + |
| 38 | + # Tiny edge devices might have very low ridge points (e.g. 0.05), but cloud GPUs |
| 39 | + # are usually 100-500. We set a safe global upper bound of 5000 FLOP/byte. |
| 40 | + # Anything higher implies a typo in FLOPS (too high) or Bandwidth (too low). |
| 41 | + assert ridge.m_as("flop/byte") < 5000, f"{node.name} has physically improbable ridge point: {ridge}" |
| 42 | + |
| 43 | +@pytest.mark.parametrize("node", get_all_hardware(), ids=lambda n: n.name) |
| 44 | +def test_physics_power_density(node): |
| 45 | + """ |
| 46 | + TDP must be within safe operating limits. |
| 47 | + A single chip rarely exceeds 1500W. |
| 48 | + A rack-scale system (like NVL72) might reach 150,000W. |
| 49 | + """ |
| 50 | + if node.tdp is not None: |
| 51 | + tdp_w = node.tdp.m_as("watt") |
| 52 | + assert tdp_w > 0, f"{node.name} has zero or negative TDP: {tdp_w}W" |
| 53 | + assert tdp_w <= 150_000, f"{node.name} exceeds max rack-scale power density: {tdp_w}W" |
| 54 | + |
| 55 | +@pytest.mark.parametrize("node", get_all_hardware(), ids=lambda n: n.name) |
| 56 | +def test_physics_memory_bandwidth(node): |
| 57 | + """ |
| 58 | + Memory bandwidth must be physically achievable. |
| 59 | + Sub-GB/s is possible for TinyML. |
| 60 | + Wafer-scale (Cerebras) can hit ~25 PB/s. |
| 61 | + """ |
| 62 | + bw_gbs = node.memory.bandwidth.m_as("GB/s") |
| 63 | + assert bw_gbs > 0, f"{node.name} has zero memory bandwidth." |
| 64 | + |
| 65 | + # Check for accidental TB/s vs GB/s typos |
| 66 | + if "Cloud" in node.__class__.__module__ or "Cloud" in str(node): |
| 67 | + # A cloud GPU should generally have > 100 GB/s bandwidth |
| 68 | + assert bw_gbs > 100 or node.name == "Google TPU v1", f"{node.name} has suspiciously low bandwidth for cloud: {bw_gbs} GB/s" |
| 69 | + |
| 70 | +@pytest.mark.parametrize("node", get_all_hardware(), ids=lambda n: n.name) |
| 71 | +def test_physics_peak_flops(node): |
| 72 | + """ |
| 73 | + Peak FLOPS must be positive. |
| 74 | + """ |
| 75 | + flops_tflops = node.compute.peak_flops.m_as("TFLOPs/s") |
| 76 | + assert flops_tflops > 0, f"{node.name} has zero peak FLOPS." |
0 commit comments