mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-07-10 11:22:07 -05:00
refactor: move mlsysim to repo root, extract fmt module from viz
Moves the mlsysim package from book/quarto/mlsysim/ to the repo root so it is importable as a proper top-level package across the codebase. Key changes: - mlsysim/fmt.py: new top-level module for all formatting helpers (fmt, sci, check, md_math, fmt_full, fmt_split, etc.), moved out of viz/ - mlsysim/viz/__init__.py: now exports only plot utilities; dashboard.py (marimo-only) is no longer wildcard-exported and must be imported explicitly by marimo labs - mlsysim/__init__.py: added `from . import fmt` and `from .core import constants`; removed broken `from .viz import plots as viz` alias - execute-env.yml: fixed PYTHONPATH from "../../.." to "../.." so chapters resolve to repo root, not parent of repo - 51 QMD files: updated `from mlsysim.viz import <fmt-fns>` to `from mlsysim.fmt import <fmt-fns>` - book/quarto/mlsys/: legacy shadow package contents cleaned up; stub __init__.py remains for backward compat - All Vol1 and Vol2 chapters verified to build with `binder build pdf`
This commit is contained in:
11
mlsysim/sim/__init__.py
Normal file
11
mlsysim/sim/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# mlsysim.sim — The ML Systems Simulator Sub-package
|
||||
|
||||
from .personas import Persona, Personas
|
||||
from .simulations import BaseSimulation, ResourceSimulation
|
||||
from .ledger import (
|
||||
SystemLedger,
|
||||
PerformanceMetrics,
|
||||
SustainabilityMetrics,
|
||||
EconomicMetrics,
|
||||
ReliabilityMetrics
|
||||
)
|
||||
84
mlsysim/sim/ledger.py
Normal file
84
mlsysim/sim/ledger.py
Normal file
@@ -0,0 +1,84 @@
|
||||
# ledger.py
|
||||
"""
|
||||
MLSys Scorecard Module
|
||||
======================
|
||||
The multi-dimensional 'Scorecard' for MLSys simulations.
|
||||
It tracks metrics across four primary engineering axes:
|
||||
Performance, Sustainability, Economics, and Reliability.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any
|
||||
import pandas as pd
|
||||
from ..core.constants import ureg, Q_
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PerformanceMetrics:
|
||||
"""🚀 Performance: Speed and Utilization metrics."""
|
||||
latency: Q_
|
||||
throughput: Q_
|
||||
mfu: float
|
||||
hfu: float
|
||||
bottleneck: str
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SustainabilityMetrics:
|
||||
"""🌍 Sustainability: Environmental impact and resource efficiency."""
|
||||
energy: Q_
|
||||
carbon_kg: float
|
||||
pue: float
|
||||
water_liters: float
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EconomicMetrics:
|
||||
"""💰 Economics: Total Cost of Ownership (TCO) and unit economics."""
|
||||
capex: float
|
||||
opex: float
|
||||
tco: float
|
||||
cost_per_million: float
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ReliabilityMetrics:
|
||||
"""🛡️ Reliability: Resilience, uptime, and recovery metrics."""
|
||||
mttf: Q_
|
||||
goodput: float
|
||||
recovery_time: Q_
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SystemLedger:
|
||||
"""
|
||||
The Universal Scorecard for all MLSys simulation results.
|
||||
Binds the four dimensions into a single immutable result object.
|
||||
"""
|
||||
performance: PerformanceMetrics
|
||||
sustainability: SustainabilityMetrics
|
||||
economics: EconomicMetrics
|
||||
reliability: ReliabilityMetrics
|
||||
|
||||
mission_name: str
|
||||
track_name: str
|
||||
choice_summary: str
|
||||
|
||||
def validate(self) -> None:
|
||||
"""Ensures physical invariants are maintained."""
|
||||
assert 0 <= self.performance.mfu <= 1.0, f"MFU {self.performance.mfu} must be between 0 and 1"
|
||||
assert self.performance.latency.m >= 0, "Latency cannot be negative"
|
||||
assert self.sustainability.carbon_kg >= 0, "Carbon footprint cannot be negative"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Flattens the ledger into a simple dictionary for JSON/UI consumption."""
|
||||
return {
|
||||
"mission": self.mission_name,
|
||||
"track": self.track_name,
|
||||
"choice": self.choice_summary,
|
||||
"latency_ms": self.performance.latency.m_as("ms"),
|
||||
"throughput_sps": self.performance.throughput.m_as("1/second"),
|
||||
"mfu_pct": self.performance.mfu * 100,
|
||||
"carbon_kg": self.sustainability.carbon_kg,
|
||||
"tco_usd": self.economics.tco,
|
||||
"goodput_pct": self.reliability.goodput * 100
|
||||
}
|
||||
|
||||
def to_df(self) -> pd.DataFrame:
|
||||
"""Converts the metrics to a single-row Pandas DataFrame for easy plotting."""
|
||||
return pd.DataFrame([self.to_dict()])
|
||||
93
mlsysim/sim/personas.py
Normal file
93
mlsysim/sim/personas.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# personas.py
|
||||
"""
|
||||
MLSys Personas
|
||||
==============
|
||||
Defines the Persona Archetypes (the rows of the lab matrix).
|
||||
Each persona defines the scale multiplier and the primary engineering constraint
|
||||
for a specific real-world deployment tier.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict
|
||||
from ..core.constants import ureg, Q_
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Persona:
|
||||
"""
|
||||
A Persona defining scaling factors and narrative focus for a simulation.
|
||||
|
||||
Attributes:
|
||||
name: The display name of the persona (e.g., 'Cloud Titan').
|
||||
role: The job title/role (e.g., 'LLM Infrastructure Lead').
|
||||
description: A brief narrative overview of the persona's goal.
|
||||
scale_factor: The multiplier for device/node count in the simulation.
|
||||
primary_constraint: The 'Critical Wall' this persona must manage.
|
||||
unit_of_scale: The noun used for scaling (e.g., 'Fleet', 'Billion Devices').
|
||||
"""
|
||||
name: str
|
||||
role: str
|
||||
description: str
|
||||
scale_factor: float
|
||||
primary_constraint: str
|
||||
unit_of_scale: str
|
||||
|
||||
class Personas:
|
||||
"""The four canonical personas for the MLSys curriculum."""
|
||||
|
||||
# --- CLOUD TITAN ---
|
||||
CloudTitan = Persona(
|
||||
name="Cloud Titan",
|
||||
role="LLM Infrastructure Lead",
|
||||
description="Responsible for utility-scale training and serving clusters.",
|
||||
scale_factor=1.0,
|
||||
primary_constraint="Total Cost of Ownership (TCO) & Grid Stability",
|
||||
unit_of_scale="Cluster"
|
||||
)
|
||||
|
||||
# --- EDGE GUARDIAN ---
|
||||
EdgeGuardian = Persona(
|
||||
name="Edge Guardian",
|
||||
role="Autonomous Systems Lead",
|
||||
description="Manages safety-critical real-time vehicle fleets.",
|
||||
scale_factor=10_000.0,
|
||||
primary_constraint="Latency Determinism & Safety",
|
||||
unit_of_scale="Fleet"
|
||||
)
|
||||
|
||||
# --- MOBILE NOMAD ---
|
||||
MobileNomad = Persona(
|
||||
name="Mobile Nomad",
|
||||
role="Smartphone App Architect",
|
||||
description="Optimizes global inference for consumer-scale applications.",
|
||||
scale_factor=100_000_000.0,
|
||||
primary_constraint="Battery Life & UX Responsiveness",
|
||||
unit_of_scale="Global User Base"
|
||||
)
|
||||
|
||||
# --- TINY PIONEER ---
|
||||
TinyPioneer = Persona(
|
||||
name="Tiny Pioneer",
|
||||
role="Smart Doorbell Product Lead",
|
||||
description="Scales always-on sensing to billions of sub-milliwatt devices.",
|
||||
scale_factor=10_000_000.0,
|
||||
primary_constraint="Embodied Carbon & SRAM Limits",
|
||||
unit_of_scale="Installed Base"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get(cls, key: str) -> Persona:
|
||||
"""Fetch a persona by its identifier key (cloud, edge, mobile, tiny).
|
||||
|
||||
Args:
|
||||
key: Persona identifier.
|
||||
|
||||
Returns:
|
||||
The corresponding Persona object. Defaults to CloudTitan.
|
||||
"""
|
||||
lookup = {
|
||||
"cloud": cls.CloudTitan,
|
||||
"edge": cls.EdgeGuardian,
|
||||
"mobile": cls.MobileNomad,
|
||||
"tiny": cls.TinyPioneer
|
||||
}
|
||||
return lookup.get(key.lower(), cls.CloudTitan)
|
||||
141
mlsysim/sim/simulations.py
Normal file
141
mlsysim/sim/simulations.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# simulations.py
|
||||
"""
|
||||
MLSys Analytical Simulations
|
||||
============================
|
||||
This module provides domain-specific analytical solvers for lab simulations.
|
||||
Each simulation class implements the 'Physics' of a specific engineering domain.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Any, List, Union, Optional
|
||||
from ..core.constants import ureg, Q_, HOURS_PER_DAY
|
||||
from .ledger import SystemLedger, PerformanceMetrics, SustainabilityMetrics, EconomicMetrics, ReliabilityMetrics
|
||||
from .personas import Persona, Personas
|
||||
from ..core.scenarios import ApplicationScenario, ClusterScenario
|
||||
from ..core.engine import Engine
|
||||
from ..core.systems import SystemArchetype, Systems
|
||||
from ..core.datacenters import Datacenters
|
||||
|
||||
@dataclass
|
||||
class SimulationResult:
|
||||
"""The complete outcome of a simulation evaluation, including raw data for plots."""
|
||||
ledger: SystemLedger
|
||||
plots: Dict[str, Any]
|
||||
|
||||
class BaseSimulation:
|
||||
"""
|
||||
Abstract Base Class for all Analytical Simulations.
|
||||
Provides the standard 'evaluate' interface for student choice processing.
|
||||
"""
|
||||
def __init__(self, scenario: Union[ApplicationScenario, ClusterScenario], persona: Persona):
|
||||
"""Initializes the simulation with a static scenario and a persona.
|
||||
|
||||
Args:
|
||||
scenario: The base model + hardware setup.
|
||||
persona: The scaling and narrative context.
|
||||
"""
|
||||
self.scenario = scenario
|
||||
self.persona = persona
|
||||
|
||||
def _get_system_archetype(self) -> SystemArchetype:
|
||||
"""Helper to unify Application and Cluster scenarios for the Engine.
|
||||
|
||||
Returns:
|
||||
A SystemArchetype object compatible with Engine.solve().
|
||||
"""
|
||||
if hasattr(self.scenario, "system"):
|
||||
return self.scenario.system
|
||||
|
||||
if hasattr(self.scenario, "cluster"):
|
||||
cluster = self.scenario.cluster
|
||||
return SystemArchetype(
|
||||
name=f"Virtual Node ({cluster.node.name})",
|
||||
hardware=cluster.node.accelerator,
|
||||
tier=Systems.Cloud.tier,
|
||||
network_bw=cluster.fabric.bandwidth,
|
||||
power_budget=cluster.node.node_tdp or Q_("700 watt")
|
||||
)
|
||||
|
||||
return Systems.Cloud
|
||||
|
||||
def evaluate(self, choice: Dict[str, Any]) -> SystemLedger:
|
||||
"""Processes a student's choice and returns a Ledger.
|
||||
|
||||
Args:
|
||||
choice: A dictionary of parameters from the UI (e.g., {'region': 'Quebec'}).
|
||||
|
||||
Returns:
|
||||
A SystemLedger containing the multi-dimensional results.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# RESOURCE SIMULATION: Sustainability, Carbon, and TCO
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class ResourceSimulation(BaseSimulation):
|
||||
"""
|
||||
Analyzes energy consumption, carbon footprint, and economic TCO.
|
||||
|
||||
This simulation handles regional grid math and fleet-wide power scaling.
|
||||
"""
|
||||
def evaluate(self, choice: Dict[str, Any]) -> SystemLedger:
|
||||
# 1. BASE PERFORMANCE
|
||||
system = self._get_system_archetype()
|
||||
perf_base = Engine.solve(self.scenario.model, system)
|
||||
mfu_val = (perf_base.latency_compute / perf_base.latency).to_base_units().m
|
||||
|
||||
# 2. EXTRACT USER CHOICES
|
||||
region_name = choice.get("region", "US_Avg")
|
||||
grid = getattr(Datacenters.Grids, region_name, Datacenters.Grids.US_Avg)
|
||||
duration_days = float(choice.get("duration_days", 365.0))
|
||||
|
||||
# 3. SCALE TO FLEET (Persona Context)
|
||||
scale = self.persona.scale_factor
|
||||
|
||||
# IT Energy (kWh) = Power(W) * Time(h) / 1000
|
||||
it_power_w = (perf_base.energy / perf_base.latency).to(ureg.watt).m
|
||||
total_hours = duration_days * HOURS_PER_DAY
|
||||
it_energy_kwh = (it_power_w * total_hours * scale) / 1000.0
|
||||
|
||||
# 4. APPLY PHYSICAL INVARIANTS (Sustainability)
|
||||
total_energy_kwh = it_energy_kwh * grid.pue
|
||||
total_carbon_kg = grid.carbon_kg(it_energy_kwh)
|
||||
|
||||
# 5. ECONOMIC MATH
|
||||
electricity_cost = total_energy_kwh * 0.12
|
||||
hw_cost_per_unit = 10.0 if system.tier.name == "Tiny" else 30000.0
|
||||
total_capex = hw_cost_per_unit * scale
|
||||
|
||||
# 6. ASSEMBLE UNIVERSAL LEDGER
|
||||
ledger = SystemLedger(
|
||||
mission_name="Global Efficiency Challenge",
|
||||
track_name=self.persona.name,
|
||||
choice_summary=f"Region: {grid.name}, Duration: {duration_days} days",
|
||||
performance=PerformanceMetrics(
|
||||
latency=perf_base.latency,
|
||||
throughput=perf_base.throughput * scale,
|
||||
mfu=mfu_val,
|
||||
hfu=mfu_val * 1.1,
|
||||
bottleneck=perf_base.bottleneck
|
||||
),
|
||||
sustainability=SustainabilityMetrics(
|
||||
energy=total_energy_kwh * ureg.kilowatt_hour,
|
||||
carbon_kg=total_carbon_kg,
|
||||
pue=grid.pue,
|
||||
water_liters=total_energy_kwh * grid.wue
|
||||
),
|
||||
economics=EconomicMetrics(
|
||||
capex=total_capex,
|
||||
opex=electricity_cost,
|
||||
tco=total_capex + electricity_cost,
|
||||
cost_per_million=(electricity_cost / (perf_base.throughput.m * total_hours * scale * 3600)) * 1e6
|
||||
),
|
||||
reliability=ReliabilityMetrics(
|
||||
mttf=Q_("100 hours"),
|
||||
goodput=0.95,
|
||||
recovery_time=Q_("15 minutes")
|
||||
)
|
||||
)
|
||||
ledger.validate()
|
||||
return ledger
|
||||
Reference in New Issue
Block a user