[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "mlsysim" version = "0.1.1" description = "First-principles infrastructure modeling for machine learning systems — predicts performance, cost, and carbon footprint from hardware and workload specifications." readme = "README.md" requires-python = ">=3.10" authors = [ {name = "Vijay Janapa Reddi", email = "vj@eecs.harvard.edu"} ] license = {text = "Apache-2.0"} classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Education", "Intended Audience :: Science/Research", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Education", "License :: OSI Approved :: Apache Software License", ] dependencies = [ "pint>=0.24.4", "pydantic>=2.0.0", # Browser-Pyodide bundles numpy 2.0.2; Node-Pyodide bundles 2.2.5; # neither will upgrade at runtime. mlsysim's actual numpy API surface # (argmin, array, column_stack, full_like, linspace, log, logspace, # meshgrid, minimum) is stable since numpy 1.20 and uses no 2.2-only # behavior, so 2.0 is the correct floor — wider than either Pyodide # bundle while staying on numpy 2.x. "numpy>=2.0", "typer>=0.9.0", "rich>=15.0.0", "pyyaml>=6.0", ] [project.optional-dependencies] cli = [ # CLI deps now in core (required for console_scripts entry point). # This extra is kept for backward compat with `pip install mlsysim[cli]`. ] viz = [ "plotly>=6.7.0", "matplotlib>=3.10.9", ] labs = [ "marimo>=0.23.3", "plotly>=6.7.0", ] opt = [ "scipy>=1.15.3", "ortools>=9.15.6755", ] mcp = [ "mcp", ] full = [ "mlsysim[cli,viz,labs,opt,mcp]", "pandas>=2.0.0", ] docs = [ "nbformat>=5.10.4", "jupyter>=1.1.1", "nbclient>=0.7", "matplotlib>=3.10.9", "numpy>=2.0", "plotly>=6.7.0", ] dev = [ "pytest>=9.0.3", "pytest-cov>=7.1.0", "pyright>=1.1.300", ] [project.scripts] mlsysim = "mlsysim.cli.main:app" [project.urls] Homepage = "https://mlsysbook.ai/mlsysim" Repository = "https://github.com/harvard-edge/cs249r_book" Documentation = "https://mlsysbook.ai/mlsysim" Issues = "https://github.com/harvard-edge/cs249r_book/issues" [tool.hatch.build.targets.wheel] # Standard nested layout: pyproject.toml lives in this dir, package code lives # in the inner mlsysim/ directory. This layout supports `pip install -e .` # (editable installs) without the prefix-add `sources` rewrite that breaks # the editables backend. packages = ["mlsysim"] [tool.hatch.build.targets.sdist] include = [ "mlsysim", "README.md", "LICENSE.md", "CHANGELOG.md", "CITATION.cff", "pyproject.toml", ] [tool.pytest.ini_options] minversion = "7.0" testpaths = ["tests"] python_files = ["test_*.py"] python_functions = ["test_*"] addopts = ["--strict-markers"] [tool.pyright] include = ["mlsysim"] exclude = ["tests", "docs", "examples", "mlsysim/mlsysim/labs"] typeCheckingMode = "basic" reportMissingImports = true [tool.ruff] line-length = 120 target-version = "py310" extend-exclude = [ "build", "dist", "docs/_site", "notebooks", ".venv", "venv", ] [tool.ruff.lint] # Conservative defaults. Stick to E/F (style + pyflakes); leave W (whitespace # nits) to a future formatter pass if the project decides to enforce it. select = ["E", "F"] ignore = [ # Long lines: formatter's responsibility, not the linter's. "E501", # Single-line `if x: do_thing()` is fine for early-returns / guards. "E701", # Module-level imports after `sys.path` munging are common in tests/notebooks. "E402", # Ambiguous variable names (`l`, `I`, `O`) — used intentionally for math symbols. "E741", ] [tool.ruff.lint.per-file-ignores] # Re-export pattern: `__init__.py` files intentionally use `from .submod import *` # to expose a flat public API. The redefinitions and "unused" imports are by design. "**/__init__.py" = ["F401", "F403", "F405", "F811"] # constants.py imports the full unit registry via `from .units import *` and # uses `TFLOPs`, `second`, `GB`, etc. throughout. The star import is by design. "mlsysim/core/constants.py" = ["F403", "F405"] # Tests sometimes shadow imports (parametrize/fixtures) and use bare excepts in # negative-path checks; keep the noise out. "tests/**" = ["F401", "F811", "E722"] # Examples are illustrative scripts — allow unused imports for clarity and the # occasional bare except in demo error-handling. "examples/**" = ["F401", "F541", "E722"]