From 3f50fdbaf12a93285d8e16b52bf5da71d4d759ae Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Fri, 28 Mar 2025 09:37:46 +0000 Subject: [PATCH 01/17] working modal server --- scripts/__init__.py | 0 scripts/requirements.txt | 19 + scripts/run_and_check_modal.py | 306 ++++++++++++++ scripts/server_run_and_check.py | 165 ++++++++ scripts/server_run_and_check_modal.py | 587 ++++++++++++++++++++++++++ setup.py | 2 +- 6 files changed, 1078 insertions(+), 1 deletion(-) create mode 100644 scripts/__init__.py create mode 100644 scripts/requirements.txt create mode 100644 scripts/run_and_check_modal.py create mode 100644 scripts/server_run_and_check.py create mode 100644 scripts/server_run_and_check_modal.py diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 00000000..ab811bcf --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,19 @@ +anthropic +modal +numpy +openai +packaging +pydra_config +torch +--find-links https://download.pytorch.org/whl/nightly/cu128 +torchvision +torchaudio +tqdm +datasets +transformers +google-generativeai +together +pytest +ninja +archon-ai +einops \ No newline at end of file diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py new file mode 100644 index 00000000..9ee34e0c --- /dev/null +++ b/scripts/run_and_check_modal.py @@ -0,0 +1,306 @@ +import torch +import pydra +from pydra import REQUIRED, Config +import os +import shutil +import modal +import numpy as np + +from src import eval as kernel_eval +from src.utils import read_file + +def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> kernel_eval.KernelExecResult: + """Evaluate a single sample source code against a reference source code""" + kernel_hash = str(hash(kernel_src)) + build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) + + if configs["clear_cache"]: + print(f"[INFO] Clearing cache for build directory: {build_dir}") + shutil.rmtree(build_dir, ignore_errors=True) + + try: + eval_result = kernel_eval.eval_kernel_against_ref( + original_model_src=ref_arch_src, + custom_model_src=kernel_src, + measure_performance=configs["measure_performance"], + verbose=configs["verbose"], + num_correct_trials=configs["num_correct_trials"], + num_perf_trials=configs["num_perf_trials"], + build_dir=build_dir, + device=device + ) + return eval_result + except Exception as e: + print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") + if "CUDA error" in str(e): + metadata = {"cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device) + } + else: + metadata = {"other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device) + } + return kernel_eval.KernelExecResult(compiled=False, correctness=False, metadata=metadata) + +""" +Run a pair of (reference, solution) to check if solution is correct and compute speedup using Modal + +Usage: +python3 scripts/run_and_check_modal.py ref_arch_src_path=src/prompts/model_ex_add.py kernel_src_path=src/prompts/model_new_ex_add.py +""" + +torch.set_printoptions(precision=4, threshold=10) +app = modal.App("run_and_check") +gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} + +class ScriptConfig(Config): + def __init__(self): + # Required file paths + self.ref_arch_src_path = REQUIRED # Reference implementation + self.kernel_src_path = REQUIRED # Custom kernel implementation + self.gpu = "L40S" # GPU type for modal + self.num_correct_trials = 5 # Number of trials for correctness + self.num_perf_trials = 100 # Number of trials for performance + self.timeout = 300 # Timeout for each trial + self.verbose = False # Verbose logging + self.measure_performance = True # Whether to measure performance + self.build_dir_prefix = "" # Custom build directory prefix + self.clear_cache = False # Whether to clear build cache + self.gpu_arch = ["Ada"] # Default GPU architecture + + def __repr__(self): + return f"ScriptConfig({self.to_dict()})" + +# Configure Modal image +cuda_version = "12.8.0" +flavor = "devel" +operating_sys = "ubuntu22.04" +tag = f"{cuda_version}-{flavor}-{operating_sys}" + +image = ( + modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") + .apt_install("git", "gcc-10", "g++-10", "clang") + .pip_install_from_requirements("requirements.txt") + .add_local_python_source("_remote_module_non_scriptable", "scripts", "src") +) + +@app.cls(image=image) +class EvalFunc: + @modal.method() + def evaluate_single_sample_src_modal(self, ref_arch_src, kernel_src, configs, gpu_arch): + """Evaluate a single sample source code against a reference source code""" + import torch + from src import utils as kernel_utils + import sys + + kernel_utils.set_gpu_arch(gpu_arch) + device = torch.device("cuda:0") + current_module = sys.modules[__name__] + + eval_result = current_module.evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device + ) + + return { + "compiled": eval_result.compiled, + "correctness": eval_result.correctness, + "runtime": eval_result.runtime, + "metadata": eval_result.metadata + } + + @modal.method() + def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, + use_torch_compile=False, torch_compile_backend=None, + torch_compile_options=None, gpu_arch=None): + """Measure the execution time of a reference program""" + import torch + import numpy as np + import importlib.util + import sys + import os + import tempfile + from src import utils as kernel_utils + + # Setup + if gpu_arch: + kernel_utils.set_gpu_arch(gpu_arch) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + x if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if torch_compile_options is not None and torch_compile_options != "default": + compile_options = {"mode": torch_compile_options} if torch_compile_options in ["max-autotune", "reduce-overhead"] else {} + ref_model = torch.compile(ref_model, backend=torch_compile_backend, options=compile_options) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + x if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple(torch.randn(1, 128, device=device) for _ in range(argcount - 1)) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize() + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize() + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + +@pydra.main(base=ScriptConfig) +def main(config: ScriptConfig): + print("Running with config", config) + + # Read source files + ref_arch_src = read_file(config.ref_arch_src_path) + kernel_src = read_file(config.kernel_src_path) + + # Prepare GPU architecture settings + gpu_arch = gpu_arch_mapping.get(config.gpu, config.gpu_arch) + print(f"[INFO] Using GPU architecture: {gpu_arch}") + + # Start Evaluation + with app.run(): + # Evaluate kernel against reference code + print("[INFO] Evaluating kernel against reference code") + kernel_eval_result_dict = EvalFunc.with_options(gpu=config.gpu)().evaluate_single_sample_src_modal.remote( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=config.to_dict(), + gpu_arch=gpu_arch + ) + + # Convert dict back to KernelExecResult object + kernel_eval_result = kernel_eval.KernelExecResult( + compiled=kernel_eval_result_dict["compiled"], + correctness=kernel_eval_result_dict["correctness"], + runtime=kernel_eval_result_dict["runtime"], + metadata=kernel_eval_result_dict["metadata"] + ) + kernel_exec_time = kernel_eval_result.runtime + + # Measure baseline time for PyTorch Eager + print("[INFO] Measuring reference program time (eager mode)") + ref_time_eager_result = EvalFunc.with_options(gpu=config.gpu)().measure_program_time.remote( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=gpu_arch + ) + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + + # Measure Torch Compile time + print("[INFO] Measuring reference program time (torch.compile)") + ref_time_compile_result = EvalFunc.with_options(gpu=config.gpu)().measure_program_time.remote( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + gpu_arch=gpu_arch + ) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + + # Print results + print("="*40) + print(f"[Eval] Kernel eval result: {kernel_eval_result}") + print("-"*40) + print(f"[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms") + print(f"[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms") + print(f"[Timing] Custom Kernel exec time: {kernel_exec_time} ms") + print("-"*40) + + if kernel_eval_result.correctness: + print(f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x") + print(f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x") + else: + print("[Speedup] Speedup Not Available as Kernel did not pass correctness") + + print("="*40) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/server_run_and_check.py b/scripts/server_run_and_check.py new file mode 100644 index 00000000..9fd575b9 --- /dev/null +++ b/scripts/server_run_and_check.py @@ -0,0 +1,165 @@ +import fastapi +import uvicorn +import tempfile +import os +import shutil +from fastapi import UploadFile, File, HTTPException, status +from pydantic import BaseModel +from typing import Optional, Dict, Any, List + +# Import the relevant modules directly +from scripts.run_and_check import evaluate_single_sample_src +from scripts.generate_baseline_time import measure_program_time +from src.utils import read_file, set_gpu_arch +import torch + +# Define the response model +class BenchmarkResult(BaseModel): + compiled: bool + correctness: bool + ref_exec_eager_time_ms: Optional[float] = None + ref_exec_compile_time_ms: Optional[float] = None + kernel_exec_time_ms: Optional[float] = None + speedup_vs_eager: Optional[float] = None + speedup_vs_compile: Optional[float] = None + metadata: Dict[str, Any] + error: Optional[str] = None + +app = fastapi.FastAPI() + +@app.post("/benchmark", response_model=BenchmarkResult) +async def run_benchmark( + ref_file: UploadFile = File(...), + kernel_file: UploadFile = File(...), + gpu_arch: List[str] = ["Ada"], + num_correct_trials: int = 5, + num_perf_trials: int = 100, + verbose: bool = False +): + # Create temporary files for the uploaded code + with tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as ref_tmp, \ + tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as kernel_tmp: + try: + # Save uploaded file contents to temporary files + shutil.copyfileobj(ref_file.file, ref_tmp) + shutil.copyfileobj(kernel_file.file, kernel_tmp) + + # Ensure files are flushed and closed before script access + ref_path = ref_tmp.name + kernel_path = kernel_tmp.name + finally: + ref_file.file.close() + kernel_file.file.close() + + try: + # Read the contents of the files + ref_arch_src = read_file(ref_path) + kernel_src = read_file(kernel_path) + + # Set up GPU architecture + set_gpu_arch(gpu_arch) + + # Default device + device = torch.device("cuda:0") + + # Prepare configs + configs = { + "num_correct_trials": num_correct_trials, + "num_perf_trials": num_perf_trials, + "verbose": verbose, + "measure_performance": True, + "build_dir_prefix": "server_builds", + "clear_cache": False + } + + # Evaluate kernel against reference + kernel_eval_result = evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device + ) + + # Measure reference times + ref_time_eager_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=False, + device=device + ) + + ref_time_compile_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + device=device + ) + + # Extract values + kernel_exec_time = kernel_eval_result.runtime + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + + # Calculate speedups + speedup_vs_eager = None + speedup_vs_compile = None + + if kernel_eval_result.correctness and kernel_exec_time and ref_exec_eager_time: + speedup_vs_eager = ref_exec_eager_time / kernel_exec_time + + if kernel_eval_result.correctness and kernel_exec_time and ref_exec_compile_time: + speedup_vs_compile = ref_exec_compile_time / kernel_exec_time + + # Prepare output summary + raw_output = f""" +============================== +[Eval] Kernel eval result: {kernel_eval_result} +------------------------------ +[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms +[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms +[Timing] Custom Kernel exec time: {kernel_exec_time} ms +------------------------------ +""" + if kernel_eval_result.correctness: + raw_output += f""" +[Speedup] Speedup over eager: {speedup_vs_eager:.2f}x +[Speedup] Speedup over torch.compile: {speedup_vs_compile:.2f}x +""" + else: + raw_output += "[Speedup] Speedup Not Available as Kernel did not pass correctness" + + raw_output += "==============================" + + # Prepare the response + response = BenchmarkResult( + compiled=kernel_eval_result.compiled, + correctness=kernel_eval_result.correctness, + ref_exec_eager_time_ms=ref_exec_eager_time, + ref_exec_compile_time_ms=ref_exec_compile_time, + kernel_exec_time_ms=kernel_exec_time, + speedup_vs_eager=speedup_vs_eager, + speedup_vs_compile=speedup_vs_compile, + metadata=kernel_eval_result.metadata or {}, + + ) + print(raw_output) + return response + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"An error occurred during benchmarking: {str(e)}" + ) + finally: + # Clean up temporary files + if 'ref_path' in locals() and os.path.exists(ref_path): + os.remove(ref_path) + if 'kernel_path' in locals() and os.path.exists(kernel_path): + os.remove(kernel_path) + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py new file mode 100644 index 00000000..f4dc537d --- /dev/null +++ b/scripts/server_run_and_check_modal.py @@ -0,0 +1,587 @@ +import os +import shutil +import tempfile +from typing import Dict, List, Optional, Any +import sys +import traceback +import importlib.util +import time + +import weave +import torch +import modal +import numpy as np +from fastapi import FastAPI, File, Form, UploadFile, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse + +# Import project-specific modules +# Assuming 'src' is in the Python path +try: + from src import eval as kernel_eval + from src import utils as kernel_utils +except ImportError as e: + print(f"[ERROR] Failed to import project modules at startup: {e}") + # Decide how to handle this - exit, log, or proceed cautiously + kernel_eval = None + kernel_utils = None + +# Create Modal app +app = modal.App("kernel-benchmark-server") # Still here + +# GPU architecture mapping +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"] +} + +# Configure Modal image +cuda_version = "12.8.0" +flavor = "devel" +operating_sys = "ubuntu22.04" +tag = f"{cuda_version}-{flavor}-{operating_sys}" + +image = ( + modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") + .apt_install("git", "gcc-10", "g++-10", "clang") + .pip_install( + "fastapi==0.115.0", + "uvicorn==0.27.1", + "python-multipart==0.0.9", + "pydantic==2.6.1", + "aiofiles==23.2.1", # For serving static files + "weave" + ) + .pip_install_from_requirements("requirements.txt") + # Add source directories + .add_local_python_source("scripts", "src") + .add_local_dir("static", "/root/static") +) + +# Define response models +class KernelExecResult(BaseModel): + compiled: bool + correctness: bool + runtime: Optional[float] = None + metadata: Dict[str, Any] = {} + +class BenchmarkResult(BaseModel): + kernel_result: KernelExecResult + ref_exec_eager_time_ms: Optional[float] = None + ref_exec_compile_time_ms: Optional[float] = None + kernel_exec_time_ms: Optional[float] = None + speedup_vs_eager: Optional[float] = None + speedup_vs_compile: Optional[float] = None + compile_time_ms: Optional[float] = None + total_benchmark_time_ms: Optional[float] = None + error: Optional[str] = None + +@app.cls(image=image, gpu="L40S", scaledown_window=300, secrets=[modal.Secret.from_name("wandb-api-key")]) +class BenchmarkService: + def __init__(self): + pass + + @weave.op() + def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: + """Evaluate a single sample source code against a reference source code""" + # Check if kernel_eval was imported successfully + if kernel_eval is None: + print("[ERROR] src.eval module not available.") + return KernelExecResult( + compiled=False, + correctness=False, + metadata={"import_error": "Failed to import src.eval at startup"} + ) + + try: + print(f"[DEBUG] Python paths: {sys.path}") + + kernel_hash = str(hash(kernel_src)) + build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) + + if configs["clear_cache"]: + print(f"[INFO] Clearing cache for build directory: {build_dir}") + shutil.rmtree(build_dir, ignore_errors=True) + + try: + eval_result = kernel_eval.eval_kernel_against_ref( + original_model_src=ref_arch_src, + custom_model_src=kernel_src, + measure_performance=configs["measure_performance"], + verbose=configs["verbose"], + num_correct_trials=configs["num_correct_trials"], + num_perf_trials=configs["num_perf_trials"], + build_dir=build_dir, + device=device + ) + return KernelExecResult( + compiled=eval_result.compiled, + correctness=eval_result.correctness, + runtime=eval_result.runtime, + metadata=eval_result.metadata or {} + ) + except Exception as e: + print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") + if "CUDA error" in str(e): + metadata = {"cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device) + } + else: + metadata = {"other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device) + } + return KernelExecResult(compiled=False, correctness=False, metadata=metadata) + except ImportError as e: # This catch might be less likely now, but keep for safety + print(f"[ERROR] Import error during evaluation (unexpected): {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return KernelExecResult( + compiled=False, + correctness=False, + metadata={"import_error": f"Unexpected import error during eval: {str(e)}"} + ) + except Exception as e: + print(f"[ERROR] Unexpected error during evaluation: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return KernelExecResult( + compiled=False, + correctness=False, + metadata={"unexpected_error": str(e)} + ) + + @weave.op() + def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, + use_torch_compile=False, torch_compile_backend=None, + torch_compile_options=None, gpu_arch=None): + """Measure the execution time of a reference program""" + # Removed imports: torch, numpy, importlib.util, sys, os, tempfile, src.utils + + # Check if kernel_utils was imported successfully + if kernel_utils is None: + print("[ERROR] src.utils module not available.") + # Return an error structure or raise an exception + return { + "error": "src.utils module not available", + "mean": None, "std": None, "min": None, "max": None, "median": None + } + + # Setup + if gpu_arch: + kernel_utils.set_gpu_arch(gpu_arch) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + x if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if torch_compile_options is not None and torch_compile_options != "default": + compile_options = {"mode": torch_compile_options} if torch_compile_options in ["max-autotune", "reduce-overhead"] else {} + ref_model = torch.compile(ref_model, backend=torch_compile_backend, options=compile_options) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + x if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple(torch.randn(1, 128, device=device) for _ in range(argcount - 1)) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize() + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize() + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + + @modal.method() + def run_benchmark(self, ref_arch_src: str, kernel_src: str, + gpu_type: str = "L40S", + num_correct_trials: int = 5, + num_perf_trials: int = 100, + verbose: bool = False): + """Run a complete benchmark of kernel vs reference implementation""" + print(f"[DEBUG] Starting benchmark on GPU: {gpu_type}") + + import time + start_time = time.time() + + # Check if kernel_utils was imported successfully + if kernel_utils is None: + print("[ERROR] src.utils module not available.") + return BenchmarkResult( + kernel_result=KernelExecResult(compiled=False, correctness=False), + error="src.utils module not available" + ) + + try: + # Get GPU architecture + gpu_arch = gpu_arch_mapping.get(gpu_type, ["Ada"]) + print(f"[DEBUG] Using GPU architecture: {gpu_arch}") + + # Removed from src import utils as kernel_utils + kernel_utils.set_gpu_arch(gpu_arch) + + # Default device + device = torch.device("cuda:0") + print(f"[DEBUG] Using device: {device}") + + # Check CUDA availability + if torch.cuda.is_available(): + print(f"[DEBUG] CUDA is available. Device count: {torch.cuda.device_count()}") + print(f"[DEBUG] Current device: {torch.cuda.current_device()}") + print(f"[DEBUG] Device name: {torch.cuda.get_device_name(device)}") + else: + print(f"[WARNING] CUDA is not available. Using CPU.") + + # Config dictionary + configs = { + "num_correct_trials": num_correct_trials, + "num_perf_trials": num_perf_trials, + "verbose": verbose, + "measure_performance": True, + "build_dir_prefix": "api_builds", + "clear_cache": False + } + print(f"[DEBUG] Using configs: {configs}") + + try: + # Time the compilation specifically + compile_start_time = time.time() + kernel_result = self.evaluate_single_sample_src( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + configs=configs, + device=device + ) + compile_time = (time.time() - compile_start_time) * 1000 # Convert to ms + + # Evaluate kernel + print(f"[DEBUG] Evaluating kernel against reference...") + kernel_exec_time = kernel_result.runtime + print(f"[DEBUG] Kernel execution time: {kernel_exec_time} ms") + + # Measure baseline time for PyTorch Eager + print(f"[DEBUG] Measuring PyTorch Eager execution time...") + ref_time_eager_result = self.measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=gpu_arch + ) + ref_exec_eager_time = ref_time_eager_result.get("mean", None) + print(f"[DEBUG] PyTorch Eager execution time: {ref_exec_eager_time} ms") + + # Measure Torch Compile time + print(f"[DEBUG] Measuring PyTorch Compiled execution time...") + ref_time_compile_result = self.measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + gpu_arch=gpu_arch + ) + ref_exec_compile_time = ref_time_compile_result.get("mean", None) + print(f"[DEBUG] PyTorch Compiled execution time: {ref_exec_compile_time} ms") + + # Calculate speedups + speedup_vs_eager = None + speedup_vs_compile = None + + if kernel_result.correctness and kernel_exec_time and ref_exec_eager_time: + speedup_vs_eager = ref_exec_eager_time / kernel_exec_time + print(f"[DEBUG] Speedup vs Eager: {speedup_vs_eager}x") + + if kernel_result.correctness and kernel_exec_time and ref_exec_compile_time: + speedup_vs_compile = ref_exec_compile_time / kernel_exec_time + print(f"[DEBUG] Speedup vs Compiled: {speedup_vs_compile}x") + + # Round all float values to 2 decimal places + if ref_exec_eager_time: + ref_exec_eager_time = round(ref_exec_eager_time, 2) + if ref_exec_compile_time: + ref_exec_compile_time = round(ref_exec_compile_time, 2) + if kernel_exec_time: + kernel_exec_time = round(kernel_exec_time, 2) + if speedup_vs_eager: + speedup_vs_eager = round(speedup_vs_eager, 2) + if speedup_vs_compile: + speedup_vs_compile = round(speedup_vs_compile, 2) + + # Calculate total benchmark time + total_time = round((time.time() - start_time) * 1000, 2) # Convert to ms and round + compile_time = round(compile_time, 2) + + # Build response + print(f"[DEBUG] Building response...") + return BenchmarkResult( + kernel_result=kernel_result, + ref_exec_eager_time_ms=ref_exec_eager_time, + ref_exec_compile_time_ms=ref_exec_compile_time, + kernel_exec_time_ms=kernel_exec_time, + speedup_vs_eager=speedup_vs_eager, + speedup_vs_compile=speedup_vs_compile, + compile_time_ms=compile_time, + total_benchmark_time_ms=total_time + ) + except Exception as e: + print(f"[ERROR] Error during benchmark execution: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return BenchmarkResult( + kernel_result=KernelExecResult(compiled=False, correctness=False), + error=f"Benchmark execution error: {str(e)}" + ) + except Exception as e: + print(f"[ERROR] Fatal error in run_benchmark: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + return BenchmarkResult( + kernel_result=KernelExecResult(compiled=False, correctness=False), + error=str(e) + ) + + @modal.asgi_app() + def fastapi_app(self): + web_app = FastAPI(title="KernelBench Benchmarking API") + + # Add CORS middleware + web_app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Determine if we're running locally or in Modal + static_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static") + modal_static_dir = "/root/static" + + # Check both possible locations for static files + if os.path.exists(static_dir): + # Mount static files directory (local development) + web_app.mount("/static", StaticFiles(directory=static_dir), name="static") + + @web_app.get("/") + async def root(): + return FileResponse(os.path.join(static_dir, "index.html")) + elif os.path.exists(modal_static_dir): + # Mount static files directory (Modal environment) + web_app.mount("/static", StaticFiles(directory=modal_static_dir), name="static") + + @web_app.get("/") + async def root(): + return FileResponse(os.path.join(modal_static_dir, "index.html")) + else: + # Fallback for when static directory isn't available + @web_app.get("/") + async def root(): + return { + "name": "KernelBench Benchmarking API", + "version": "1.0.0", + "description": "API for benchmarking CUDA kernels against PyTorch reference implementations", + "endpoints": { + "/benchmark": "POST endpoint for benchmarking kernels", + "/status": "GET endpoint for checking server status" + } + } + + @web_app.post("/benchmark", response_model=BenchmarkResult) + async def benchmark_endpoint( + ref_file: UploadFile = File(...), + kernel_file: UploadFile = File(...), + gpu_type: str = Form("L40S"), + num_correct_trials: int = Form(5), + num_perf_trials: int = Form(100), + verbose: bool = Form(False) + ): + weave.init("gpu-server-modal") + try: + print(f"[DEBUG] Received benchmark request with GPU: {gpu_type}, trials: {num_correct_trials}/{num_perf_trials}") + + # Validate GPU type + if gpu_type not in gpu_arch_mapping: + raise HTTPException(status_code=400, detail=f"Invalid GPU type. Must be one of: {list(gpu_arch_mapping.keys())}") + + # Read file contents + try: + ref_content = await ref_file.read() + print(f"[DEBUG] Read reference file: {len(ref_content)} bytes") + kernel_content = await kernel_file.read() + print(f"[DEBUG] Read kernel file: {len(kernel_content)} bytes") + + ref_arch_src = ref_content.decode("utf-8") + kernel_src = kernel_content.decode("utf-8") + except Exception as e: + print(f"[ERROR] Failed to read uploaded files: {str(e)}") + raise HTTPException(status_code=400, detail=f"Failed to read uploaded files: {str(e)}") + + # Run the benchmark with the specified GPU type + try: + print(f"[DEBUG] Calling run_benchmark method") + result = self.run_benchmark.remote( + ref_arch_src=ref_arch_src, + kernel_src=kernel_src, + gpu_type=gpu_type, + num_correct_trials=num_correct_trials, + num_perf_trials=num_perf_trials, + verbose=verbose + ) + print(f"[DEBUG] Benchmark completed successfully") + return result + except Exception as e: + print(f"[ERROR] Benchmark execution failed: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + raise HTTPException(status_code=500, detail=f"Benchmark execution failed: {str(e)}") + except Exception as e: + print(f"[ERROR] Unexpected error in benchmark endpoint: {str(e)}") + print(f"[ERROR] Traceback: {traceback.format_exc()}") + raise HTTPException(status_code=500, detail=f"Benchmark failed: {str(e)}") + finally: + weave.finish() + + @web_app.get("/status") + async def status(): + return { + "status": "online", + "gpu_types": list(gpu_arch_mapping.keys()) + } + + @web_app.get("/test_imports") + async def test_imports(): + """Test endpoint to check if we can import the necessary modules""" + result = { + "python_version": sys.version, + "sys_path": sys.path, + "env_vars": dict(os.environ), + "imports": {} + } + + # Check modules that should have been imported at the top + try: + # Verify torch import + if 'torch' in sys.modules: + result["imports"]["torch"] = { + "version": torch.__version__, + "cuda_available": torch.cuda.is_available(), + "cuda_version": torch.version.cuda if hasattr(torch.version, "cuda") else None + } + else: + result["imports"]["torch"] = {"error": "torch module not loaded"} + except Exception as e: + result["imports"]["torch"] = {"error": f"Error checking torch: {str(e)}"} + + # Verify src.eval import + if kernel_eval is not None: + result["imports"]["src.eval"] = {"success": True} + else: + result["imports"]["src.eval"] = {"error": "src.eval module failed to load at startup"} + + # Verify src.utils import + if kernel_utils is not None: + result["imports"]["src.utils"] = {"success": True} + else: + result["imports"]["src.utils"] = {"error": "src.utils module failed to load at startup"} + + # Check for file existence + result["files"] = { + "static_local": os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static")), + "static_modal": os.path.exists("/root/static"), + "requirements_txt": os.path.exists("requirements.txt") + } + + return result + + return web_app + +def main(): + # For local development, you can use: + # modal serve scripts.server_run_and_check_modal + print("Starting KernelBench API server...") + print("Use 'modal serve scripts.server_run_and_check_modal' to start the development server") + print("Use 'modal deploy scripts.server_run_and_check_modal' to deploy to production") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/setup.py b/setup.py index 220b79d1..510b811a 100644 --- a/setup.py +++ b/setup.py @@ -4,5 +4,5 @@ setup( name="src", version="0.0.1", - packages=["src"], + packages=["src", "scripts"], ) From c9ffb801d85ca2864cb2c1c5de93d4cd25c77f4e Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Fri, 28 Mar 2025 11:03:03 +0000 Subject: [PATCH 02/17] GPU as global --- scripts/server_run_and_check_modal.py | 54 +++++++++++---------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py index f4dc537d..a0ae02e5 100644 --- a/scripts/server_run_and_check_modal.py +++ b/scripts/server_run_and_check_modal.py @@ -7,7 +7,6 @@ import importlib.util import time -import weave import torch import modal import numpy as np @@ -28,8 +27,7 @@ kernel_eval = None kernel_utils = None -# Create Modal app -app = modal.App("kernel-benchmark-server") # Still here + # GPU architecture mapping gpu_arch_mapping = { @@ -41,8 +39,11 @@ "A10G": ["Ampere"] } +GPU = "L40S" +SCALEDOWN_WINDOW = 300 + # Configure Modal image -cuda_version = "12.8.0" +cuda_version = "12.4.0" flavor = "devel" operating_sys = "ubuntu22.04" tag = f"{cuda_version}-{flavor}-{operating_sys}" @@ -51,12 +52,11 @@ modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") .apt_install("git", "gcc-10", "g++-10", "clang") .pip_install( - "fastapi==0.115.0", - "uvicorn==0.27.1", - "python-multipart==0.0.9", - "pydantic==2.6.1", - "aiofiles==23.2.1", # For serving static files - "weave" + "fastapi", + "uvicorn", + "python-multipart", + "pydantic", + "aiofiles", # For serving static files ) .pip_install_from_requirements("requirements.txt") # Add source directories @@ -64,6 +64,9 @@ .add_local_dir("static", "/root/static") ) +# Create Modal app +app = modal.App("kernel-benchmark-server", image=image) # Still here + # Define response models class KernelExecResult(BaseModel): compiled: bool @@ -82,12 +85,9 @@ class BenchmarkResult(BaseModel): total_benchmark_time_ms: Optional[float] = None error: Optional[str] = None -@app.cls(image=image, gpu="L40S", scaledown_window=300, secrets=[modal.Secret.from_name("wandb-api-key")]) +@app.cls(gpu=GPU, scaledown_window=SCALEDOWN_WINDOW, secrets=[modal.Secret.from_name("wandb-api-key")]) class BenchmarkService: - def __init__(self): - pass - - @weave.op() + def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: """Evaluate a single sample source code against a reference source code""" # Check if kernel_eval was imported successfully @@ -156,7 +156,6 @@ def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs metadata={"unexpected_error": str(e)} ) - @weave.op() def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, use_torch_compile=False, torch_compile_backend=None, torch_compile_options=None, gpu_arch=None): @@ -274,14 +273,12 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, @modal.method() def run_benchmark(self, ref_arch_src: str, kernel_src: str, - gpu_type: str = "L40S", num_correct_trials: int = 5, num_perf_trials: int = 100, verbose: bool = False): """Run a complete benchmark of kernel vs reference implementation""" - print(f"[DEBUG] Starting benchmark on GPU: {gpu_type}") + print(f"[DEBUG] Starting benchmark on GPU: {GPU}") - import time start_time = time.time() # Check if kernel_utils was imported successfully @@ -294,10 +291,10 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, try: # Get GPU architecture - gpu_arch = gpu_arch_mapping.get(gpu_type, ["Ada"]) + gpu_arch = gpu_arch_mapping.get(GPU, ["Ada"]) print(f"[DEBUG] Using GPU architecture: {gpu_arch}") - # Removed from src import utils as kernel_utils + # Set GPU architecture kernel_utils.set_gpu_arch(gpu_arch) # Default device @@ -472,18 +469,12 @@ async def root(): async def benchmark_endpoint( ref_file: UploadFile = File(...), kernel_file: UploadFile = File(...), - gpu_type: str = Form("L40S"), num_correct_trials: int = Form(5), num_perf_trials: int = Form(100), verbose: bool = Form(False) ): - weave.init("gpu-server-modal") try: - print(f"[DEBUG] Received benchmark request with GPU: {gpu_type}, trials: {num_correct_trials}/{num_perf_trials}") - - # Validate GPU type - if gpu_type not in gpu_arch_mapping: - raise HTTPException(status_code=400, detail=f"Invalid GPU type. Must be one of: {list(gpu_arch_mapping.keys())}") + print(f"[DEBUG] Received benchmark request for GPU: {GPU}, trials: {num_correct_trials}/{num_perf_trials}") # Read file contents try: @@ -498,13 +489,12 @@ async def benchmark_endpoint( print(f"[ERROR] Failed to read uploaded files: {str(e)}") raise HTTPException(status_code=400, detail=f"Failed to read uploaded files: {str(e)}") - # Run the benchmark with the specified GPU type + # Run the benchmark try: print(f"[DEBUG] Calling run_benchmark method") result = self.run_benchmark.remote( ref_arch_src=ref_arch_src, kernel_src=kernel_src, - gpu_type=gpu_type, num_correct_trials=num_correct_trials, num_perf_trials=num_perf_trials, verbose=verbose @@ -519,14 +509,12 @@ async def benchmark_endpoint( print(f"[ERROR] Unexpected error in benchmark endpoint: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") raise HTTPException(status_code=500, detail=f"Benchmark failed: {str(e)}") - finally: - weave.finish() @web_app.get("/status") async def status(): return { "status": "online", - "gpu_types": list(gpu_arch_mapping.keys()) + "gpu_type": GPU } @web_app.get("/test_imports") From 3bb0bd47bcbc29cd40a7d07cd141e37ebe8ded02 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Fri, 28 Mar 2025 11:03:20 +0000 Subject: [PATCH 03/17] triton --- scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/requirements.txt b/scripts/requirements.txt index ab811bcf..e2af6b82 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -5,7 +5,7 @@ openai packaging pydra_config torch ---find-links https://download.pytorch.org/whl/nightly/cu128 +triton torchvision torchaudio tqdm From 15e21bd33dc0f9107ad62be0f71827ac004cf0c4 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 12:36:31 +0200 Subject: [PATCH 04/17] quick fixes --- src/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.py b/src/utils.py index bc7366fb..c56a70af 100644 --- a/src/utils.py +++ b/src/utils.py @@ -63,7 +63,7 @@ def is_safe_to_send_to_deepseek(prompt): # print(f"Prompt: {len(prompt)}") # print(f"Prompt length: {len(tokenizer(prompt, verbose=False)['input_ids'])}") - if type(prompt) == str: + if isinstance(prompt, str): return ( len(tokenizer(prompt, verbose=False)["input_ids"]) < TOO_LONG_FOR_DEEPSEEK ) @@ -168,7 +168,7 @@ def query_server( ) # Logic to query the LLM if server_type == "anthropic": - assert type(prompt) == str + assert isinstance(prompt, str), f"The prompt must be a string for Anthropic, but it was a {type(prompt)}" if is_reasoning_model: # Use beta endpoint with thinking enabled for reasoning models @@ -325,7 +325,7 @@ def query_server( outputs = [choice.message.content for choice in response.choices] # for all other kinds of servers, use standard API else: - if type(prompt) == str: + if isinstance(prompt, str): response = client.completions.create( model=model, prompt=prompt, From 2c4a366e16b0083ea40480f8b0fd03c20b4981f1 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 12:37:17 +0200 Subject: [PATCH 05/17] organize prompt constructor better, add weave --- src/prompt_constructor.py | 98 ++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/src/prompt_constructor.py b/src/prompt_constructor.py index 36cde19f..d85480fb 100644 --- a/src/prompt_constructor.py +++ b/src/prompt_constructor.py @@ -1,4 +1,5 @@ import os +import weave from .utils import read_file @@ -42,7 +43,7 @@ def get_arch_definition(arch_src): Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Just output the new model code, no other text, and NO testing code! \n """ - +@weave.op def prompt_generate_custom_cuda( arc_src: str, example_arch_src: str, example_new_arch_src: str ) -> str: @@ -76,6 +77,7 @@ def prompt_generate_custom_cuda( Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Just output the new model code, no other text, and NO testing code! \n """ +@weave.op def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: list) -> str: """ Generate a prompt with specified few-shot examples following a template @@ -179,6 +181,7 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l prompt += PROBLEM_INSTRUCTION_CLEANED return prompt +@weave.op def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> str: """ Generate a prompt with a CoT example following a template @@ -379,7 +382,7 @@ def prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src: str, g ) - +@weave.op def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, gpu_name: str, example_arch_src: str, @@ -406,53 +409,72 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, assert gpu_name in GPU_SPEC_INFO, f"GPU name {gpu_name} not found in GPU_SPEC_INFO" - prompt = PROBLEM_STATEMENT - - if example_arch_src != "" and example_new_arch_src != "": - prompt += f""" - Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is: \n - ``` \n - {example_arch_src} - ``` \n - The example new arch with custom CUDA kernels looks like this: - ``` - {example_new_arch_src} - ``` \n - """ - + # Get GPU-specific information curr_gpu_spec_info = GPU_SPEC_INFO[gpu_name] - gpu_architecture = curr_gpu_spec_info.get("GPU Architecture") - prompt += f""" - Here is some information about the underlying hardware that you should keep in mind. \n\n -The GPU that will run the kernel is NVIDIA {gpu_name}, {gpu_architecture} architecture.\n\n""" + # Create the title and objective section + objective_section = """# CUDA Kernel Optimization Task + +## Objective +Your task is to optimize PyTorch models by replacing standard PyTorch operators with custom CUDA kernels. You should: +- Choose which operators to replace with custom implementations +- Consider operator fusion opportunities (e.g., combining matmul+relu) +- Explore algorithmic optimizations (e.g., online softmax) +- Rename your optimized implementation as "ModelNew" +""" + + # Create hardware specifications section + hardware_section = f"\n## Hardware Specifications (NVIDIA {gpu_name}, {gpu_architecture} architecture)\n" + hardware_specs = [] for key, value in curr_gpu_spec_info.items(): if key == "GPU Architecture": continue - prompt += f"""- We have {value} of {key}.\n""" + hardware_specs.append(f"- {value} of {key}") + hardware_section += "\n".join(hardware_specs) - - prompt += f"""\n\n -Here are some concepts about the GPU architecture that could be helpful: \n\n""" + # Create GPU concepts section + concepts_section = "\n\n## Key GPU Programming Concepts" + concepts = [] for key, value in GPU_DEFINITIONS.items(): - prompt += f"""- {key}: {value}\n""" - - prompt += f"""\n\n -Here are some best practices for writing CUDA kernels on GPU: \n\n""" + concepts.append(f"- {key}: {value}") + concepts_section += "\n" + "\n".join(concepts) + + # Create best practices section + practices_section = "\n\n## Best Practices" + practices = [] for best_practice in GPU_BEST_PRACTICES: - prompt += f"""- {best_practice}\n""" - + practices.append(f"- {best_practice}") + practices_section += "\n" + "\n".join(practices) + + # Create examples section if provided + examples_section = "" + if example_arch_src and example_new_arch_src: + examples_section = f""" +## Example: Original Model +```python +{example_arch_src} +``` - prompt += f""" - You are given the following architecture: \n - ``` - {ref_arch_src} - ``` - """ +## Example: Optimized Model with Custom CUDA +```python +{example_new_arch_src} +``` +""" + # Create task section + task_section = f""" +## Your Task: Optimize This Model +```python +{ref_arch_src} +``` - prompt += PROBLEM_INSTRUCTION +Implement an optimized version called "ModelNew" with custom CUDA operators. +""" + + # Combine all sections into the final prompt + prompt = objective_section + hardware_section + concepts_section + practices_section + examples_section + task_section + return prompt @@ -502,6 +524,7 @@ def prompt_fix_correctness(ref_arch_src, custom_cuda, metadata): """ return prompt +@weave.op def main(): gpu_name = "L40S" @@ -517,4 +540,5 @@ def main(): f.write(prompt) if __name__ == "__main__": + weave.init("prompt_constructor") main() From 71f183d82ffeb60437193eecde9249c397303f02 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 13:08:08 +0200 Subject: [PATCH 06/17] use UV for install --- .python-version | 1 + pyproject.toml | 33 + requirements.txt | 16 - scripts/run_and_check_modal.py | 2 +- ...quirements.txt => server_requirements.txt} | 0 scripts/server_run_and_check_modal.py | 2 +- setup.py | 8 - uv.lock | 2216 +++++++++++++++++ 8 files changed, 2252 insertions(+), 26 deletions(-) create mode 100644 .python-version create mode 100644 pyproject.toml delete mode 100644 requirements.txt rename scripts/{requirements.txt => server_requirements.txt} (100%) delete mode 100644 setup.py create mode 100644 uv.lock diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..c8cfe395 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..818aa60b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,33 @@ +[project] +name = "kernelbench" +version = "0.1.0" +description = "Benchmarking Cuda/Triton Kernels made easy" +readme = "README.md" +requires-python = "==3.10.*" +dependencies = [ + "anthropic>=0.34.2", + "archon-ai>=0.1.4", + "datasets>=3.5.0", + "einops>=0.8.1", + "google-generativeai>=0.8.4", + "modal>=0.73.136", + "ninja>=1.11.1.4", + "numpy>=2.2.4", + "openai>=1.69.0", + "packaging>=24.2", + "pydra-config>=0.0.14", + "pytest>=8.3.5", + "together>=1.5.4", + "torch==2.5.0", + "tqdm>=4.67.1", + "transformers>=4.50.3", +] + + +[tool.setuptools] +packages = ["src", "scripts"] + +[dependency-groups] +dev = [ + "weave>=0.51.39", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 04381881..00000000 --- a/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -anthropic -modal -numpy -openai -packaging -pydra_config -torch==2.5.0 -tqdm -datasets -transformers -google-generativeai -together -pytest -ninja -archon-ai -einops \ No newline at end of file diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py index 9ee34e0c..3d1d57aa 100644 --- a/scripts/run_and_check_modal.py +++ b/scripts/run_and_check_modal.py @@ -82,7 +82,7 @@ def __repr__(self): image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") .apt_install("git", "gcc-10", "g++-10", "clang") - .pip_install_from_requirements("requirements.txt") + .pip_install_from_requirements("server_requirements.txt") .add_local_python_source("_remote_module_non_scriptable", "scripts", "src") ) diff --git a/scripts/requirements.txt b/scripts/server_requirements.txt similarity index 100% rename from scripts/requirements.txt rename to scripts/server_requirements.txt diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py index a0ae02e5..94cb99bb 100644 --- a/scripts/server_run_and_check_modal.py +++ b/scripts/server_run_and_check_modal.py @@ -58,7 +58,7 @@ "pydantic", "aiofiles", # For serving static files ) - .pip_install_from_requirements("requirements.txt") + .pip_install_from_requirements("server_requirements.txt") # Add source directories .add_local_python_source("scripts", "src") .add_local_dir("static", "/root/static") diff --git a/setup.py b/setup.py deleted file mode 100644 index 510b811a..00000000 --- a/setup.py +++ /dev/null @@ -1,8 +0,0 @@ -from setuptools import setup - -if __name__ == "__main__": - setup( - name="src", - version="0.0.1", - packages=["src", "scripts"], - ) diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..53d54bc7 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2216 @@ +version = 1 +revision = 1 +requires-python = "==3.10.*" +resolution-markers = [ + "sys_platform == 'linux'", + "sys_platform != 'linux'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.11.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/96/91e93ae5fd04d428c101cdbabce6c820d284d61d2614d00518f4fa52ea24/aiohttp-3.11.14.tar.gz", hash = "sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c", size = 7676994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/e1/f1ccc6cf29a31fb33e4eaa07a9d8e4dff00e23b32423b679cdb89536fe71/aiohttp-3.11.14-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d", size = 709390 }, + { url = "https://files.pythonhosted.org/packages/80/7d/195965f183a724d0470560b097543e96dc4a672fc2714012d1be87d6775c/aiohttp-3.11.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa", size = 469246 }, + { url = "https://files.pythonhosted.org/packages/46/02/3a4f05e966c2edeace5103f40d296ba0159cee633ab0f162fbea579653e3/aiohttp-3.11.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f", size = 456384 }, + { url = "https://files.pythonhosted.org/packages/68/a6/c96cd5452af267fdda1cf46accc356d1295fb14da4a7a0e081567ea297af/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0", size = 1589803 }, + { url = "https://files.pythonhosted.org/packages/7f/f4/e50ef78483485bcdae9cf29c9144af2b42457e18175a6ace7c560d89325e/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc", size = 1632525 }, + { url = "https://files.pythonhosted.org/packages/8b/92/b6bd4b89304eee827cf07a40b98af171342cddfa1f8b02b55cd0485b9d4f/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7", size = 1666839 }, + { url = "https://files.pythonhosted.org/packages/c7/21/f3230a9f78bb4a4c4462040bf8425ebb673e3773dd17fd9d06d1af43a955/aiohttp-3.11.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628", size = 1590572 }, + { url = "https://files.pythonhosted.org/packages/8e/12/e4fd2616950a39425b739476c3eccc820061ea5f892815566d27282e7825/aiohttp-3.11.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0", size = 1543380 }, + { url = "https://files.pythonhosted.org/packages/6a/7c/3f82c2fdcca53cc8732fa342abbe0372bbbd8af3162d6629ac0a7dc8b281/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9", size = 1530160 }, + { url = "https://files.pythonhosted.org/packages/aa/3e/60af2d40f78612062788c2bf6be38738f9525750d3a7678d31f950047536/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff", size = 1558543 }, + { url = "https://files.pythonhosted.org/packages/08/71/93e11c4ef9a72f5f26d7e9f92294707437fae8de49c2019ed713dea7625b/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914", size = 1536286 }, + { url = "https://files.pythonhosted.org/packages/da/4b/77b170ae7eb9859d80b9648a7439991425663f66422f3ef0b27f29bde9d0/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d", size = 1608387 }, + { url = "https://files.pythonhosted.org/packages/02/0b/5fcad20243799e9a3f326140d3d767884449e293fb5d8fca10f83001787c/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3", size = 1629633 }, + { url = "https://files.pythonhosted.org/packages/3f/e3/bb454add253f939c7331794b2619c156ef5a108403000221ff2dc01f9072/aiohttp-3.11.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b", size = 1565329 }, + { url = "https://files.pythonhosted.org/packages/6f/08/6b061de352a614461a4a19e60a87e578fe28e1d3fca38315484a17ff484f/aiohttp-3.11.14-cp310-cp310-win32.whl", hash = "sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990", size = 417394 }, + { url = "https://files.pythonhosted.org/packages/91/f7/533384607d35a8c7a9dbe4497cee7899aa7c3b29c14cd83373c0f415bdcf/aiohttp-3.11.14-cp310-cp310-win_amd64.whl", hash = "sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186", size = 442856 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anthropic" +version = "0.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tokenizers" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/a6/10efc0ca36712673a11ac90095bdb84a299cd6f591d5111bfa9acbb2e76e/anthropic-0.34.2.tar.gz", hash = "sha256:808ea19276f26646bfde9ee535669735519376e4eeb301a2974fc69892be1d6e", size = 902318 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/6d/c739c11fb3838cda8d4052d0ab3462b2b7d2499a726a7869e5d3e228cb74/anthropic-0.34.2-py3-none-any.whl", hash = "sha256:f50a628eb71e2c76858b106c8cbea278c45c6bd2077cb3aff716a112abddc9fc", size = 891945 }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, +] + +[[package]] +name = "archon-ai" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "google-generativeai" }, + { name = "groq" }, + { name = "litellm" }, + { name = "loguru" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tiktoken" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/7f084ae2109d7e5eb1e25028f03e609dd9891b43f19ed15d0d237f04da4e/archon_ai-0.1.4.tar.gz", hash = "sha256:d00b160a32bf51b58d4ffeb43562ae03f8e32a3c23c46d9cd821f221a4ae72f4", size = 27745 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/c3/0b989c86b4f408a602993ba5503692784be17ec61a6606c9384ce23a2db1/archon_ai-0.1.4-py3-none-any.whl", hash = "sha256:04942caa758d0fbd4aab6a817a9b272b1c7f6e687ede490d634147c5fb3c352a", size = 32322 }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, + { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, + { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, + { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, + { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, + { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, + { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, + { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, + { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, + { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, + { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, + { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "datasets" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/dd/6afb1d440a21da104adefb93269a02058bf9f3cf665db6421fe04eceab7d/datasets-3.5.0.tar.gz", hash = "sha256:9e39560e34f83a64e48ceca7adeb645ede3c3055c5cf48ed2b454f8ed2b89754", size = 568325 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/83/50abe521eb75744a01efe2ebe836a4b61f4df37941a776f650f291aabdf9/datasets-3.5.0-py3-none-any.whl", hash = "sha256:b3b7f163acc6ac4e01a1b00eef26d48bd4039288ceea3601d169272bd5581006", size = 491167 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "diskcache" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982 }, +] + +[[package]] +name = "einops" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, +] + +[[package]] +name = "emoji" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/7d/01cddcbb6f5cc0ba72e00ddf9b1fa206c802d557fd0a20b18e130edf1336/emoji-2.14.1.tar.gz", hash = "sha256:f8c50043d79a2c1410ebfae833ae1868d5941a67a6cd4d18377e2eb0bd79346b", size = 597182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/db/a0335710caaa6d0aebdaa65ad4df789c15d89b7babd9a30277838a7d9aac/emoji-2.14.1-py3-none-any.whl", hash = "sha256:35a8a486c1460addb1499e3bf7929d3889b2e2841a57401903699fef595e942b", size = 590617 }, +] + +[[package]] +name = "eval-type-backport" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/ea/8b0ac4469d4c347c6a385ff09dc3c048c2d021696664e26c7ee6791631b5/eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1", size = 9079 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/31/55cd413eaccd39125368be33c46de24a1f639f2e12349b0361b4678f3915/eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a", size = 5830 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "fastapi" +version = "0.115.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "frozenlist" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/79/29d44c4af36b2b240725dce566b20f63f9b36ef267aaaa64ee7466f4f2f8/frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", size = 94451 }, + { url = "https://files.pythonhosted.org/packages/47/47/0c999aeace6ead8a44441b4f4173e2261b18219e4ad1fe9a479871ca02fc/frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", size = 54301 }, + { url = "https://files.pythonhosted.org/packages/8d/60/107a38c1e54176d12e06e9d4b5d755b677d71d1219217cee063911b1384f/frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", size = 52213 }, + { url = "https://files.pythonhosted.org/packages/17/62/594a6829ac5679c25755362a9dc93486a8a45241394564309641425d3ff6/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", size = 240946 }, + { url = "https://files.pythonhosted.org/packages/7e/75/6c8419d8f92c80dd0ee3f63bdde2702ce6398b0ac8410ff459f9b6f2f9cb/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", size = 264608 }, + { url = "https://files.pythonhosted.org/packages/88/3e/82a6f0b84bc6fb7e0be240e52863c6d4ab6098cd62e4f5b972cd31e002e8/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", size = 261361 }, + { url = "https://files.pythonhosted.org/packages/fd/85/14e5f9ccac1b64ff2f10c927b3ffdf88772aea875882406f9ba0cec8ad84/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", size = 231649 }, + { url = "https://files.pythonhosted.org/packages/ee/59/928322800306f6529d1852323014ee9008551e9bb027cc38d276cbc0b0e7/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", size = 241853 }, + { url = "https://files.pythonhosted.org/packages/7d/bd/e01fa4f146a6f6c18c5d34cab8abdc4013774a26c4ff851128cd1bd3008e/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", size = 243652 }, + { url = "https://files.pythonhosted.org/packages/a5/bd/e4771fd18a8ec6757033f0fa903e447aecc3fbba54e3630397b61596acf0/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", size = 241734 }, + { url = "https://files.pythonhosted.org/packages/21/13/c83821fa5544af4f60c5d3a65d054af3213c26b14d3f5f48e43e5fb48556/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", size = 260959 }, + { url = "https://files.pythonhosted.org/packages/71/f3/1f91c9a9bf7ed0e8edcf52698d23f3c211d8d00291a53c9f115ceb977ab1/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", size = 262706 }, + { url = "https://files.pythonhosted.org/packages/4c/22/4a256fdf5d9bcb3ae32622c796ee5ff9451b3a13a68cfe3f68e2c95588ce/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", size = 250401 }, + { url = "https://files.pythonhosted.org/packages/af/89/c48ebe1f7991bd2be6d5f4ed202d94960c01b3017a03d6954dd5fa9ea1e8/frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", size = 45498 }, + { url = "https://files.pythonhosted.org/packages/28/2f/cc27d5f43e023d21fe5c19538e08894db3d7e081cbf582ad5ed366c24446/frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", size = 51622 }, + { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, +] + +[[package]] +name = "fsspec" +version = "2024.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/11/de70dee31455c546fbc88301971ec03c328f3d1138cfba14263f651e9551/fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f", size = 291600 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/86/5486b0188d08aa643e127774a99bac51ffa6cf343e3deb0583956dca5b22/fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2", size = 183862 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "google-ai-generativelanguage" +version = "0.6.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "proto-plus" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/d1/48fe5d7a43d278e9f6b5ada810b0a3530bbeac7ed7fcbcd366f932f05316/google_ai_generativelanguage-0.6.15.tar.gz", hash = "sha256:8f6d9dc4c12b065fe2d0289026171acea5183ebf2d0b11cefe12f3821e159ec3", size = 1375443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/a3/67b8a6ff5001a1d8864922f2d6488dc2a14367ceb651bc3f09a947f2f306/google_ai_generativelanguage-0.6.15-py3-none-any.whl", hash = "sha256:5a03ef86377aa184ffef3662ca28f19eeee158733e45d7947982eb953c6ebb6c", size = 1327356 }, +] + +[[package]] +name = "google-api-core" +version = "2.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/5c/085bcb872556934bb119e5e09de54daa07873f6866b8f0303c49e72287f7/google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696", size = 163516 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/95/f472d85adab6e538da2025dfca9e976a0d125cc0af2301f190e77b76e51c/google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9", size = 160061 }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, + { name = "grpcio-status" }, +] + +[[package]] +name = "google-api-python-client" +version = "2.166.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httplib2" }, + { name = "uritemplate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/c9/eac7b4e843039f0a54a563c2328d43de6f02e426a11b6a7e378996f667db/google_api_python_client-2.166.0.tar.gz", hash = "sha256:b8cf843bd9d736c134aef76cf1dc7a47c9283a2ef24267b97207b9dd43b30ef7", size = 12680525 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/44/ae1528a6ca296d89704c8febb72b3e263c28b4e50ab29b9202df7a0f273d/google_api_python_client-2.166.0-py2.py3-none-any.whl", hash = "sha256:dd8cc74d9fc18538ab05cbd2e93cb4f82382f910c5f6945db06c91f1deae6e45", size = 13190078 }, +] + +[[package]] +name = "google-auth" +version = "2.38.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/eb/d504ba1daf190af6b204a9d4714d457462b486043744901a6eeea711f913/google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4", size = 270866 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 }, +] + +[[package]] +name = "google-auth-httplib2" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "httplib2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253 }, +] + +[[package]] +name = "google-generativeai" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-ai-generativelanguage" }, + { name = "google-api-core" }, + { name = "google-api-python-client" }, + { name = "google-auth" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/b0/6c6af327a8a6ef3be6fe79be1d6f1e2914d6c363aa6b081b93396f4460a7/google_generativeai-0.8.4-py3-none-any.whl", hash = "sha256:e987b33ea6decde1e69191ddcaec6ef974458864d243de7191db50c21a7c5b82", size = 175409 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.69.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/d7/ee9d56af4e6dbe958562b5020f46263c8a4628e7952070241fc0e9b182ae/googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f", size = 144496 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/53/d35476d547a286506f0a6a634ccf1e5d288fffd53d48f0bd5fef61d68684/googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212", size = 293215 }, +] + +[[package]] +name = "gql" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "backoff" }, + { name = "graphql-core" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/ef/5298d9d628b6a54b3b810052cb5a935d324fe28d9bfdeb741733d5c2446b/gql-3.5.2.tar.gz", hash = "sha256:07e1325b820c8ba9478e95de27ce9f23250486e7e79113dbb7659a442dc13e74", size = 180502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/71/b028b937992056e721bbf0371e13819fcca0dacde7b3c821f775ed903917/gql-3.5.2-py2.py3-none-any.whl", hash = "sha256:c830ffc38b3997b2a146317b27758305ab3d0da3bde607b49f34e32affb23ba2", size = 74346 }, +] + +[package.optional-dependencies] +aiohttp = [ + { name = "aiohttp" }, +] +requests = [ + { name = "requests" }, + { name = "requests-toolbelt" }, +] + +[[package]] +name = "graphql-core" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/9e/aa527fb09a9d7399d5d7d2aa2da490e4580707652d3b4fc156996ae88a5b/graphql-core-3.2.4.tar.gz", hash = "sha256:acbe2e800980d0e39b4685dd058c2f4042660b89ebca38af83020fd872ff1264", size = 504611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/cc72c4c658c6316f188a60bc4e5a91cd4ceaaa8c3e7e691ac9297e4e72c7/graphql_core-3.2.4-py3-none-any.whl", hash = "sha256:1604f2042edc5f3114f49cac9d77e25863be51b23a54a61a23245cf32f6476f0", size = 203179 }, +] + +[[package]] +name = "groq" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/4d/14b9a0c27695b2aa3bcd7f397a9c6d5aa84165d0bed4e4ca3f08fe59a546/groq-0.11.0.tar.gz", hash = "sha256:dbb9aefedf388ddd4801ec7bf3eba7f5edb67948fec0cd2829d97244059f42a7", size = 104986 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/c3/8bf3cd2987e262f9fd45024313dae1d009edaa7a5b429566edfdaedaa024/groq-0.11.0-py3-none-any.whl", hash = "sha256:e328531c979542e563668c62260aec13b43a6ee0ca9e2fb22dff1d26f8c8ce54", size = 106518 }, +] + +[[package]] +name = "grpcio" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/95/aa11fc09a85d91fbc7dd405dcb2a1e0256989d67bf89fa65ae24b3ba105a/grpcio-1.71.0.tar.gz", hash = "sha256:2b85f7820475ad3edec209d3d89a7909ada16caab05d3f2e08a7e8ae3200a55c", size = 12549828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/c5/ef610b3f988cc0cc67b765f72b8e2db06a1db14e65acb5ae7810a6b7042e/grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd", size = 5210643 }, + { url = "https://files.pythonhosted.org/packages/bf/de/c84293c961622df302c0d5d07ec6e2d4cd3874ea42f602be2df09c4ad44f/grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d", size = 11308962 }, + { url = "https://files.pythonhosted.org/packages/7c/38/04c9e0dc8c904570c80faa1f1349b190b63e45d6b2782ec8567b050efa9d/grpcio-1.71.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:0ab8b2864396663a5b0b0d6d79495657ae85fa37dcb6498a2669d067c65c11ea", size = 5699236 }, + { url = "https://files.pythonhosted.org/packages/95/96/e7be331d1298fa605ea7c9ceafc931490edd3d5b33c4f695f1a0667f3491/grpcio-1.71.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c30f393f9d5ff00a71bb56de4aa75b8fe91b161aeb61d39528db6b768d7eac69", size = 6339767 }, + { url = "https://files.pythonhosted.org/packages/5d/b7/7e7b7bb6bb18baf156fd4f2f5b254150dcdd6cbf0def1ee427a2fb2bfc4d/grpcio-1.71.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f250ff44843d9a0615e350c77f890082102a0318d66a99540f54769c8766ab73", size = 5943028 }, + { url = "https://files.pythonhosted.org/packages/13/aa/5fb756175995aeb47238d706530772d9a7ac8e73bcca1b47dc145d02c95f/grpcio-1.71.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6d8de076528f7c43a2f576bc311799f89d795aa6c9b637377cc2b1616473804", size = 6031841 }, + { url = "https://files.pythonhosted.org/packages/54/93/172783e01eed61f7f180617b7fa4470f504e383e32af2587f664576a7101/grpcio-1.71.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9b91879d6da1605811ebc60d21ab6a7e4bae6c35f6b63a061d61eb818c8168f6", size = 6651039 }, + { url = "https://files.pythonhosted.org/packages/6f/99/62654b220a27ed46d3313252214f4bc66261143dc9b58004085cd0646753/grpcio-1.71.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f71574afdf944e6652203cd1badcda195b2a27d9c83e6d88dc1ce3cfb73b31a5", size = 6198465 }, + { url = "https://files.pythonhosted.org/packages/68/35/96116de833b330abe4412cc94edc68f99ed2fa3e39d8713ff307b3799e81/grpcio-1.71.0-cp310-cp310-win32.whl", hash = "sha256:8997d6785e93308f277884ee6899ba63baafa0dfb4729748200fcc537858a509", size = 3620382 }, + { url = "https://files.pythonhosted.org/packages/b7/09/f32ef637e386f3f2c02effac49699229fa560ce9007682d24e9e212d2eb4/grpcio-1.71.0-cp310-cp310-win_amd64.whl", hash = "sha256:7d6ac9481d9d0d129224f6d5934d5832c4b1cddb96b59e7eba8416868909786a", size = 4280302 }, +] + +[[package]] +name = "grpcio-status" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/53/a911467bece076020456401f55a27415d2d70d3bc2c37af06b44ea41fc5c/grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968", size = 13669 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/d6/31fbc43ff097d8c4c9fc3df741431b8018f67bf8dfbe6553a555f6e5f675/grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68", size = 14424 }, +] + +[[package]] +name = "grpclib" +version = "0.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 } + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "h2" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/38/d7f80fd13e6582fb8e0df8c9a653dcc02b03ca34f4d72f34869298c5baf8/h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f", size = 2150682 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/9e/984486f2d0a0bd2b024bf4bc1c62688fcafa9e61991f041fb0e2def4a982/h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0", size = 60957 }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httplib2" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/b6a403e31a806d3b2560adf73b60881330b954d4000fdfb6b0e3beac5f90/huggingface_hub-0.30.0.tar.gz", hash = "sha256:21ac16bc79c950dc058fad6f25e6e13444d878cc504e386d3a9b83096b3851cb", size = 400791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/54/ab179a3cbc6cb285b34dfb1686176db4c3e7d9634b23cabf5aff0d00c776/huggingface_hub-0.30.0-py3-none-any.whl", hash = "sha256:17470b2b0f902209beb0344d659a18c48b69bc202af27c1cfc814d637807322f", size = 481128 }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/82/39f7c9e67b3b0121f02a0b90d433626caa95a565c3d2449fea6bcfa3f5f5/jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad", size = 314540 }, + { url = "https://files.pythonhosted.org/packages/01/07/7bf6022c5a152fca767cf5c086bb41f7c28f70cf33ad259d023b53c0b858/jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea", size = 321065 }, + { url = "https://files.pythonhosted.org/packages/6c/b2/de3f3446ecba7c48f317568e111cc112613da36c7b29a6de45a1df365556/jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51", size = 341664 }, + { url = "https://files.pythonhosted.org/packages/13/cf/6485a4012af5d407689c91296105fcdb080a3538e0658d2abf679619c72f/jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538", size = 364635 }, + { url = "https://files.pythonhosted.org/packages/0d/f7/4a491c568f005553240b486f8e05c82547340572d5018ef79414b4449327/jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d", size = 406288 }, + { url = "https://files.pythonhosted.org/packages/d3/ca/f4263ecbce7f5e6bded8f52a9f1a66540b270c300b5c9f5353d163f9ac61/jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12", size = 397499 }, + { url = "https://files.pythonhosted.org/packages/ac/a2/522039e522a10bac2f2194f50e183a49a360d5f63ebf46f6d890ef8aa3f9/jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51", size = 352926 }, + { url = "https://files.pythonhosted.org/packages/b1/67/306a5c5abc82f2e32bd47333a1c9799499c1c3a415f8dde19dbf876f00cb/jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708", size = 384506 }, + { url = "https://files.pythonhosted.org/packages/0f/89/c12fe7b65a4fb74f6c0d7b5119576f1f16c79fc2953641f31b288fad8a04/jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5", size = 520621 }, + { url = "https://files.pythonhosted.org/packages/c4/2b/d57900c5c06e6273fbaa76a19efa74dbc6e70c7427ab421bf0095dfe5d4a/jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678", size = 512613 }, + { url = "https://files.pythonhosted.org/packages/89/05/d8b90bfb21e58097d5a4e0224f2940568366f68488a079ae77d4b2653500/jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4", size = 206613 }, + { url = "https://files.pythonhosted.org/packages/2c/1d/5767f23f88e4f885090d74bbd2755518050a63040c0f59aa059947035711/jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322", size = 208371 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "kernelbench" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "anthropic" }, + { name = "archon-ai" }, + { name = "datasets" }, + { name = "einops" }, + { name = "google-generativeai" }, + { name = "modal" }, + { name = "ninja" }, + { name = "numpy" }, + { name = "openai" }, + { name = "packaging" }, + { name = "pydra-config" }, + { name = "pytest" }, + { name = "together" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] + +[package.dev-dependencies] +dev = [ + { name = "weave" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=0.34.2" }, + { name = "archon-ai", specifier = ">=0.1.4" }, + { name = "datasets", specifier = ">=3.5.0" }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "google-generativeai", specifier = ">=0.8.4" }, + { name = "modal", specifier = ">=0.73.136" }, + { name = "ninja", specifier = ">=1.11.1.4" }, + { name = "numpy", specifier = ">=2.2.4" }, + { name = "openai", specifier = ">=1.69.0" }, + { name = "packaging", specifier = ">=24.2" }, + { name = "pydra-config", specifier = ">=0.0.14" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "together", specifier = ">=1.5.4" }, + { name = "torch", specifier = "==2.5.0" }, + { name = "tqdm", specifier = ">=4.67.1" }, + { name = "transformers", specifier = ">=4.50.3" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "weave", specifier = ">=0.51.39" }] + +[[package]] +name = "litellm" +version = "1.65.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/43/57e2efee4fc7e46d2c8e98e221158b0d9720237fd9d1d5dbff69c58c9037/litellm-1.65.0.tar.gz", hash = "sha256:147a74d18601ccaaff3ca125eba914ab6e5b5854aff480dce5a52be5b9d52ff8", size = 6679690 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/48/6985411f5fcb561c5da9ffef95fb71bb31e9ea812bdcb06d451f9c1727f5/litellm-1.65.0-py3-none-any.whl", hash = "sha256:bbc211f3d03e1830ed7f4304b40f70fa1fa4a2f9109d006ede5f78e83a189aba", size = 7012224 }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "modal" +version = "0.73.136" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "certifi" }, + { name = "click" }, + { name = "fastapi" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "synchronicity" }, + { name = "toml" }, + { name = "typer" }, + { name = "types-certifi" }, + { name = "types-toml" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/2f/e5f6d1c1f28a4a449f7625bc58f63f7c4dc01506e6ab71bbb47958abc52b/modal-0.73.136.tar.gz", hash = "sha256:e8a6d3961c11e6440b2ab9a7f344fb1beb9aae8b8511df871ce3b2399f194af0", size = 483697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/e5/b38b0d9f365d685d9f5a0e411c2cceb8439ce44854695eb92fe55d2e8d58/modal-0.73.136-py3-none-any.whl", hash = "sha256:1f812712ea616cce949c06c5a4b45497d1157879775986de54db9ed2023b79e9", size = 551173 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "multidict" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/4a/7874ca44a1c9b23796c767dd94159f6c17e31c0e7d090552a1c623247d82/multidict-6.2.0.tar.gz", hash = "sha256:0085b0afb2446e57050140240a8595846ed64d1cbd26cef936bfab3192c673b8", size = 71066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/ca/3ae4d9c9ba78e7bcb63e3f12974b8fa16b9a20de44e9785f5d291ccb823c/multidict-6.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b9f6392d98c0bd70676ae41474e2eecf4c7150cb419237a41f8f96043fcb81d1", size = 49238 }, + { url = "https://files.pythonhosted.org/packages/25/a4/55e595d2df586e442c85b2610542d1e14def4c6f641761125d35fb38f87c/multidict-6.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3501621d5e86f1a88521ea65d5cad0a0834c77b26f193747615b7c911e5422d2", size = 29748 }, + { url = "https://files.pythonhosted.org/packages/35/6f/09bc361a34bbf953e9897f69823f9c4b46aec0aaed6ec94ce63093ede317/multidict-6.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32ed748ff9ac682eae7859790d3044b50e3076c7d80e17a44239683769ff485e", size = 30026 }, + { url = "https://files.pythonhosted.org/packages/b6/c7/5b51816f7c38049fc50786f46e63c009e6fecd1953fbbafa8bfe4e2eb39d/multidict-6.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc826b9a8176e686b67aa60fd6c6a7047b0461cae5591ea1dc73d28f72332a8a", size = 132393 }, + { url = "https://files.pythonhosted.org/packages/1a/21/c51aca665afa93b397d2c47369f6c267193977611a55a7c9d8683dc095bc/multidict-6.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:214207dcc7a6221d9942f23797fe89144128a71c03632bf713d918db99bd36de", size = 139237 }, + { url = "https://files.pythonhosted.org/packages/2e/9b/a7b91f8ed63314e7a3c276b4ca90ae5d0267a584ca2e42106baa728622d6/multidict-6.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05fefbc3cddc4e36da209a5e49f1094bbece9a581faa7f3589201fd95df40e5d", size = 134920 }, + { url = "https://files.pythonhosted.org/packages/c8/84/4b590a121b1009fe79d1ae5875b4aa9339d37d23e368dd3bcf5e36d27452/multidict-6.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e851e6363d0dbe515d8de81fd544a2c956fdec6f8a049739562286727d4a00c3", size = 129764 }, + { url = "https://files.pythonhosted.org/packages/b8/de/831be406b5ab0dc0d25430ddf597c6ce1a2e23a4991363f1ca48f16fb817/multidict-6.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32c9b4878f48be3e75808ea7e499d6223b1eea6d54c487a66bc10a1871e3dc6a", size = 122121 }, + { url = "https://files.pythonhosted.org/packages/fa/2f/892334f4d3efc7cd11e3a64dc922a85611627380ee2de3d0627ac159a975/multidict-6.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7243c5a6523c5cfeca76e063efa5f6a656d1d74c8b1fc64b2cd1e84e507f7e2a", size = 135640 }, + { url = "https://files.pythonhosted.org/packages/6c/53/bf91c5fdede9406247dcbceaa9d7e7fa08e4d0e27fa3c76a0dab126bc6b2/multidict-6.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0e5a644e50ef9fb87878d4d57907f03a12410d2aa3b93b3acdf90a741df52c49", size = 129655 }, + { url = "https://files.pythonhosted.org/packages/d4/7a/f98e1c5d14c1bbbb83025a69da9a37344f7556c09fef39979cf62b464d60/multidict-6.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0dc25a3293c50744796e87048de5e68996104d86d940bb24bc3ec31df281b191", size = 140691 }, + { url = "https://files.pythonhosted.org/packages/dd/c9/af0ab78b53d5b769bc1fa751e53cc7356cef422bd1cf38ed653985a46ddf/multidict-6.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a49994481b99cd7dedde07f2e7e93b1d86c01c0fca1c32aded18f10695ae17eb", size = 135254 }, + { url = "https://files.pythonhosted.org/packages/c9/53/28cc971b17e25487a089bcf720fe284478f264a6fc619427ddf7145fcb2b/multidict-6.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641cf2e3447c9ecff2f7aa6e9eee9eaa286ea65d57b014543a4911ff2799d08a", size = 133620 }, + { url = "https://files.pythonhosted.org/packages/b6/9a/d7637fbe1d5928b9f6a33ce36c2ff37e0aab9aa22f5fc9552fd75fe7f364/multidict-6.2.0-cp310-cp310-win32.whl", hash = "sha256:0c383d28857f66f5aebe3e91d6cf498da73af75fbd51cedbe1adfb85e90c0460", size = 27044 }, + { url = "https://files.pythonhosted.org/packages/4e/11/04758cc18a51227dbb350a8a25c7db0620d63fb23db5b8d1f87762f05cbe/multidict-6.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:a33273a541f1e1a8219b2a4ed2de355848ecc0254264915b9290c8d2de1c74e1", size = 29149 }, + { url = "https://files.pythonhosted.org/packages/9c/fd/b247aec6add5601956d440488b7f23151d8343747e82c038af37b28d6098/multidict-6.2.0-py3-none-any.whl", hash = "sha256:5d26547423e5e71dcc562c4acdc134b900640a39abd9066d7326a7cc2324c530", size = 10266 }, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980 }, + { url = "https://files.pythonhosted.org/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982 }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 }, + { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 }, + { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + +[[package]] +name = "ninja" +version = "1.11.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d4/6b0324541018561c5e73e617bd16f20a4fc17d1179bb3b3520b6ca8beb7b/ninja-1.11.1.4.tar.gz", hash = "sha256:6aa39f6e894e0452e5b297327db00019383ae55d5d9c57c73b04f13bf79d438a", size = 201256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/b1/3a61b348936b62a386465b1937cd778fa3a5748582e26d832dbab844ff27/ninja-1.11.1.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:b33923c8da88e8da20b6053e38deb433f53656441614207e01d283ad02c5e8e7", size = 279071 }, + { url = "https://files.pythonhosted.org/packages/12/42/4c94fdad51fcf1f039a156e97de9e4d564c2a8cc0303782d36f9bd893a4b/ninja-1.11.1.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cede0af00b58e27b31f2482ba83292a8e9171cdb9acc2c867a3b6e40b3353e43", size = 472026 }, + { url = "https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0", size = 422814 }, + { url = "https://files.pythonhosted.org/packages/e3/ad/fb6cca942528e25e8e0ab0f0cf98fe007319bf05cf69d726c564b815c4af/ninja-1.11.1.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3090d4488fadf6047d0d7a1db0c9643a8d391f0d94729554dbb89b5bdc769d7", size = 156965 }, + { url = "https://files.pythonhosted.org/packages/a8/e7/d94a1b60031b115dd88526834b3da69eaacdc3c1a6769773ca8e2b1386b5/ninja-1.11.1.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecce44a00325a93631792974659cf253a815cc6da4ec96f89742925dfc295a0d", size = 179937 }, + { url = "https://files.pythonhosted.org/packages/08/cc/e9316a28235409e9363794fc3d0b3083e48dd80d441006de66421e55f364/ninja-1.11.1.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c29bb66d2aa46a2409ab369ea804c730faec7652e8c22c1e428cc09216543e5", size = 157020 }, + { url = "https://files.pythonhosted.org/packages/e3/30/389b22300541aa5f2e9dad322c4de2f84be4e32aa4e8babd9160d620b5f1/ninja-1.11.1.4-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:055f386fb550c2c9d6157e45e20a84d29c47968876b9c5794ae2aec46f952306", size = 130389 }, + { url = "https://files.pythonhosted.org/packages/a9/10/e27f35cb92813aabbb7ae771b1685b45be1cc8a0798ce7d4bfd08d142b93/ninja-1.11.1.4-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:f6186d7607bb090c3be1e10c8a56b690be238f953616626f5032238c66e56867", size = 372435 }, + { url = "https://files.pythonhosted.org/packages/c2/26/e3559619756739aae124c6abf7fe41f7e546ab1209cfbffb13137bff2d2e/ninja-1.11.1.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:cf4453679d15babc04ba023d68d091bb613091b67101c88f85d2171c6621c6eb", size = 419300 }, + { url = "https://files.pythonhosted.org/packages/35/46/809e4e9572570991b8e6f88f3583807d017371ab4cb09171cbc72a7eb3e4/ninja-1.11.1.4-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:d4a6f159b08b0ac4aca5ee1572e3e402f969139e71d85d37c0e2872129098749", size = 420239 }, + { url = "https://files.pythonhosted.org/packages/e6/64/5cb5710d15f844edf02ada577f8eddfdcd116f47eec15850f3371a3a4b33/ninja-1.11.1.4-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:c3b96bd875f3ef1db782470e9e41d7508905a0986571f219d20ffed238befa15", size = 415986 }, + { url = "https://files.pythonhosted.org/packages/95/b2/0e9ab1d926f423b12b09925f78afcc5e48b3c22e7121be3ddf6c35bf06a3/ninja-1.11.1.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:cf554e73f72c04deb04d0cf51f5fdb1903d9c9ca3d2344249c8ce3bd616ebc02", size = 379657 }, + { url = "https://files.pythonhosted.org/packages/c8/3e/fd6d330d0434168e7fe070d414b57dd99c4c133faa69c05b42a3cbdc6c13/ninja-1.11.1.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:cfdd09776436a1ff3c4a2558d3fc50a689fb9d7f1bdbc3e6f7b8c2991341ddb3", size = 454466 }, + { url = "https://files.pythonhosted.org/packages/e6/df/a25f3ad0b1c59d1b90564096e4fd89a6ca30d562b1e942f23880c3000b89/ninja-1.11.1.4-py3-none-win32.whl", hash = "sha256:2ab67a41c90bea5ec4b795bab084bc0b3b3bb69d3cd21ca0294fc0fc15a111eb", size = 255931 }, + { url = "https://files.pythonhosted.org/packages/5b/10/9b8fe9ac004847490cc7b54896124c01ce2d87d95dc60aabd0b8591addff/ninja-1.11.1.4-py3-none-win_amd64.whl", hash = "sha256:4617b3c12ff64b611a7d93fd9e378275512bb36eff8babff7c83f5116b4f8d66", size = 296461 }, + { url = "https://files.pythonhosted.org/packages/b9/58/612a17593c2d117f96c7f6b7f1e6570246bddc4b1e808519403a1417f217/ninja-1.11.1.4-py3-none-win_arm64.whl", hash = "sha256:5713cf50c5be50084a8693308a63ecf9e55c3132a78a41ab1363a28b6caaaee1", size = 271441 }, +] + +[[package]] +name = "numpy" +version = "2.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/78/31103410a57bc2c2b93a3597340a8119588571f6a4539067546cb9a0bfac/numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f", size = 20270701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/89/a79e86e5c1433926ed7d60cb267fb64aa578b6101ab645800fd43b4801de/numpy-2.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9", size = 21250661 }, + { url = "https://files.pythonhosted.org/packages/79/c2/f50921beb8afd60ed9589ad880332cfefdb805422210d327fb48f12b7a81/numpy-2.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae", size = 14389926 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/2c4e96130b0b0f97b0ef4a06d6dae3b39d058b21a5e2fa2decd7fd6b1c8f/numpy-2.2.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775", size = 5428329 }, + { url = "https://files.pythonhosted.org/packages/7f/a5/3d7094aa898f4fc5c84cdfb26beeae780352d43f5d8bdec966c4393d644c/numpy-2.2.4-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9", size = 6963559 }, + { url = "https://files.pythonhosted.org/packages/4c/22/fb1be710a14434c09080dd4a0acc08939f612ec02efcb04b9e210474782d/numpy-2.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2", size = 14368066 }, + { url = "https://files.pythonhosted.org/packages/c2/07/2e5cc71193e3ef3a219ffcf6ca4858e46ea2be09c026ddd480d596b32867/numpy-2.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020", size = 16417040 }, + { url = "https://files.pythonhosted.org/packages/1a/97/3b1537776ad9a6d1a41813818343745e8dd928a2916d4c9edcd9a8af1dac/numpy-2.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3", size = 15879862 }, + { url = "https://files.pythonhosted.org/packages/b0/b7/4472f603dd45ef36ff3d8e84e84fe02d9467c78f92cc121633dce6da307b/numpy-2.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017", size = 18206032 }, + { url = "https://files.pythonhosted.org/packages/0d/bd/6a092963fb82e6c5aa0d0440635827bbb2910da229545473bbb58c537ed3/numpy-2.2.4-cp310-cp310-win32.whl", hash = "sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a", size = 6608517 }, + { url = "https://files.pythonhosted.org/packages/01/e3/cb04627bc2a1638948bc13e818df26495aa18e20d5be1ed95ab2b10b6847/numpy-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542", size = 12943498 }, + { url = "https://files.pythonhosted.org/packages/b2/5c/f09c33a511aff41a098e6ef3498465d95f6360621034a3d95f47edbc9119/numpy-2.2.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8", size = 21081956 }, + { url = "https://files.pythonhosted.org/packages/ba/30/74c48b3b6494c4b820b7fa1781d441e94d87a08daa5b35d222f06ba41a6f/numpy-2.2.4-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c", size = 6827143 }, + { url = "https://files.pythonhosted.org/packages/54/f5/ab0d2f48b490535c7a80e05da4a98902b632369efc04f0e47bb31ca97d8f/numpy-2.2.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d", size = 16233350 }, + { url = "https://files.pythonhosted.org/packages/3b/3a/2f6d8c1f8e45d496bca6baaec93208035faeb40d5735c25afac092ec9a12/numpy-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d", size = 12857565 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, +] + +[[package]] +name = "openai" +version = "1.69.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/99/d164612528dfb7a9b19330623daded608e75d25823b01f81e0376eb388a4/openai-1.69.0.tar.gz", hash = "sha256:7b8a10a8ff77e1ae827e5e4c8480410af2070fb68bc973d6c994cf8218f1f98d", size = 409579 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/a4/28113be8b7bc937656aaf7b06feff7e9a5eb742ee4e405c6c48c30d879c4/openai-1.69.0-py3-none-any.whl", hash = "sha256:73c4b2ddfd050060f8d93c70367189bd891e70a5adb6d69c04c3571f4fea5627", size = 599068 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, + { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, + { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, + { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, + { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, + { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 }, + { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 }, + { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 }, + { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 }, + { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 }, + { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 }, + { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 }, + { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 }, + { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 }, + { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 }, + { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 }, + { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 }, + { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 }, + { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 }, + { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 }, + { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 }, + { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "propcache" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/56/e27c136101addf877c8291dbda1b3b86ae848f3837ce758510a0d806c92f/propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98", size = 80224 }, + { url = "https://files.pythonhosted.org/packages/63/bd/88e98836544c4f04db97eefd23b037c2002fa173dd2772301c61cd3085f9/propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180", size = 46491 }, + { url = "https://files.pythonhosted.org/packages/15/43/0b8eb2a55753c4a574fc0899885da504b521068d3b08ca56774cad0bea2b/propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71", size = 45927 }, + { url = "https://files.pythonhosted.org/packages/ad/6c/d01f9dfbbdc613305e0a831016844987a1fb4861dd221cd4c69b1216b43f/propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649", size = 206135 }, + { url = "https://files.pythonhosted.org/packages/9a/8a/e6e1c77394088f4cfdace4a91a7328e398ebed745d59c2f6764135c5342d/propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f", size = 220517 }, + { url = "https://files.pythonhosted.org/packages/19/3b/6c44fa59d6418f4239d5db8b1ece757351e85d6f3ca126dfe37d427020c8/propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229", size = 218952 }, + { url = "https://files.pythonhosted.org/packages/7c/e4/4aeb95a1cd085e0558ab0de95abfc5187329616193a1012a6c4c930e9f7a/propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46", size = 206593 }, + { url = "https://files.pythonhosted.org/packages/da/6a/29fa75de1cbbb302f1e1d684009b969976ca603ee162282ae702287b6621/propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7", size = 196745 }, + { url = "https://files.pythonhosted.org/packages/19/7e/2237dad1dbffdd2162de470599fa1a1d55df493b16b71e5d25a0ac1c1543/propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0", size = 203369 }, + { url = "https://files.pythonhosted.org/packages/a4/bc/a82c5878eb3afb5c88da86e2cf06e1fe78b7875b26198dbb70fe50a010dc/propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519", size = 198723 }, + { url = "https://files.pythonhosted.org/packages/17/76/9632254479c55516f51644ddbf747a45f813031af5adcb8db91c0b824375/propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd", size = 200751 }, + { url = "https://files.pythonhosted.org/packages/3e/c3/a90b773cf639bd01d12a9e20c95be0ae978a5a8abe6d2d343900ae76cd71/propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259", size = 210730 }, + { url = "https://files.pythonhosted.org/packages/ed/ec/ad5a952cdb9d65c351f88db7c46957edd3d65ffeee72a2f18bd6341433e0/propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e", size = 213499 }, + { url = "https://files.pythonhosted.org/packages/83/c0/ea5133dda43e298cd2010ec05c2821b391e10980e64ee72c0a76cdbb813a/propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136", size = 207132 }, + { url = "https://files.pythonhosted.org/packages/79/dd/71aae9dec59333064cfdd7eb31a63fa09f64181b979802a67a90b2abfcba/propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42", size = 40952 }, + { url = "https://files.pythonhosted.org/packages/31/0a/49ff7e5056c17dfba62cbdcbb90a29daffd199c52f8e65e5cb09d5f53a57/propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833", size = 45163 }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, +] + +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163 }, +] + +[[package]] +name = "protobuf" +version = "5.29.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/7d/b9dca7365f0e2c4fa7c193ff795427cfa6290147e5185ab11ece280a18e7/protobuf-5.29.4.tar.gz", hash = "sha256:4f1dfcd7997b31ef8f53ec82781ff434a28bf71d9102ddde14d076adcfc78c99", size = 424902 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/b2/043a1a1a20edd134563699b0e91862726a0dc9146c090743b6c44d798e75/protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7", size = 422709 }, + { url = "https://files.pythonhosted.org/packages/79/fc/2474b59570daa818de6124c0a15741ee3e5d6302e9d6ce0bdfd12e98119f/protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d", size = 434506 }, + { url = "https://files.pythonhosted.org/packages/46/de/7c126bbb06aa0f8a7b38aaf8bd746c514d70e6a2a3f6dd460b3b7aad7aae/protobuf-5.29.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:307ecba1d852ec237e9ba668e087326a67564ef83e45a0189a772ede9e854dd0", size = 417826 }, + { url = "https://files.pythonhosted.org/packages/a2/b5/bade14ae31ba871a139aa45e7a8183d869efe87c34a4850c87b936963261/protobuf-5.29.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:aec4962f9ea93c431d5714ed1be1c93f13e1a8618e70035ba2b0564d9e633f2e", size = 319574 }, + { url = "https://files.pythonhosted.org/packages/46/88/b01ed2291aae68b708f7d334288ad5fb3e7aa769a9c309c91a0d55cb91b0/protobuf-5.29.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:d7d3f7d1d5a66ed4942d4fefb12ac4b14a29028b209d4bfb25c68ae172059922", size = 319672 }, + { url = "https://files.pythonhosted.org/packages/12/fb/a586e0c973c95502e054ac5f81f88394f24ccc7982dac19c515acd9e2c93/protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862", size = 172551 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "pyarrow" +version = "19.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/01/b23b514d86b839956238d3f8ef206fd2728eee87ff1b8ce150a5678d9721/pyarrow-19.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:fc28912a2dc924dddc2087679cc8b7263accc71b9ff025a1362b004711661a69", size = 30688914 }, + { url = "https://files.pythonhosted.org/packages/c6/68/218ff7cf4a0652a933e5f2ed11274f724dd43b9813cb18dd72c0a35226a2/pyarrow-19.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fca15aabbe9b8355800d923cc2e82c8ef514af321e18b437c3d782aa884eaeec", size = 32102866 }, + { url = "https://files.pythonhosted.org/packages/98/01/c295050d183014f4a2eb796d7d2bbfa04b6cccde7258bb68aacf6f18779b/pyarrow-19.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76aef7f5f7e4a757fddcdcf010a8290958f09e3470ea458c80d26f4316ae89", size = 41147682 }, + { url = "https://files.pythonhosted.org/packages/40/17/a6c3db0b5f3678f33bbb552d2acbc16def67f89a72955b67b0109af23eb0/pyarrow-19.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d03c9d6f2a3dffbd62671ca070f13fc527bb1867b4ec2b98c7eeed381d4f389a", size = 42179192 }, + { url = "https://files.pythonhosted.org/packages/cf/75/c7c8e599300d8cebb6cb339014800e1c720c9db2a3fcb66aa64ec84bac72/pyarrow-19.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:65cf9feebab489b19cdfcfe4aa82f62147218558d8d3f0fc1e9dea0ab8e7905a", size = 40517272 }, + { url = "https://files.pythonhosted.org/packages/ef/c9/68ab123ee1528699c4d5055f645ecd1dd68ff93e4699527249d02f55afeb/pyarrow-19.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:41f9706fbe505e0abc10e84bf3a906a1338905cbbcf1177b71486b03e6ea6608", size = 42069036 }, + { url = "https://files.pythonhosted.org/packages/54/e3/d5cfd7654084e6c0d9c3ce949e5d9e0ccad569ae1e2d5a68a3ec03b2be89/pyarrow-19.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6cb2335a411b713fdf1e82a752162f72d4a7b5dbc588e32aa18383318b05866", size = 25277951 }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259 }, +] + +[[package]] +name = "pydantic" +version = "2.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/a3/698b87a4d4d303d7c5f62ea5fbf7a79cab236ccfbd0a17847b7f77f8163e/pydantic-2.11.1.tar.gz", hash = "sha256:442557d2910e75c991c39f4b4ab18963d57b9b55122c8b2a9cd176d8c29ce968", size = 782817 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/12/f9221a949f2419e2e23847303c002476c26fbcfd62dc7f3d25d0bec5ca99/pydantic-2.11.1-py3-none-any.whl", hash = "sha256:5b6c415eee9f8123a14d859be0c84363fec6b1feb6b688d6435801230b56e0b8", size = 442648 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/05/91ce14dfd5a3a99555fce436318cc0fd1f08c4daa32b3248ad63669ea8b4/pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3", size = 434080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/43/0649ad07e66b36a3fb21442b425bd0348ac162c5e686b36471f363201535/pydantic_core-2.33.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71dffba8fe9ddff628c68f3abd845e91b028361d43c5f8e7b3f8b91d7d85413e", size = 2042968 }, + { url = "https://files.pythonhosted.org/packages/a0/a6/975fea4774a459e495cb4be288efd8b041ac756a0a763f0b976d0861334b/pydantic_core-2.33.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:abaeec1be6ed535a5d7ffc2e6c390083c425832b20efd621562fbb5bff6dc518", size = 1860347 }, + { url = "https://files.pythonhosted.org/packages/aa/49/7858dadad305101a077ec4d0c606b6425a2b134ea8d858458a6d287fd871/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759871f00e26ad3709efc773ac37b4d571de065f9dfb1778012908bcc36b3a73", size = 1910060 }, + { url = "https://files.pythonhosted.org/packages/8d/4f/6522527911d9c5fe6d76b084d8b388d5c84b09d113247b39f91937500b34/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dcfebee69cd5e1c0b76a17e17e347c84b00acebb8dd8edb22d4a03e88e82a207", size = 1997129 }, + { url = "https://files.pythonhosted.org/packages/75/d0/06f396da053e3d73001ea4787e56b4d7132a87c0b5e2e15a041e808c35cd/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b1262b912435a501fa04cd213720609e2cefa723a07c92017d18693e69bf00b", size = 2140389 }, + { url = "https://files.pythonhosted.org/packages/f5/6b/b9ff5b69cd4ef007cf665463f3be2e481dc7eb26c4a55b2f57a94308c31a/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4726f1f3f42d6a25678c67da3f0b10f148f5655813c5aca54b0d1742ba821b8f", size = 2754237 }, + { url = "https://files.pythonhosted.org/packages/53/80/b4879de375cdf3718d05fcb60c9aa1f119d28e261dafa51b6a69c78f7178/pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e790954b5093dff1e3a9a2523fddc4e79722d6f07993b4cd5547825c3cbf97b5", size = 2007433 }, + { url = "https://files.pythonhosted.org/packages/46/24/54054713dc0af98a94eab37e0f4294dfd5cd8f70b2ca9dcdccd15709fd7e/pydantic_core-2.33.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34e7fb3abe375b5c4e64fab75733d605dda0f59827752debc99c17cb2d5f3276", size = 2123980 }, + { url = "https://files.pythonhosted.org/packages/3a/4c/257c1cb89e14cfa6e95ebcb91b308eb1dd2b348340ff76a6e6fcfa9969e1/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ecb158fb9b9091b515213bed3061eb7deb1d3b4e02327c27a0ea714ff46b0760", size = 2087433 }, + { url = "https://files.pythonhosted.org/packages/0c/62/927df8a39ad78ef7b82c5446e01dec9bb0043e1ad71d8f426062f5f014db/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:4d9149e7528af8bbd76cc055967e6e04617dcb2a2afdaa3dea899406c5521faa", size = 2260242 }, + { url = "https://files.pythonhosted.org/packages/74/f2/389414f7c77a100954e84d6f52a82bd1788ae69db72364376d8a73b38765/pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e81a295adccf73477220e15ff79235ca9dcbcee4be459eb9d4ce9a2763b8386c", size = 2258227 }, + { url = "https://files.pythonhosted.org/packages/53/99/94516313e15d906a1264bb40faf24a01a4af4e2ca8a7c10dd173b6513c5a/pydantic_core-2.33.0-cp310-cp310-win32.whl", hash = "sha256:f22dab23cdbce2005f26a8f0c71698457861f97fc6318c75814a50c75e87d025", size = 1925523 }, + { url = "https://files.pythonhosted.org/packages/7d/67/cc789611c6035a0b71305a1ec6ba196256ced76eba8375f316f840a70456/pydantic_core-2.33.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cb2390355ba084c1ad49485d18449b4242da344dea3e0fe10babd1f0db7dcfc", size = 1951872 }, + { url = "https://files.pythonhosted.org/packages/44/77/85e173b715e1a277ce934f28d877d82492df13e564fa68a01c96f36a47ad/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2762c568596332fdab56b07060c8ab8362c56cf2a339ee54e491cd503612c50", size = 2040129 }, + { url = "https://files.pythonhosted.org/packages/33/e7/33da5f8a94bbe2191cfcd15bd6d16ecd113e67da1b8c78d3cc3478112dab/pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bf637300ff35d4f59c006fff201c510b2b5e745b07125458a5389af3c0dff8c", size = 1872656 }, + { url = "https://files.pythonhosted.org/packages/b4/7a/9600f222bea840e5b9ba1f17c0acc79b669b24542a78c42c6a10712c0aae/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c151ce3d59ed56ebd7ce9ce5986a409a85db697d25fc232f8e81f195aa39a1", size = 1903731 }, + { url = "https://files.pythonhosted.org/packages/81/d2/94c7ca4e24c5dcfb74df92e0836c189e9eb6814cf62d2f26a75ea0a906db/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee65f0cc652261744fd07f2c6e6901c914aa6c5ff4dcfaf1136bc394d0dd26b", size = 2083966 }, + { url = "https://files.pythonhosted.org/packages/b8/74/a0259989d220e8865ed6866a6d40539e40fa8f507e587e35d2414cc081f8/pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:024d136ae44d233e6322027bbf356712b3940bee816e6c948ce4b90f18471b3d", size = 2118951 }, + { url = "https://files.pythonhosted.org/packages/13/4c/87405ed04d6d07597920b657f082a8e8e58bf3034178bb9044b4d57a91e2/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e37f10f6d4bc67c58fbd727108ae1d8b92b397355e68519f1e4a7babb1473442", size = 2079632 }, + { url = "https://files.pythonhosted.org/packages/5a/4c/bcb02970ef91d4cd6de7c6893101302637da456bc8b52c18ea0d047b55ce/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:502ed542e0d958bd12e7c3e9a015bce57deaf50eaa8c2e1c439b512cb9db1e3a", size = 2250541 }, + { url = "https://files.pythonhosted.org/packages/a3/2b/dbe5450c4cd904be5da736dcc7f2357b828199e29e38de19fc81f988b288/pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:715c62af74c236bf386825c0fdfa08d092ab0f191eb5b4580d11c3189af9d330", size = 2255685 }, + { url = "https://files.pythonhosted.org/packages/ca/a6/ca1d35f695d81f639c5617fc9efb44caad21a9463383fa45364b3044175a/pydantic_core-2.33.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bccc06fa0372151f37f6b69834181aa9eb57cf8665ed36405fb45fbf6cac3bae", size = 2082395 }, +] + +[[package]] +name = "pydra-config" +version = "0.0.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "pydantic" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/8f/e834d71d308b98c97c37f94b7df4bee01761c57ec16d506dbe6cb7d8deda/pydra_config-0.0.14.tar.gz", hash = "sha256:a486cbb3921a49131a6e9151948ce42e731bfafe1695a143132bd9d9bc16994b", size = 16261 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/ff/745adc70a88f0e4752972b30b591ebed0824597306b2329ce706f05a04da/pydra_config-0.0.14-py3-none-any.whl", hash = "sha256:f24d3a8b8364e75bcc3f854a7763befffc56738c563425bf62d1b62f12582009", size = 10803 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pyparsing" +version = "3.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "rpds-py" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/b3/52b213298a0ba7097c7ea96bee95e1947aa84cc816d48cebb539770cdf41/rpds_py-0.24.0.tar.gz", hash = "sha256:772cc1b2cd963e7e17e6cc55fe0371fb9c704d63e44cacec7b9b7f523b78919e", size = 26863 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/cbc43b220c9deb536b07fbd598c97d463bbb7afb788851891252fc920742/rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724", size = 377531 }, + { url = "https://files.pythonhosted.org/packages/42/15/cc4b09ef160483e49c3aab3b56f3d375eadf19c87c48718fb0147e86a446/rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b", size = 362273 }, + { url = "https://files.pythonhosted.org/packages/8c/a2/67718a188a88dbd5138d959bed6efe1cc7413a4caa8283bd46477ed0d1ad/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8acd55bd5b071156bae57b555f5d33697998752673b9de554dd82f5b5352727", size = 388111 }, + { url = "https://files.pythonhosted.org/packages/e5/e6/cbf1d3163405ad5f4a1a6d23f80245f2204d0c743b18525f34982dec7f4d/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7e80d375134ddb04231a53800503752093dbb65dad8dabacce2c84cccc78e964", size = 394447 }, + { url = "https://files.pythonhosted.org/packages/21/bb/4fe220ccc8a549b38b9e9cec66212dc3385a82a5ee9e37b54411cce4c898/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60748789e028d2a46fc1c70750454f83c6bdd0d05db50f5ae83e2db500b34da5", size = 448028 }, + { url = "https://files.pythonhosted.org/packages/a5/41/d2d6e0fd774818c4cadb94185d30cf3768de1c2a9e0143fc8bc6ce59389e/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e1daf5bf6c2be39654beae83ee6b9a12347cb5aced9a29eecf12a2d25fff664", size = 447410 }, + { url = "https://files.pythonhosted.org/packages/a7/a7/6d04d438f53d8bb2356bb000bea9cf5c96a9315e405b577117e344cc7404/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b221c2457d92a1fb3c97bee9095c874144d196f47c038462ae6e4a14436f7bc", size = 389531 }, + { url = "https://files.pythonhosted.org/packages/23/be/72e6df39bd7ca5a66799762bf54d8e702483fdad246585af96723109d486/rpds_py-0.24.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66420986c9afff67ef0c5d1e4cdc2d0e5262f53ad11e4f90e5e22448df485bf0", size = 420099 }, + { url = "https://files.pythonhosted.org/packages/8c/c9/ca100cd4688ee0aa266197a5cb9f685231676dd7d573041ca53787b23f4e/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:43dba99f00f1d37b2a0265a259592d05fcc8e7c19d140fe51c6e6f16faabeb1f", size = 564950 }, + { url = "https://files.pythonhosted.org/packages/05/98/908cd95686d33b3ac8ac2e582d7ae38e2c3aa2c0377bf1f5663bafd1ffb2/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a88c0d17d039333a41d9bf4616bd062f0bd7aa0edeb6cafe00a2fc2a804e944f", size = 591778 }, + { url = "https://files.pythonhosted.org/packages/7b/ac/e143726f1dd3215efcb974b50b03bd08a8a1556b404a0a7872af6d197e57/rpds_py-0.24.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc31e13ce212e14a539d430428cd365e74f8b2d534f8bc22dd4c9c55b277b875", size = 560421 }, + { url = "https://files.pythonhosted.org/packages/60/28/add1c1d2fcd5aa354f7225d036d4492261759a22d449cff14841ef36a514/rpds_py-0.24.0-cp310-cp310-win32.whl", hash = "sha256:fc2c1e1b00f88317d9de6b2c2b39b012ebbfe35fe5e7bef980fd2a91f6100a07", size = 222089 }, + { url = "https://files.pythonhosted.org/packages/b0/ac/81f8066c6de44c507caca488ba336ae30d35d57f61fe10578824d1a70196/rpds_py-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0145295ca415668420ad142ee42189f78d27af806fcf1f32a18e51d47dd2052", size = 234622 }, + { url = "https://files.pythonhosted.org/packages/99/48/11dae46d0c7f7e156ca0971a83f89c510af0316cd5d42c771b7cef945f0c/rpds_py-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:619ca56a5468f933d940e1bf431c6f4e13bef8e688698b067ae68eb4f9b30e3a", size = 378224 }, + { url = "https://files.pythonhosted.org/packages/33/18/e8398d255369e35d312942f3bb8ecaff013c44968904891be2ab63b3aa94/rpds_py-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b28e5122829181de1898c2c97f81c0b3246d49f585f22743a1246420bb8d399", size = 363252 }, + { url = "https://files.pythonhosted.org/packages/17/39/dd73ba691f4df3e6834bf982de214086ac3359ab3ac035adfb30041570e3/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e5ab32cf9eb3647450bc74eb201b27c185d3857276162c101c0f8c6374e098", size = 388871 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/da0530b25cabd0feca2a759b899d2df325069a94281eeea8ac44c6cfeff7/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:208b3a70a98cf3710e97cabdc308a51cd4f28aa6e7bb11de3d56cd8b74bab98d", size = 394766 }, + { url = "https://files.pythonhosted.org/packages/4c/ee/dd1c5040a431beb40fad4a5d7868acf343444b0bc43e627c71df2506538b/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbc4362e06f950c62cad3d4abf1191021b2ffaf0b31ac230fbf0526453eee75e", size = 448712 }, + { url = "https://files.pythonhosted.org/packages/f5/ec/6b93ffbb686be948e4d91ec76f4e6757f8551034b2a8176dd848103a1e34/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebea2821cdb5f9fef44933617be76185b80150632736f3d76e54829ab4a3b4d1", size = 447150 }, + { url = "https://files.pythonhosted.org/packages/55/d5/a1c23760adad85b432df074ced6f910dd28f222b8c60aeace5aeb9a6654e/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4df06c35465ef4d81799999bba810c68d29972bf1c31db61bfdb81dd9d5bb", size = 390662 }, + { url = "https://files.pythonhosted.org/packages/a5/f3/419cb1f9bfbd3a48c256528c156e00f3349e3edce5ad50cbc141e71f66a5/rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3aa13bdf38630da298f2e0d77aca967b200b8cc1473ea05248f6c5e9c9bdb44", size = 421351 }, + { url = "https://files.pythonhosted.org/packages/98/8e/62d1a55078e5ede0b3b09f35e751fa35924a34a0d44d7c760743383cd54a/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:041f00419e1da7a03c46042453598479f45be3d787eb837af382bfc169c0db33", size = 566074 }, + { url = "https://files.pythonhosted.org/packages/fc/69/b7d1003166d78685da032b3c4ff1599fa536a3cfe6e5ce2da87c9c431906/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8754d872a5dfc3c5bf9c0e059e8107451364a30d9fd50f1f1a85c4fb9481164", size = 592398 }, + { url = "https://files.pythonhosted.org/packages/ea/a8/1c98bc99338c37faadd28dd667d336df7409d77b4da999506a0b6b1c0aa2/rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:896c41007931217a343eff197c34513c154267636c8056fb409eafd494c3dcdc", size = 561114 }, + { url = "https://files.pythonhosted.org/packages/2b/41/65c91443685a4c7b5f1dd271beadc4a3e063d57c3269221548dd9416e15c/rpds_py-0.24.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:92558d37d872e808944c3c96d0423b8604879a3d1c86fdad508d7ed91ea547d5", size = 235548 }, +] + +[[package]] +name = "rsa" +version = "4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "sentry-sdk" +version = "2.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/ef/4847dcd63e3f3c451cf701a825d21200f1322d46ac97586d5c90a13dfea1/sentry_sdk-2.24.1.tar.gz", hash = "sha256:8ba3c29990fa48865b908b3b9dc5ae7fa7e72407c7c9e91303e5206b32d7b8b1", size = 318124 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/95/91137ffe7a5956496155af5ffbe45ee4ddfa795a569136147e766abd14b1/sentry_sdk-2.24.1-py2.py3-none-any.whl", hash = "sha256:36baa6a1128b9d98d2adc5e9b2f887eff0a6af558fc2b96ed51919042413556d", size = 336945 }, +] + +[[package]] +name = "setproctitle" +version = "1.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/e1/9ccff2682c38061baa07e128b60712bc18e3398aa7d5471c51a704f9d24c/setproctitle-1.3.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02870e0cb0de7f68a7a8a5b23c2bc0ce63821cab3d9b126f9be80bb6cd674c80", size = 17256 }, + { url = "https://files.pythonhosted.org/packages/ed/64/936c1f92d60052f11a8de9f90a4b7ec4996b8ebd6d67ba425ed214c80771/setproctitle-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55b278135be742b8901067479626d909f6613bd2d2c4fd0de6bb46f80e07a919", size = 11893 }, + { url = "https://files.pythonhosted.org/packages/01/2d/abc817b3778d9b1f7675020030379a0c39e0bf74b36af211b26191a63da3/setproctitle-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53fc971f7bf7a674f571a23cdec70f2f0ac88152c59c06aa0808d0be6d834046", size = 31295 }, + { url = "https://files.pythonhosted.org/packages/03/4d/e2055dfb1b492fd3a3b27deeaa642d81c580d48a16bc9b07afc3504af677/setproctitle-1.3.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb0500e1bc6f00b8ba696c3743ddff14c8679e3c2ca9d292c008ac51488d17cf", size = 32637 }, + { url = "https://files.pythonhosted.org/packages/89/28/a1f23d7d127dff59fe75ad671d1d5c83ab8cba10d0e343820b96d5d8a2f7/setproctitle-1.3.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995b3ac1b5fe510f4e1d1c19ebf19f4bceb448f2d6e8d99ea23f33cb6f1a277e", size = 29772 }, + { url = "https://files.pythonhosted.org/packages/df/46/2ea4d436c7d664d41df7e60fbd3103f1139a931638e998f478e870e72255/setproctitle-1.3.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a05e2c3fdfbda32b9c9da72d0506398d1efb5bd2c5981b9e12d3622eb3d4f9", size = 30811 }, + { url = "https://files.pythonhosted.org/packages/45/60/4c17211c2d80e6fe9fa486fa3214d565d0cd9a6eff0b67e6219ddb2ba49c/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:310c7f4ca4c8476a9840b2cd4b22ee602a49a3c902fdcd2dd8284685abd10a9a", size = 30442 }, + { url = "https://files.pythonhosted.org/packages/7e/bf/65a8f8f2d03cd9a9429cfa0d6b22282ff7a609a4d08602bcb8351a271bec/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:867af4a5c3d85484fbcc50ea88bcd375acf709cff88a3259575361849c0da351", size = 29492 }, + { url = "https://files.pythonhosted.org/packages/c6/96/56f45f0b81fcc776f925c34e2699040df39cfc6b3cc7520d9b378314435b/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ec0a7fe9f1ba90900144489bc93ce7dd4dec3f3df1e7f188c9e58364fe4a4c5", size = 31947 }, + { url = "https://files.pythonhosted.org/packages/ec/9d/6b697c1562b21368e579d820bca2a607e565638fd332247841eb65dec4b2/setproctitle-1.3.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aaee7acba2733a14a886488b7495bfec4a8d6407124c04a0946dbde1684230a3", size = 29863 }, + { url = "https://files.pythonhosted.org/packages/ba/0f/4551cbb120d003fa1284ee35d559366e09b513a87dfee02f804da1936054/setproctitle-1.3.5-cp310-cp310-win32.whl", hash = "sha256:bd2cccd972e4282af4ce2c13cd9ebdf07be157eabafd8ce648fffdc8ae6fbe28", size = 11471 }, + { url = "https://files.pythonhosted.org/packages/a6/f4/2dd926687b7a3bdaa83533e2898f929e1ff3bdeb6aa271bdb1d4d5923c7e/setproctitle-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:81f2328ac34c9584e1e5f87eea916c0bc48476a06606a07debae07acdd7ab5ea", size = 12196 }, + { url = "https://files.pythonhosted.org/packages/4a/ba/2524329ce958599069f0d0e4cfd3d6fbb7c58a4408b9e5609698e47353ec/setproctitle-1.3.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dc66b84beb0d5eb03abf0c3140c6d2cbe3d67ae9f0824a09dfa8c6ff164319a6", size = 11418 }, + { url = "https://files.pythonhosted.org/packages/a6/5f/a049640b05c609585ad0f471e667be0fd9ab533219127b455826d31587d5/setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31dc9b330e7cac7685bdef790747c07914081c11ee1066eb0c597303dfb52010", size = 13425 }, + { url = "https://files.pythonhosted.org/packages/a9/15/caa47039e267ea67316b285e2e308ae529872ad6a143edf03a7d8edf6175/setproctitle-1.3.5-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4028639b511f5e641d116b3b54ad70c637ebd1b4baac0948283daf11b104119f", size = 13026 }, + { url = "https://files.pythonhosted.org/packages/c1/a2/1fb0647a251f4c788b94f751cf23171b2a905758fd13ef8d126222d41428/setproctitle-1.3.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6bddef4e27d0ed74e44b58bf050bc3108591bf17d20d461fc59cd141282f849c", size = 12222 }, +] + +[[package]] +name = "setuptools" +version = "78.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "sigtools" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/db/669ca14166814da187b3087b908ca924cf83f5b504fe23b3859a3ef67d4f/sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c", size = 71910 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/91/853dbf6ec096197dba9cd5fd0c836c5fc19142038b7db60ebe6332b1bab1/sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c", size = 76419 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "starlette" +version = "0.46.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 }, +] + +[[package]] +name = "sympy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, +] + +[[package]] +name = "synchronicity" +version = "0.9.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sigtools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/52/f34a9ab6d514e0808d0f572affb360411d596b3439107318c00889277dd6/synchronicity-0.9.11.tar.gz", hash = "sha256:cb5dbbcb43d637e516ae50db05a776da51a705d1e1a9c0e301f6049afc3c2cae", size = 50323 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/d5/7675cd9b8e18f05b9ea261acad5d197fcb8027d2a65b1a750427ec084593/synchronicity-0.9.11-py3-none-any.whl", hash = "sha256:231129654d2f56b1aa148e85ebd8545231be135771f6d2196d414175b1594ef6", size = 36827 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "tiktoken" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6", size = 33437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/10/28d59d43d72a0ebd4211371d0bf10c935cdecbb62b812ae04c58bfc37d96/tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f", size = 961465 }, + { url = "https://files.pythonhosted.org/packages/f8/0c/d4125348dedd1f8f38e3f85245e7fc38858ffc77c9b7edfb762a8191ba0b/tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225", size = 906849 }, + { url = "https://files.pythonhosted.org/packages/b9/ab/f9c7675747f259d133d66065106cf732a7c2bef6043062fbca8e011f7f4d/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590", size = 1048795 }, + { url = "https://files.pythonhosted.org/packages/e7/8c/7d1007557b343d5cf18349802e94d3a14397121e9105b4661f8cd753f9bf/tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c", size = 1080866 }, + { url = "https://files.pythonhosted.org/packages/72/40/61d6354cb64a563fce475a2907039be9fe809ca5f801213856353b01a35b/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311", size = 1092776 }, + { url = "https://files.pythonhosted.org/packages/f2/6c/83ca40527d072739f0704b9f59b325786c444ca63672a77cb69adc8181f7/tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5", size = 1142591 }, + { url = "https://files.pythonhosted.org/packages/ec/1f/a5d72755118e9e1b62cdf3ef9138eb83d49088f3cb37a9540025c81c0e75/tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702", size = 798864 }, +] + +[[package]] +name = "together" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "eval-type-backport" }, + { name = "filelock" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "pyarrow" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "rich" }, + { name = "tabulate" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/42/90cdaeac6e9db959228f77c84ab0069445302cc2e684db356417f1979052/together-1.5.4.tar.gz", hash = "sha256:b6cb28ea8d8d30723e53b2b1020b2f5924c395e24f358e948c334b049265bdfb", size = 64205 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/c5/2911b399b9ec755e8a4ba67a848bf80816ae55b6561fdeee739612d994f2/together-1.5.4-py3-none-any.whl", hash = "sha256:fef7d1b8a4b497dbe2b148750dfc2473da0cbc195390cf9194f99f17873690f3", size = 87774 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "torch" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/82/adc3a77b9fbbcb79d398d565d39dc0e09f43fff088599d15da81e6cfaaec/torch-2.5.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:7f179373a047b947dec448243f4e6598a1c960fa3bb978a9a7eecd529fbc363f", size = 906443143 }, + { url = "https://files.pythonhosted.org/packages/64/b0/0d2056c8d379a3f7f0c9fa9adece180f64fd6c339e2007a4fffbea7ecaa0/torch-2.5.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:15fbc95e38d330e5b0ef1593b7bc0a19f30e5bdad76895a5cffa1a6a044235e9", size = 91839507 }, + { url = "https://files.pythonhosted.org/packages/60/41/073193dd2566012eaeae44d6c5e55ba6a9b1d5687a251f12e1804a9e2968/torch-2.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:f499212f1cffea5d587e5f06144630ed9aa9c399bba12ec8905798d833bd1404", size = 203108822 }, + { url = "https://files.pythonhosted.org/packages/93/d4/6e7bda4e52c37a78b5066e407baff2426fd4543356ead3419383a0bf4011/torch-2.5.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:c54db1fade17287aabbeed685d8e8ab3a56fea9dd8d46e71ced2da367f09a49f", size = 64283014 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.50.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/29/37877123d6633a188997d75dc17d6f526745d63361794348ce748db23d49/transformers-4.50.3.tar.gz", hash = "sha256:1d795d24925e615a8e63687d077e4f7348c2702eb87032286eaa76d83cdc684f", size = 8774363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/22/733a6fc4a6445d835242f64c490fdd30f4a08d58f2b788613de3f9170692/transformers-4.50.3-py3-none-any.whl", hash = "sha256:6111610a43dec24ef32c3df0632c6b25b07d9711c01d9e1077bdd2ff6b14a38c", size = 10180411 }, +] + +[[package]] +name = "triton" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", marker = "sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/29/69aa56dc0b2eb2602b553881e34243475ea2afd9699be042316842788ff5/triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8", size = 209460013 }, +] + +[[package]] +name = "typer" +version = "0.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 }, +] + +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136 }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b", size = 106520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, +] + +[[package]] +name = "uritemplate" +version = "4.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/5a/4742fdba39cd02a56226815abfa72fe0aa81c33bed16ed045647d6000eba/uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", size = 273898 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e", size = 10356 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "uuid-utils" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0a/cbdb2eb4845dafeb632d02a18f47b02f87f2ce4f25266f5e3c017976ce89/uuid_utils-0.10.0.tar.gz", hash = "sha256:5db0e1890e8f008657ffe6ded4d9459af724ab114cfe82af1557c87545301539", size = 18828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/54/9d22fa16b19e5d1676eba510f08a9c458d96e2a62ff2c8ebad64251afb18/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d5a4508feefec62456cd6a41bcdde458d56827d908f226803b886d22a3d5e63", size = 573006 }, + { url = "https://files.pythonhosted.org/packages/08/8e/f895c6e52aa603e521fbc13b8626ba5dd99b6e2f5a55aa96ba5b232f4c53/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dbefc2b9113f9dfe56bdae58301a2b3c53792221410d422826f3d1e3e6555fe7", size = 292543 }, + { url = "https://files.pythonhosted.org/packages/b6/58/cc4834f377a5e97d6e184408ad96d13042308de56643b6e24afe1f6f34df/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffc49c33edf87d1ec8112a9b43e4cf55326877716f929c165a2cc307d31c73d5", size = 323340 }, + { url = "https://files.pythonhosted.org/packages/37/e3/6aeddf148f6a7dd7759621b000e8c85382ec83f52ae79b60842d1dc3ab6b/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0636b6208f69d5a4e629707ad2a89a04dfa8d1023e1999181f6830646ca048a1", size = 329653 }, + { url = "https://files.pythonhosted.org/packages/0c/00/dd6c2164ace70b7b1671d9129267df331481d7d1e5f9c5e6a564f07953f6/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc06452856b724df9dedfc161c3582199547da54aeb81915ec2ed54f92d19b0", size = 365471 }, + { url = "https://files.pythonhosted.org/packages/b4/e7/0ab8080fcae5462a7b5e555c1cef3d63457baffb97a59b9bc7b005a3ecb1/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:263b2589111c61decdd74a762e8f850c9e4386fb78d2cf7cb4dfc537054cda1b", size = 325844 }, + { url = "https://files.pythonhosted.org/packages/73/39/52d94e9ef75b03f44b39ffc6ac3167e93e74ef4d010a93d25589d9f48540/uuid_utils-0.10.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a558db48b7096de6b4d2d2210d82bba8586a6d55f99106b03bb7d01dc5c5bcd6", size = 344389 }, + { url = "https://files.pythonhosted.org/packages/7c/29/4824566f62666238290d99c62a58e4ab2a8b9cf2eccf94cebd9b3359131e/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:807465067f3c892514230326ac71a79b28a8dfe2c88ecd2d5675fc844f3c76b5", size = 510078 }, + { url = "https://files.pythonhosted.org/packages/5e/8f/bbcc7130d652462c685f0d3bd26bb214b754215b476340885a4cb50fb89a/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:57423d4a2b9d7b916de6dbd75ba85465a28f9578a89a97f7d3e098d9aa4e5d4a", size = 515937 }, + { url = "https://files.pythonhosted.org/packages/23/f8/34e0c00f5f188604d336713e6a020fcf53b10998e8ab24735a39ab076740/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:76d8d660f18ff6b767e319b1b5f927350cd92eafa4831d7ef5b57fdd1d91f974", size = 494111 }, + { url = "https://files.pythonhosted.org/packages/1a/52/b7f0066cc90a7a9c28d54061ed195cd617fde822e5d6ac3ccc88509c3c44/uuid_utils-0.10.0-cp39-abi3-win32.whl", hash = "sha256:6c11a71489338837db0b902b75e1ba7618d5d29f05fde4f68b3f909177dbc226", size = 173520 }, + { url = "https://files.pythonhosted.org/packages/8b/15/f04f58094674d333974243fb45d2c740cf4b79186fb707168e57943c84a3/uuid_utils-0.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:11c55ae64f6c0a7a0c741deae8ca2a4eaa11e9c09dbb7bec2099635696034cf7", size = 182965 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/8f3288797487c82981134732dee13b1ad12082890905476f95994ce49e0f/uuid_utils-0.10.0-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:acea543dfc7b87df749e3e814c54ac739a82ff5e3800d25bd25a3e00599e1554", size = 573053 }, + { url = "https://files.pythonhosted.org/packages/91/28/0eb5190aa39547015d60ce5453cfd37c4d87a48d25026d72044c20cad4fc/uuid_utils-0.10.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0767eefa7b1e96f06cfa9b95758d286240c01bbf19e9d8f1b6043cdbe76cc639", size = 292596 }, + { url = "https://files.pythonhosted.org/packages/e4/27/a451725d5df0db8baaa84adde94bbac4a33c3816a5215740c3f1dbdc46d3/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973fe4bb5258fd2ccb144d8b40c2d3158f16cc856a20527f8b40d14b2ae1dee9", size = 323414 }, + { url = "https://files.pythonhosted.org/packages/22/6b/0edc2ad855cbe07ffd891ec636c6ff57ae3a56cdf0e90467b2edbe5b7b43/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71b8505b67a0d77d0fbd765d8463094a8f447677125da7647bec7ea0b99406f0", size = 329720 }, + { url = "https://files.pythonhosted.org/packages/4b/1d/f73af741d9a4d3168704235ef06fbda823bf2ecf551ac29caa8d7cf8ea2a/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdcb1211bb61476cbef12a87101fa48243e20ed82b2bd324c816b1b5826bd5e", size = 365545 }, + { url = "https://files.pythonhosted.org/packages/b1/06/92104c8ea66a6d645f00520222a52c4b91a444c2c30201ff0036dedfb8da/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5247f1df040aae71ea313819b563debe69bca7086a2cc6a3ac0eaddd3dadac", size = 325920 }, + { url = "https://files.pythonhosted.org/packages/94/fe/0710e28b94f2311b40757dc43513290134cb4579f79981127c58640d736c/uuid_utils-0.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a50bd29ef89660b93aa07ffa95ac691a0e12832375030569a8bd5c9272f3b8e6", size = 344458 }, +] + +[[package]] +name = "wandb" +version = "0.19.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "docker-pycreds" }, + { name = "gitpython" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f2/001aee271c0665afc7424c14ea2fa6fd9987d9d4e186d187cd0bac2d11db/wandb-0.19.8.tar.gz", hash = "sha256:3a4844bb38758657b94b090e72ee355fe5b926e3a048232f0ca4248f801d8d80", size = 39244743 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/79/058be304cddf78e53ebaddeefbfeec66c3d67d6f733653f9f7de48efcfe0/wandb-0.19.8-py3-none-any.whl", hash = "sha256:75dea834d579f38e0e1f857e644020e22c851f9b920e9c6c6345bacb98c3f3fc", size = 6305883 }, + { url = "https://files.pythonhosted.org/packages/3c/df/e8e0ec80afd0a437e3ddc10da3e2286d9bab2169b48fd0f768a455d49971/wandb-0.19.8-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:6556147ba33b7ff4a0111bb6bf5ea485e4974c22f520f1e2a5eaad670a058c80", size = 20474304 }, + { url = "https://files.pythonhosted.org/packages/9a/6e/171701d80f0f20e53c74e8e0ecab06c31a59d53cab295ec108ac39140fef/wandb-0.19.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f68517c2059d12912a90ae32ce95a2711e39f6c157c759eb191527739a12db8b", size = 19942528 }, + { url = "https://files.pythonhosted.org/packages/59/24/24720683f6b9c19dd41b081e32d4585dc9a2f1e2d0b7a9cb63cde690868e/wandb-0.19.8-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:96cb534b19c2d301ac4fb0e7cfbc32198a704e29e87337133d6b71fdad33cf2f", size = 20471015 }, + { url = "https://files.pythonhosted.org/packages/22/0a/a9f6dcc96a6ee7cd5365af3a8e4b896cd373e4a11cbb1468b6d9aaac37f3/wandb-0.19.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1781b36434d494d6b34e2149201bae8cab960cb31571f11b981c4a62462d5af8", size = 19460731 }, + { url = "https://files.pythonhosted.org/packages/e0/71/7b7050ecab7288782ae0c7560f1ca06f4cf854a5ae08abeaf643785af1a0/wandb-0.19.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c25f0e40025b838b7a424b51837a2a5fd071686c59e1c46d73f04e760d305f79", size = 20792273 }, + { url = "https://files.pythonhosted.org/packages/45/54/8b6f1f41cf4a8b67439d4f0842de80084709cad2939152503046b42d863c/wandb-0.19.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:068eb0154f80be973ab291346d831e9cc80a9de1b8752bdeb48a997c3506fec4", size = 19470793 }, + { url = "https://files.pythonhosted.org/packages/d7/bb/28d94b0369f0055dc4aef704971858a414490f6eb23b9bbfa70d090f4b59/wandb-0.19.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:82a956150e53df0b4c193933b3e62c3e8255dc8b43bb187270939ef35b03fda3", size = 20872380 }, + { url = "https://files.pythonhosted.org/packages/80/82/9d653fe043d48075342bed7a545611391fc62095fb1e77d6574a8f2091e3/wandb-0.19.8-py3-none-win32.whl", hash = "sha256:9d71f153cb9330e307b1b054be01971a1bd164fb9bd4190d7f57989c2d6b86e8", size = 20165481 }, + { url = "https://files.pythonhosted.org/packages/b6/90/038a64abcbe5f991468f057bd21bead84a5c39d9b0409b652893263a47b4/wandb-0.19.8-py3-none-win_amd64.whl", hash = "sha256:f7da8e6fc6693014c72fb7db3ecd5e1116066198d2aca96f6eb7220cea03081c", size = 20165486 }, +] + +[[package]] +name = "watchfiles" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/02/22fcaed0396730b0d362bc8d1ffb3be2658fd473eecbb2ba84243e157f11/watchfiles-1.0.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ba5bb3073d9db37c64520681dd2650f8bd40902d991e7b4cfaeece3e32561d08", size = 395212 }, + { url = "https://files.pythonhosted.org/packages/e9/3d/ec5a2369a46edf3ebe092c39d9ae48e8cb6dacbde51c4b4f98936c524269/watchfiles-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f25d0ba0fe2b6d2c921cf587b2bf4c451860086534f40c384329fb96e2044d1", size = 384815 }, + { url = "https://files.pythonhosted.org/packages/df/b4/898991cececbe171e67142c31905510203649569d9817848f47c4177ee42/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47eb32ef8c729dbc4f4273baece89398a4d4b5d21a1493efea77a17059f4df8a", size = 450680 }, + { url = "https://files.pythonhosted.org/packages/58/f7/d4aa3000e812cfb5e5c2c6c0a3ec9d0a46a42489a8727edd160631c4e210/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:076f293100db3b0b634514aa0d294b941daa85fc777f9c698adb1009e5aca0b1", size = 455923 }, + { url = "https://files.pythonhosted.org/packages/dd/95/7e2e4c6aba1b02fb5c76d2f6a450b85215921ec5f8f7ad5efd075369563f/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eacd91daeb5158c598fe22d7ce66d60878b6294a86477a4715154990394c9b3", size = 482339 }, + { url = "https://files.pythonhosted.org/packages/bb/67/4265b0fabcc2ef2c9e3e8802ba7908cf718a357ebfb49c72e53787156a48/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13c2ce7b72026cfbca120d652f02c7750f33b4c9395d79c9790b27f014c8a5a2", size = 519908 }, + { url = "https://files.pythonhosted.org/packages/0d/96/b57802d5f8164bdf070befb4fd3dec4edba5a364ec0670965a97eb8098ce/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90192cdc15ab7254caa7765a98132a5a41471cf739513cc9bcf7d2ffcc0ec7b2", size = 501410 }, + { url = "https://files.pythonhosted.org/packages/8b/18/6db0de4e8911ba14e31853201b40c0fa9fea5ecf3feb86b0ad58f006dfc3/watchfiles-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278aaa395f405972e9f523bd786ed59dfb61e4b827856be46a42130605fd0899", size = 452876 }, + { url = "https://files.pythonhosted.org/packages/df/df/092a961815edf723a38ba2638c49491365943919c3526cc9cf82c42786a6/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a462490e75e466edbb9fc4cd679b62187153b3ba804868452ef0577ec958f5ff", size = 615353 }, + { url = "https://files.pythonhosted.org/packages/f3/cf/b85fe645de4ff82f3f436c5e9032379fce37c303f6396a18f9726cc34519/watchfiles-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8d0d0630930f5cd5af929040e0778cf676a46775753e442a3f60511f2409f48f", size = 613187 }, + { url = "https://files.pythonhosted.org/packages/f6/d4/a9fea27aef4dd69689bc3556718c1157a7accb72aa035ece87c1fa8483b5/watchfiles-1.0.4-cp310-cp310-win32.whl", hash = "sha256:cc27a65069bcabac4552f34fd2dce923ce3fcde0721a16e4fb1b466d63ec831f", size = 270799 }, + { url = "https://files.pythonhosted.org/packages/df/02/dbe9d4439f15dd4ad0720b6e039bde9d66d1f830331f34c18eb70fa6608e/watchfiles-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:8b1f135238e75d075359cf506b27bf3f4ca12029c47d3e769d8593a2024ce161", size = 284145 }, + { url = "https://files.pythonhosted.org/packages/6f/06/175d5ac6b838fb319008c0cd981d7bf289317c510154d411d3584ca2b67b/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdcc92daeae268de1acf5b7befcd6cfffd9a047098199056c72e4623f531de18", size = 396269 }, + { url = "https://files.pythonhosted.org/packages/86/ee/5db93b0b57dc0587abdbac4149296ee73275f615d790a82cb5598af0557f/watchfiles-1.0.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8d3d9203705b5797f0af7e7e5baa17c8588030aaadb7f6a86107b7247303817", size = 386010 }, + { url = "https://files.pythonhosted.org/packages/75/61/fe0dc5fedf152bfc085a53711f740701f6bdb8ab6b5c950402b681d4858b/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdef5a1be32d0b07dcea3318a0be95d42c98ece24177820226b56276e06b63b0", size = 450913 }, + { url = "https://files.pythonhosted.org/packages/9f/dd/3c7731af3baf1a9957afc643d176f94480921a690ec3237c9f9d11301c08/watchfiles-1.0.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342622287b5604ddf0ed2d085f3a589099c9ae8b7331df3ae9845571586c4f3d", size = 453474 }, +] + +[[package]] +name = "weave" +version = "0.51.39" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "diskcache" }, + { name = "emoji" }, + { name = "gql", extra = ["aiohttp", "requests"] }, + { name = "jsonschema" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "rich" }, + { name = "tenacity" }, + { name = "uuid-utils" }, + { name = "wandb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/89/44633cc13c25aeec0edfddf65c4c439ca6d85d6cfc79656afdaabd2fd9f9/weave-0.51.39.tar.gz", hash = "sha256:011e0886edcd50e3ae36256ede78ca51d69e37417313e228cb673947cbecd3c8", size = 325182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/05/43dbe7cd40fe2b7efb5b7c02afa49e4009c8cf4f48209e9c18567a5a885b/weave-0.51.39-py3-none-any.whl", hash = "sha256:1d6778556998fc6d6f62b7aba6a9a4a201adcfbdf18c2b5e11ec50180186cbcd", size = 417269 }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083 }, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970 }, + { url = "https://files.pythonhosted.org/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801 }, + { url = "https://files.pythonhosted.org/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927 }, + { url = "https://files.pythonhosted.org/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360 }, + { url = "https://files.pythonhosted.org/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528 }, + { url = "https://files.pythonhosted.org/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149 }, + { url = "https://files.pythonhosted.org/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703 }, + { url = "https://files.pythonhosted.org/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255 }, + { url = "https://files.pythonhosted.org/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744 }, + { url = "https://files.pythonhosted.org/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115 }, + { url = "https://files.pythonhosted.org/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247 }, + { url = "https://files.pythonhosted.org/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419 }, + { url = "https://files.pythonhosted.org/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003 }, + { url = "https://files.pythonhosted.org/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773 }, + { url = "https://files.pythonhosted.org/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732 }, + { url = "https://files.pythonhosted.org/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214 }, + { url = "https://files.pythonhosted.org/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020 }, + { url = "https://files.pythonhosted.org/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515 }, + { url = "https://files.pythonhosted.org/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064 }, +] + +[[package]] +name = "yarl" +version = "1.18.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/4b94a8e6d2b51b599516a5cb88e5bc99b4d8d4583e468057eaa29d5f0918/yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1", size = 181062 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/98/e005bc608765a8a5569f58e650961314873c8469c333616eb40bff19ae97/yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34", size = 141458 }, + { url = "https://files.pythonhosted.org/packages/df/5d/f8106b263b8ae8a866b46d9be869ac01f9b3fb7f2325f3ecb3df8003f796/yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7", size = 94365 }, + { url = "https://files.pythonhosted.org/packages/56/3e/d8637ddb9ba69bf851f765a3ee288676f7cf64fb3be13760c18cbc9d10bd/yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed", size = 92181 }, + { url = "https://files.pythonhosted.org/packages/76/f9/d616a5c2daae281171de10fba41e1c0e2d8207166fc3547252f7d469b4e1/yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde", size = 315349 }, + { url = "https://files.pythonhosted.org/packages/bb/b4/3ea5e7b6f08f698b3769a06054783e434f6d59857181b5c4e145de83f59b/yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b", size = 330494 }, + { url = "https://files.pythonhosted.org/packages/55/f1/e0fc810554877b1b67420568afff51b967baed5b53bcc983ab164eebf9c9/yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5", size = 326927 }, + { url = "https://files.pythonhosted.org/packages/a9/42/b1753949b327b36f210899f2dd0a0947c0c74e42a32de3f8eb5c7d93edca/yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc", size = 319703 }, + { url = "https://files.pythonhosted.org/packages/f0/6d/e87c62dc9635daefb064b56f5c97df55a2e9cc947a2b3afd4fd2f3b841c7/yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd", size = 310246 }, + { url = "https://files.pythonhosted.org/packages/e3/ef/e2e8d1785cdcbd986f7622d7f0098205f3644546da7919c24b95790ec65a/yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990", size = 319730 }, + { url = "https://files.pythonhosted.org/packages/fc/15/8723e22345bc160dfde68c4b3ae8b236e868f9963c74015f1bc8a614101c/yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db", size = 321681 }, + { url = "https://files.pythonhosted.org/packages/86/09/bf764e974f1516efa0ae2801494a5951e959f1610dd41edbfc07e5e0f978/yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62", size = 324812 }, + { url = "https://files.pythonhosted.org/packages/f6/4c/20a0187e3b903c97d857cf0272d687c1b08b03438968ae8ffc50fe78b0d6/yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760", size = 337011 }, + { url = "https://files.pythonhosted.org/packages/c9/71/6244599a6e1cc4c9f73254a627234e0dad3883ece40cc33dce6265977461/yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b", size = 338132 }, + { url = "https://files.pythonhosted.org/packages/af/f5/e0c3efaf74566c4b4a41cb76d27097df424052a064216beccae8d303c90f/yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690", size = 331849 }, + { url = "https://files.pythonhosted.org/packages/8a/b8/3d16209c2014c2f98a8f658850a57b716efb97930aebf1ca0d9325933731/yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6", size = 84309 }, + { url = "https://files.pythonhosted.org/packages/fd/b7/2e9a5b18eb0fe24c3a0e8bae994e812ed9852ab4fd067c0107fadde0d5f0/yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8", size = 90484 }, + { url = "https://files.pythonhosted.org/packages/f5/4b/a06e0ec3d155924f77835ed2d167ebd3b211a7b0853da1cf8d8414d784ef/yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b", size = 45109 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] From 34fb7039134b444268e2bb68662589a05fd99b6f Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 13:14:37 +0200 Subject: [PATCH 07/17] vu setup --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 6fa12cd0..4680fa03 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,19 @@ pip install -r requirements.txt pip install -e . ``` +### Alternative setup using `uv` +You can also use `uv` as a faster alternative to conda and pip: + +``` +# Install a Python environment using uv +uv python install 3.10 + +# Create a virtual environment and install dependencies +uv venv +source .venv/bin/activate +uv pip install -e . +``` + To call LLM API providers, set your `{INFERENCE_SERVER_PROVIDER}_API_KEY` API key. Running and profiling kernels require a GPU. From c5e1250d598326c269224fa63bd8963f6564a700 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 15:39:20 +0200 Subject: [PATCH 08/17] rework imports, build package correctly --- curl.sh | 6 + pyproject.toml | 9 +- scripts/benchmark_eval_analysis.py | 2 +- scripts/debug_stddout.py | 9 +- scripts/eval_from_generations.py | 15 +- scripts/generate_and_eval_single_sample.py | 9 +- .../generate_and_eval_single_sample_modal.py | 8 +- scripts/generate_baseline_time.py | 7 +- scripts/generate_baseline_time_modal.py | 8 +- scripts/generate_samples.py | 16 +- scripts/inspect_baseline.py | 16 +- scripts/inspect_kernel_pytorch_profiler.py | 23 +- scripts/inspect_triton.py | 25 +- scripts/run_and_check.py | 20 +- scripts/run_and_check_modal.py | 34 +-- scripts/server_requirements.txt | 7 +- scripts/server_run_and_check.py | 13 +- scripts/server_run_and_check_modal.py | 65 +----- scripts/verify_bench.py | 9 +- scripts/verify_generation.py | 5 +- src/{ => kernelbench}/__init__.py | 0 src/{ => kernelbench}/analysis.py | 5 +- src/{ => kernelbench}/compile.py | 17 +- src/{ => kernelbench}/dataset.py | 0 src/{ => kernelbench}/eval.py | 20 +- src/{ => kernelbench}/frameworks.py | 21 +- src/{utils.py => kernelbench/llm_utils.py} | 220 +----------------- src/{ => kernelbench}/make_hf_dataset.py | 0 src/{ => kernelbench}/prompt_constructor.py | 2 +- src/{ => kernelbench}/prompts/README.md | 0 .../prompts/cot/model_cot_fuse_gelu.py | 0 .../prompts/cot/model_cot_mnist2.py | 0 .../prompts/cot/model_cot_tiled_matmul.py | 0 .../prompts/few_shot/model_ex_add.py | 0 .../prompts/few_shot/model_ex_flash_attn.py | 0 .../prompts/few_shot/model_ex_fuse_gelu.py | 0 .../prompts/few_shot/model_ex_mnist2.py | 0 .../prompts/few_shot/model_ex_tiled_matmul.py | 0 .../prompts/few_shot/model_new_ex_add.py | 0 .../few_shot/model_new_ex_flash_attn.py | 0 .../few_shot/model_new_ex_fuse_gelu.py | 0 .../prompts/few_shot/model_new_ex_mnist2.py | 0 .../few_shot/model_new_ex_tiled_matmul.py | 0 .../prompts/hardware/gpu_specs.py | 0 src/{ => kernelbench}/prompts/model_ex_0.py | 0 src/{ => kernelbench}/prompts/model_ex_1.py | 0 src/{ => kernelbench}/prompts/model_ex_2.py | 0 src/{ => kernelbench}/prompts/model_ex_add.py | 0 .../prompts/model_new_ex_0.py | 0 .../prompts/model_new_ex_1.py | 0 .../prompts/model_new_ex_2.py | 0 .../prompts/model_new_ex_add.py | 0 src/{ => kernelbench}/score.py | 0 .../unit_tests/test_dataset.py | 0 .../unit_tests/test_score.py | 0 .../unit_tests/test_utils.py | 0 src/kernelbench/utils.py | 206 ++++++++++++++++ src/scratch/log.txt | 60 ----- src/scratch/model.py | 0 src/scratch/model_new.py | 0 src/scratch/prompt.txt | 137 ----------- src/scratch/test.py | 69 ------ 62 files changed, 358 insertions(+), 705 deletions(-) create mode 100644 curl.sh rename src/{ => kernelbench}/__init__.py (100%) rename src/{ => kernelbench}/analysis.py (97%) rename src/{ => kernelbench}/compile.py (98%) rename src/{ => kernelbench}/dataset.py (100%) rename src/{ => kernelbench}/eval.py (99%) rename src/{ => kernelbench}/frameworks.py (91%) rename src/{utils.py => kernelbench/llm_utils.py} (67%) rename src/{ => kernelbench}/make_hf_dataset.py (100%) rename src/{ => kernelbench}/prompt_constructor.py (99%) rename src/{ => kernelbench}/prompts/README.md (100%) rename src/{ => kernelbench}/prompts/cot/model_cot_fuse_gelu.py (100%) rename src/{ => kernelbench}/prompts/cot/model_cot_mnist2.py (100%) rename src/{ => kernelbench}/prompts/cot/model_cot_tiled_matmul.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_ex_add.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_ex_flash_attn.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_ex_fuse_gelu.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_ex_mnist2.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_ex_tiled_matmul.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_new_ex_add.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_new_ex_flash_attn.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_new_ex_fuse_gelu.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_new_ex_mnist2.py (100%) rename src/{ => kernelbench}/prompts/few_shot/model_new_ex_tiled_matmul.py (100%) rename src/{ => kernelbench}/prompts/hardware/gpu_specs.py (100%) rename src/{ => kernelbench}/prompts/model_ex_0.py (100%) rename src/{ => kernelbench}/prompts/model_ex_1.py (100%) rename src/{ => kernelbench}/prompts/model_ex_2.py (100%) rename src/{ => kernelbench}/prompts/model_ex_add.py (100%) rename src/{ => kernelbench}/prompts/model_new_ex_0.py (100%) rename src/{ => kernelbench}/prompts/model_new_ex_1.py (100%) rename src/{ => kernelbench}/prompts/model_new_ex_2.py (100%) rename src/{ => kernelbench}/prompts/model_new_ex_add.py (100%) rename src/{ => kernelbench}/score.py (100%) rename src/{ => kernelbench}/unit_tests/test_dataset.py (100%) rename src/{ => kernelbench}/unit_tests/test_score.py (100%) rename src/{ => kernelbench}/unit_tests/test_utils.py (100%) create mode 100644 src/kernelbench/utils.py delete mode 100644 src/scratch/log.txt delete mode 100644 src/scratch/model.py delete mode 100644 src/scratch/model_new.py delete mode 100644 src/scratch/prompt.txt delete mode 100644 src/scratch/test.py diff --git a/curl.sh b/curl.sh new file mode 100644 index 00000000..df45bbfa --- /dev/null +++ b/curl.sh @@ -0,0 +1,6 @@ +curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" \ + -F "ref_file=@src/prompts/model_ex_1.py" \ + -F "kernel_file=@src/prompts/model_new_ex_1.py" \ + -F "num_correct_trials=5" \ + -F "num_perf_trials=100" \ + -F "verbose=false" | python -m json.tool \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 818aa60b..861b2f20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,10 +24,11 @@ dependencies = [ ] -[tool.setuptools] -packages = ["src", "scripts"] + [tool.setuptools] + package-dir = {"" = "src"} + packages = ["kernelbench"] -[dependency-groups] +[project.optional-dependencies] dev = [ "weave>=0.51.39", -] +] \ No newline at end of file diff --git a/scripts/benchmark_eval_analysis.py b/scripts/benchmark_eval_analysis.py index fe2d220c..eb0b96f0 100644 --- a/scripts/benchmark_eval_analysis.py +++ b/scripts/benchmark_eval_analysis.py @@ -2,7 +2,7 @@ from tabulate import tabulate import pydra from pydra import REQUIRED, Config -from src.dataset import construct_kernelbench_dataset +from kernelbench.dataset import construct_kernelbench_dataset """ Benchmark Eval Analysis diff --git a/scripts/debug_stddout.py b/scripts/debug_stddout.py index b3eb81f0..99bffa58 100644 --- a/scripts/debug_stddout.py +++ b/scripts/debug_stddout.py @@ -1,10 +1,5 @@ -# from src.eval import build_compile_cache_with_capturing -import subprocess -import os -import ninja - -from src.utils import set_gpu_arch -from src.eval import build_compile_cache_with_capturing +from kernelbench.utils import set_gpu_arch +from kernelbench.eval import build_compile_cache_with_capturing ################################################################################ # Test for checking if we can capture nvcc errors diff --git a/scripts/eval_from_generations.py b/scripts/eval_from_generations.py index 82913fce..c8094886 100644 --- a/scripts/eval_from_generations.py +++ b/scripts/eval_from_generations.py @@ -6,7 +6,6 @@ import json from tqdm import tqdm -from src import eval, utils, compile import torch import os import multiprocessing as mp @@ -14,9 +13,15 @@ from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import build_compile_cache, eval_kernel_against_ref, KernelExecResult, check_metadata_serializable_all_types -from src.utils import set_gpu_arch, read_file +from kernelbench.compile import batch_compile +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.eval import ( + build_compile_cache, + eval_kernel_against_ref, + KernelExecResult, + check_metadata_serializable_all_types +) +from kernelbench.utils import set_gpu_arch, read_file """ Batch Evaluation from Existing Generations @@ -425,7 +430,7 @@ def main(config: EvalConfig): print(f"Start evaluation on {len(total_work)} unevaluated samples in range: {problem_id_range}") # Build Cache on CPU as that is faster if config.build_cache: - compile.batch_compile(total_work, config.to_dict()) + batch_compile(total_work, config.to_dict()) # Batch Eval on multiple GPUs in parallel batch_eval(total_work, config, curr_level_dataset, run_dir, eval_file_path) diff --git a/scripts/generate_and_eval_single_sample.py b/scripts/generate_and_eval_single_sample.py index 3fdb14b5..e540f331 100644 --- a/scripts/generate_and_eval_single_sample.py +++ b/scripts/generate_and_eval_single_sample.py @@ -6,10 +6,11 @@ from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import eval_kernel_against_ref -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, query_server, set_gpu_arch, read_file, create_inference_server_from_presets +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.eval import eval_kernel_against_ref +from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template +from kernelbench.utils import extract_first_code, set_gpu_arch, read_file +from kernelbench.frameworks import create_inference_server_from_presets """ Generate and evaluate a single sample diff --git a/scripts/generate_and_eval_single_sample_modal.py b/scripts/generate_and_eval_single_sample_modal.py index e4a31233..5ac15cc8 100644 --- a/scripts/generate_and_eval_single_sample_modal.py +++ b/scripts/generate_and_eval_single_sample_modal.py @@ -27,6 +27,7 @@ class EvalConfig(Config): def __init__(self): + self.weave_project = "generate_and_eval_single_sample_modal" self.dataset_src = REQUIRED # either huggingface or local @@ -49,8 +50,8 @@ def __init__(self): # Inference config - self.server_type = "deepseek" - self.model_name = "deepseek-coder" + self.server_type = "anthropic" + self.model_name = "claude-3-5-sonnet-20241022" self.max_tokens = 4096 self.temperature = 0.0 @@ -123,6 +124,9 @@ def main(config: EvalConfig): """ Keep it simple: Generate and evaluate a single sample """ + + import weave + weave.init(config.weave_project) print(f"Starting Eval with config: {config}") # Configurations diff --git a/scripts/generate_baseline_time.py b/scripts/generate_baseline_time.py index 8846ab56..186780dc 100644 --- a/scripts/generate_baseline_time.py +++ b/scripts/generate_baseline_time.py @@ -1,14 +1,13 @@ import torch import numpy as np -from src.eval import ( +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, - fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -from src.utils import read_file +from kernelbench.dataset import construct_problem_dataset_from_problem_dir +from kernelbench.utils import read_file import os import json from tqdm import tqdm diff --git a/scripts/generate_baseline_time_modal.py b/scripts/generate_baseline_time_modal.py index 240d6c7b..6d05244f 100644 --- a/scripts/generate_baseline_time_modal.py +++ b/scripts/generate_baseline_time_modal.py @@ -1,20 +1,18 @@ import torch import numpy as np -from src.eval import ( +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, - fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -from src.utils import read_file +from kernelbench.dataset import construct_problem_dataset_from_problem_dir +from kernelbench.utils import read_file import os import json from tqdm import tqdm import multiprocessing as mp import time -import einops """ Generate baseline time for KernelBench diff --git a/scripts/generate_samples.py b/scripts/generate_samples.py index 0d552b8b..2f4d24fb 100644 --- a/scripts/generate_samples.py +++ b/scripts/generate_samples.py @@ -1,17 +1,15 @@ +import os +from dataclasses import dataclass + import pydra from pydra import REQUIRED, Config -import os, sys import torch -import json -from dataclasses import dataclass - - from datasets import load_dataset -from src.dataset import construct_kernelbench_dataset -from src.eval import eval_kernel_against_ref -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, set_gpu_arch, read_file, create_inference_server_from_presets, maybe_multithread +from kernelbench.dataset import construct_kernelbench_dataset +from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template +from kernelbench.utils import read_file, maybe_multithread +from kernelbench.llm_utils import create_inference_server_from_presets """ Batch Generate Samples for Particular Level diff --git a/scripts/inspect_baseline.py b/scripts/inspect_baseline.py index e7811f64..111303d3 100644 --- a/scripts/inspect_baseline.py +++ b/scripts/inspect_baseline.py @@ -1,19 +1,15 @@ -import torch -import logging import os -import sys +import logging + import numpy as np -from src.eval import ( +import torch + +from kernelbench.eval import ( load_original_model_and_inputs, - time_execution_with_cuda_event, - get_timing_stats, set_seed, fetch_ref_arch_from_problem_id, ) -from src.dataset import construct_problem_dataset_from_problem_dir -import os, sys -import logging -import json +from kernelbench.dataset import construct_problem_dataset_from_problem_dir device = torch.device("cuda:0") diff --git a/scripts/inspect_kernel_pytorch_profiler.py b/scripts/inspect_kernel_pytorch_profiler.py index 3e93637c..c5023fa0 100644 --- a/scripts/inspect_kernel_pytorch_profiler.py +++ b/scripts/inspect_kernel_pytorch_profiler.py @@ -1,29 +1,28 @@ -import torch -from torch.profiler import profile, record_function, ProfilerActivity -import logging -import os -import io - - """ For analysis Inspect the operator and kernel breakdown of model-generated kernel to a particular problem Using PyTorch Profiler """ -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -device = "cuda:0" +import os +import io +import logging +import torch +from torch.profiler import profile, ProfilerActivity -from src.utils import read_file -from src.eval import ( +from kernelbench.utils import read_file +from kernelbench.eval import ( load_custom_model, load_original_model_and_inputs, set_seed, ) +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +device = "cuda:0" + def get_torch_profiler_info(ref_arch_src: str, kernel_src: str, build_dir: str, diff --git a/scripts/inspect_triton.py b/scripts/inspect_triton.py index 4f13c8af..9f30a14e 100644 --- a/scripts/inspect_triton.py +++ b/scripts/inspect_triton.py @@ -1,10 +1,3 @@ -import torch -from torch.profiler import profile, record_function, ProfilerActivity -import logging -import os -import io - - """ [WIP] For debugging and analysis Inspect torch compile generated triton code @@ -12,20 +5,26 @@ using PyTorch Profiler """ -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -device = "cuda:0" +import os +import io +import logging +import torch +from torch.profiler import profile, record_function, ProfilerActivity -from src.utils import read_file -from src.eval import ( - load_custom_model, +from kernelbench.utils import read_file +from kernelbench.eval import ( load_original_model_and_inputs, time_execution_with_cuda_event, get_timing_stats, set_seed, ) +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +device = "cuda:0" + + def fetch_ref_arch_from_dataset(dataset: list[str], problem_id: int) -> tuple[str, str, str]: """ diff --git a/scripts/run_and_check.py b/scripts/run_and_check.py index 79c00a7e..539bb623 100644 --- a/scripts/run_and_check.py +++ b/scripts/run_and_check.py @@ -1,17 +1,15 @@ +import os import shutil + import torch import pydra from pydra import REQUIRED, Config -import os from datasets import load_dataset - -from src import eval as kernel_eval -from src import utils as kernel_utils +from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.utils import read_file, set_gpu_arch from scripts.generate_baseline_time import measure_program_time -from src.utils import read_file - """ Run a pair of KernelBench format (problem, solution) to check if solution is correct and compute speedup @@ -72,7 +70,7 @@ def __init__(self): def __repr__(self): return f"ScriptConfig({self.to_dict()})" -def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> kernel_eval.KernelExecResult: +def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: """ Evaluate a single sample source code against a reference source code """ @@ -89,7 +87,7 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict verbose = configs["verbose"] measure_performance = configs["measure_performance"] try: - eval_result = kernel_eval.eval_kernel_against_ref( + eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=measure_performance, @@ -108,7 +106,7 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict "hardware": torch.cuda.get_device_name(device=device), "device": str(device) } - eval_result = kernel_eval.KernelExecResult(compiled=False, correctness=False, + eval_result = KernelExecResult(compiled=False, correctness=False, metadata=metadata) return eval_result else: @@ -116,7 +114,7 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict "hardware": torch.cuda.get_device_name(device=device), "device": str(device) } - eval_result = kernel_eval.KernelExecResult(compiled=False, correctness=False, + eval_result = KernelExecResult(compiled=False, correctness=False, metadata=metadata) return eval_result @@ -160,7 +158,7 @@ def main(config: ScriptConfig): # Start Evaluation device = torch.device("cuda:0") # default device - kernel_utils.set_gpu_arch(config.gpu_arch) + set_gpu_arch(config.gpu_arch) print("[INFO] Evaluating kernel against reference code") # Evaluate kernel against reference code diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py index 3d1d57aa..1a411cab 100644 --- a/scripts/run_and_check_modal.py +++ b/scripts/run_and_check_modal.py @@ -1,15 +1,22 @@ -import torch -import pydra -from pydra import REQUIRED, Config import os import shutil +import importlib.util +import sys +import os +import tempfile + import modal +import pydra +import torch import numpy as np -from src import eval as kernel_eval -from src.utils import read_file +from pydra import REQUIRED, Config + + +from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.utils import read_file, set_gpu_arch -def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> kernel_eval.KernelExecResult: +def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: """Evaluate a single sample source code against a reference source code""" kernel_hash = str(hash(kernel_src)) build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) @@ -19,7 +26,7 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict shutil.rmtree(build_dir, ignore_errors=True) try: - eval_result = kernel_eval.eval_kernel_against_ref( + eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=configs["measure_performance"], @@ -42,7 +49,7 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict "hardware": torch.cuda.get_device_name(device=device), "device": str(device) } - return kernel_eval.KernelExecResult(compiled=False, correctness=False, metadata=metadata) + return KernelExecResult(compiled=False, correctness=False, metadata=metadata) """ Run a pair of (reference, solution) to check if solution is correct and compute speedup using Modal @@ -118,17 +125,10 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, use_torch_compile=False, torch_compile_backend=None, torch_compile_options=None, gpu_arch=None): """Measure the execution time of a reference program""" - import torch - import numpy as np - import importlib.util - import sys - import os - import tempfile - from src import utils as kernel_utils # Setup if gpu_arch: - kernel_utils.set_gpu_arch(gpu_arch) + set_gpu_arch(gpu_arch) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # Create temporary module @@ -250,7 +250,7 @@ def main(config: ScriptConfig): ) # Convert dict back to KernelExecResult object - kernel_eval_result = kernel_eval.KernelExecResult( + kernel_eval_result = KernelExecResult( compiled=kernel_eval_result_dict["compiled"], correctness=kernel_eval_result_dict["correctness"], runtime=kernel_eval_result_dict["runtime"], diff --git a/scripts/server_requirements.txt b/scripts/server_requirements.txt index e2af6b82..f46d8e67 100644 --- a/scripts/server_requirements.txt +++ b/scripts/server_requirements.txt @@ -1,13 +1,8 @@ -anthropic modal numpy -openai -packaging pydra_config -torch +torch==2.5.0 triton -torchvision -torchaudio tqdm datasets transformers diff --git a/scripts/server_run_and_check.py b/scripts/server_run_and_check.py index 9fd575b9..150dd51f 100644 --- a/scripts/server_run_and_check.py +++ b/scripts/server_run_and_check.py @@ -1,17 +1,18 @@ -import fastapi -import uvicorn -import tempfile import os import shutil +import tempfile +from typing import Optional, Dict, Any, List + +import fastapi +import uvicorn +import torch from fastapi import UploadFile, File, HTTPException, status from pydantic import BaseModel -from typing import Optional, Dict, Any, List # Import the relevant modules directly from scripts.run_and_check import evaluate_single_sample_src from scripts.generate_baseline_time import measure_program_time -from src.utils import read_file, set_gpu_arch -import torch +from kernelbench.utils import read_file, set_gpu_arch # Define the response model class BenchmarkResult(BaseModel): diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py index 94cb99bb..384ee2fc 100644 --- a/scripts/server_run_and_check_modal.py +++ b/scripts/server_run_and_check_modal.py @@ -16,17 +16,9 @@ from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse -# Import project-specific modules -# Assuming 'src' is in the Python path -try: - from src import eval as kernel_eval - from src import utils as kernel_utils -except ImportError as e: - print(f"[ERROR] Failed to import project modules at startup: {e}") - # Decide how to handle this - exit, log, or proceed cautiously - kernel_eval = None - kernel_utils = None - + +from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.utils import set_gpu_arch # GPU architecture mapping @@ -90,15 +82,7 @@ class BenchmarkService: def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: """Evaluate a single sample source code against a reference source code""" - # Check if kernel_eval was imported successfully - if kernel_eval is None: - print("[ERROR] src.eval module not available.") - return KernelExecResult( - compiled=False, - correctness=False, - metadata={"import_error": "Failed to import src.eval at startup"} - ) - + try: print(f"[DEBUG] Python paths: {sys.path}") @@ -110,7 +94,7 @@ def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs shutil.rmtree(build_dir, ignore_errors=True) try: - eval_result = kernel_eval.eval_kernel_against_ref( + eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=configs["measure_performance"], @@ -160,20 +144,9 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, use_torch_compile=False, torch_compile_backend=None, torch_compile_options=None, gpu_arch=None): """Measure the execution time of a reference program""" - # Removed imports: torch, numpy, importlib.util, sys, os, tempfile, src.utils - - # Check if kernel_utils was imported successfully - if kernel_utils is None: - print("[ERROR] src.utils module not available.") - # Return an error structure or raise an exception - return { - "error": "src.utils module not available", - "mean": None, "std": None, "min": None, "max": None, "median": None - } - # Setup if gpu_arch: - kernel_utils.set_gpu_arch(gpu_arch) + set_gpu_arch(gpu_arch) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # Create temporary module @@ -280,14 +253,6 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, print(f"[DEBUG] Starting benchmark on GPU: {GPU}") start_time = time.time() - - # Check if kernel_utils was imported successfully - if kernel_utils is None: - print("[ERROR] src.utils module not available.") - return BenchmarkResult( - kernel_result=KernelExecResult(compiled=False, correctness=False), - error="src.utils module not available" - ) try: # Get GPU architecture @@ -295,7 +260,7 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, print(f"[DEBUG] Using GPU architecture: {gpu_arch}") # Set GPU architecture - kernel_utils.set_gpu_arch(gpu_arch) + set_gpu_arch(gpu_arch) # Default device device = torch.device("cuda:0") @@ -541,18 +506,10 @@ async def test_imports(): except Exception as e: result["imports"]["torch"] = {"error": f"Error checking torch: {str(e)}"} - # Verify src.eval import - if kernel_eval is not None: - result["imports"]["src.eval"] = {"success": True} - else: - result["imports"]["src.eval"] = {"error": "src.eval module failed to load at startup"} - - # Verify src.utils import - if kernel_utils is not None: - result["imports"]["src.utils"] = {"success": True} - else: - result["imports"]["src.utils"] = {"error": "src.utils module failed to load at startup"} - + + result["imports"]["src.eval"] = {"success": True} + result["imports"]["src.utils"] = {"success": True} + # Check for file existence result["files"] = { "static_local": os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static")), diff --git a/scripts/verify_bench.py b/scripts/verify_bench.py index 5fdc6862..d39733ee 100644 --- a/scripts/verify_bench.py +++ b/scripts/verify_bench.py @@ -6,15 +6,14 @@ Usage: python test_bench.py """ +import os import importlib -import torch -import torch.nn as nn -import torch.nn.functional as F import random -import numpy as np -import os import importlib.util +import torch +import numpy as np + """ Test all the reference architectures compiles and reproduce the same results when run against itself diff --git a/scripts/verify_generation.py b/scripts/verify_generation.py index c284d3b5..b1de1fc7 100644 --- a/scripts/verify_generation.py +++ b/scripts/verify_generation.py @@ -1,7 +1,8 @@ import sys, os -import src.utils as utils import time -from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template + +import kernelbench.utils as utils +from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template """ For testing infernece and quickly iterate on prompts diff --git a/src/__init__.py b/src/kernelbench/__init__.py similarity index 100% rename from src/__init__.py rename to src/kernelbench/__init__.py diff --git a/src/analysis.py b/src/kernelbench/analysis.py similarity index 97% rename from src/analysis.py rename to src/kernelbench/analysis.py index 5c621c54..6f3c7f4e 100644 --- a/src/analysis.py +++ b/src/kernelbench/analysis.py @@ -1,12 +1,9 @@ ################################################################################ # Helpers for Analysis ################################################################################ +import re import numpy as np - -from functools import cache from transformers import AutoTokenizer -import utils -import re def pass_at_k(n, c, k): diff --git a/src/compile.py b/src/kernelbench/compile.py similarity index 98% rename from src/compile.py rename to src/kernelbench/compile.py index 41fd8c3b..d2a5cc36 100644 --- a/src/compile.py +++ b/src/kernelbench/compile.py @@ -1,15 +1,14 @@ -from dataclasses import dataclass -import random +import multiprocessing as mp +import os +import shutil import time +from dataclasses import dataclass +import torch from tqdm import tqdm -import shutil -from src.eval import build_compile_cache -from src import utils as utils -import torch -import os -import multiprocessing as mp +from kernelbench.utils import set_gpu_arch +from kernelbench.eval import build_compile_cache """ Compile and Cache @@ -32,7 +31,7 @@ def compile_single_sample(work_args: WorkArgs, config: dict) -> tuple[bool, str] sample_id = work_args.sample_id verbose = config["verbose"] - utils.set_gpu_arch(config["gpu_arch"]) + set_gpu_arch(config["gpu_arch"]) build_dir = os.path.join(config["kernel_eval_build_dir"], config["run_name"], str(problem_id), str(sample_id)) diff --git a/src/dataset.py b/src/kernelbench/dataset.py similarity index 100% rename from src/dataset.py rename to src/kernelbench/dataset.py diff --git a/src/eval.py b/src/kernelbench/eval.py similarity index 99% rename from src/eval.py rename to src/kernelbench/eval.py index 4532154e..11419f84 100644 --- a/src/eval.py +++ b/src/kernelbench/eval.py @@ -2,19 +2,19 @@ Helpers for Evaluations """ +from contextlib import redirect_stdout, redirect_stderr +from io import StringIO +import json +import numpy as np +import os import requests +import subprocess import torch import torch.nn as nn -import os, subprocess from pydantic import BaseModel -import numpy as np -import random -import json -from contextlib import redirect_stdout, redirect_stderr -from io import StringIO -import sys -from . import utils +from kernelbench.utils import read_file +from kernelbench.dataset import construct_problem_dataset_from_problem_dir REPO_TOP_PATH = os.path.abspath( os.path.join( @@ -55,7 +55,7 @@ def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str if not os.path.exists(problem_path): raise FileNotFoundError(f"Problem file at {problem_path} does not exist.") - ref_arch = utils.read_file(problem_path) + ref_arch = read_file(problem_path) if not with_name: return ref_arch else: @@ -64,7 +64,7 @@ def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str def fetch_ref_arch_from_level_problem_id(level, problem_id, with_name=False): PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) - dataset = utils.construct_problem_dataset_from_problem_dir(PROBLEM_DIR) + dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) diff --git a/src/frameworks.py b/src/kernelbench/frameworks.py similarity index 91% rename from src/frameworks.py rename to src/kernelbench/frameworks.py index cb3795f9..24d0f8ac 100644 --- a/src/frameworks.py +++ b/src/kernelbench/frameworks.py @@ -5,17 +5,9 @@ # See how we added Archon support as an example ######################## -import multiprocessing -import subprocess -import re -import random -import tempfile -from pathlib import Path -import re -import math import os import json -from tqdm import tqdm +import time # API clients from archon.completions import Archon @@ -25,17 +17,6 @@ load_dotenv() # from datasets import load_dataset -import numpy as np -from contextlib import contextmanager -from collections import defaultdict -import time -import shutil -import concurrent -from functools import cache -from transformers import AutoTokenizer -import hashlib - -from concurrent.futures import ProcessPoolExecutor, as_completed # Define API key access TOGETHER_KEY = os.environ.get("TOGETHER_API_KEY") diff --git a/src/utils.py b/src/kernelbench/llm_utils.py similarity index 67% rename from src/utils.py rename to src/kernelbench/llm_utils.py index c56a70af..f0162d6c 100644 --- a/src/utils.py +++ b/src/kernelbench/llm_utils.py @@ -1,18 +1,8 @@ ######################## -# Utils Functions +# API LLM Utils Functions ######################## -import multiprocessing -import subprocess -import re -import random -import tempfile -from pathlib import Path -import re -import math import os -import json -from tqdm import tqdm # API clients from together import Together @@ -21,17 +11,10 @@ import anthropic # from datasets import load_dataset -import numpy as np -from contextlib import contextmanager -from collections import defaultdict import time -import shutil -import concurrent from functools import cache from transformers import AutoTokenizer -import hashlib -from concurrent.futures import ProcessPoolExecutor, as_completed # Define API key access TOGETHER_KEY = os.environ.get("TOGETHER_API_KEY") @@ -70,17 +53,6 @@ def is_safe_to_send_to_deepseek(prompt): else: return len(tokenizer.apply_chat_template(prompt)) < TOO_LONG_FOR_DEEPSEEK -def set_gpu_arch(arch_list: list[str]): - """ - Set env variable for torch cuda arch list to build kernels for specified architectures - """ - valid_archs = ["Maxwell", "Pascal", "Volta", "Turing", "Ampere", "Hopper", "Ada"] - for arch in arch_list: - if arch not in valid_archs: - raise ValueError(f"Invalid architecture: {arch}. Must be one of {valid_archs}") - - os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list) - def query_server( prompt: str | list[dict], # string if normal prompt, list of dicts if chat prompt, system_prompt: str = "You are a helpful assistant", # only used for chat prompts @@ -430,192 +402,4 @@ def _query_llm(prompt: str | list[dict]): prompt, server_type=server_type, **server_args ) - return _query_llm - -""" -Model output processing -# TODO: add unit tests -""" - - -def read_file(file_path) -> str: - if not os.path.exists(file_path): - print(f"File {file_path} does not exist") - return "" - - try: - with open(file_path, "r") as file: - return file.read() - except Exception as e: - print(f"Error reading file {file_path}: {e}") - return "" - - -def print_messages(messages): - for message in messages: - print(message["role"]) - print(message["content"]) - print("-" * 50) - print("\n\n") - - -def extract_python_code(text): - """ - Extract python code from model output - """ - pattern = r"```python\n(.*?)```" - matches = re.findall(pattern, text, re.DOTALL) - return "\n".join(matches) if matches else "" - - -def remove_code_block_header(code, code_language_type): - """Assume input is code but just with like python, cpp, etc. at the top""" - if code.startswith(code_language_type): - code = code[len(code_language_type) :].strip() - return code - - -def extract_first_code(output_string: str, code_language_types: list[str]) -> str: - """ - Extract first code block from model output, specified by code_language_type - """ - trimmed = output_string.strip() - - # Extracting the first occurrence of content between backticks - code_match = re.search(r"```(.*?)```", trimmed, re.DOTALL) - - if code_match: - # Strip leading and trailing whitespace from the extracted code - code = code_match.group(1).strip() - - # depends on code_language_type: cpp, python, etc. - # sometimes the block of code is ```cpp ... ``` instead of ``` ... ``` - # in this case strip the cpp out - for code_type in code_language_types: - if code.startswith(code_type): - code = code[len(code_type) :].strip() - - return code - - return None - - -def extract_last_code(output_string: str, code_language_types: list[str]) -> str | None: - """ - Extract last code block from model output, specified by code_language_type - """ - trimmed = output_string.strip() - - # Find all matches of code blocks - code_matches = re.finditer(r"```(.*?)```", trimmed, re.DOTALL) - - # Get the last match by converting to list and taking the last element - matches_list = list(code_matches) - if matches_list: - last_match = matches_list[-1] - code = last_match.group(1).strip() - - # Remove language type headers - for code_type in code_language_types: - if code.startswith(code_type): - code = code[len(code_type):].strip() - - return code - - return None - -def extract_code_blocks(text, code_language_types: list[str]) -> str: - ''' - Extract all code blocks from text, combine them to return as a single string - ''' - pattern = r'```.*?\n(.*?)```' - matches = re.findall(pattern, text, re.DOTALL) - - # Combine all code blocks and remove language type headers - combined_code = [] - for match in matches: - code = match.strip() - # Remove any language type headers - for lang_type in code_language_types: - if code.startswith(lang_type): - code = code[len(lang_type):].strip() - combined_code.append(code) - - return " \n ".join(combined_code) if combined_code else "" - -################################################################################ -# Scale up experiments in parallel -################################################################################ - -def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs): - """ - Multithreaded execution of func, with optional time interval between queries - Ideal for querying LLM APIs, does not provide process isolation - """ - output_data = [] - if num_workers not in [1, None]: - with tqdm(total=len(instances), smoothing=0) as pbar: - with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor: - - # Submit tasks one at a time with delay between them - futures = [] - for instance in instances: - futures.append( - executor.submit( - func, - instance, - *shared_args, - **shared_kwargs - ) - ) - time.sleep(time_interval) # sleep between submitting each task - - - - # Wait for each future to complete - for future in concurrent.futures.as_completed(futures): - pbar.update(1) - try: - result = future.result() - if result is not None: - output_data.append(result) - except Exception as e: - print("Got an error!", e) - continue - else: - for instance in tqdm(instances): - output = func(instance, *shared_args, **shared_kwargs) - if output is not None: output_data.append(output) - - return output_data - - -def maybe_multiprocess_cuda( - func, instances, num_workers, *shared_args, **shared_kwargs -): - """ - From monkeys, but modified to work with CUDA - """ - output_data = [] - multiprocessing.set_start_method( - "spawn", force=True - ) # this is necessary for CUDA to work - - with tqdm(total=len(instances), smoothing=0) as pbar: - with ProcessPoolExecutor(max_workers=num_workers) as executor: - # Create a future for running each instance - futures = { - executor.submit(func, instance, *shared_args, **shared_kwargs): None - for instance in instances - } - # Wait for each future to complete - for future in as_completed(futures): - pbar.update(1) - try: - result = future.result() - if result is not None: - output_data.append(result) - except Exception as e: - print("Got an error!", e) - continue - return output_data + return _query_llm \ No newline at end of file diff --git a/src/make_hf_dataset.py b/src/kernelbench/make_hf_dataset.py similarity index 100% rename from src/make_hf_dataset.py rename to src/kernelbench/make_hf_dataset.py diff --git a/src/prompt_constructor.py b/src/kernelbench/prompt_constructor.py similarity index 99% rename from src/prompt_constructor.py rename to src/kernelbench/prompt_constructor.py index d85480fb..e2755612 100644 --- a/src/prompt_constructor.py +++ b/src/kernelbench/prompt_constructor.py @@ -1,6 +1,6 @@ import os import weave -from .utils import read_file +from kernelbench.utils import read_file """ diff --git a/src/prompts/README.md b/src/kernelbench/prompts/README.md similarity index 100% rename from src/prompts/README.md rename to src/kernelbench/prompts/README.md diff --git a/src/prompts/cot/model_cot_fuse_gelu.py b/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py similarity index 100% rename from src/prompts/cot/model_cot_fuse_gelu.py rename to src/kernelbench/prompts/cot/model_cot_fuse_gelu.py diff --git a/src/prompts/cot/model_cot_mnist2.py b/src/kernelbench/prompts/cot/model_cot_mnist2.py similarity index 100% rename from src/prompts/cot/model_cot_mnist2.py rename to src/kernelbench/prompts/cot/model_cot_mnist2.py diff --git a/src/prompts/cot/model_cot_tiled_matmul.py b/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py similarity index 100% rename from src/prompts/cot/model_cot_tiled_matmul.py rename to src/kernelbench/prompts/cot/model_cot_tiled_matmul.py diff --git a/src/prompts/few_shot/model_ex_add.py b/src/kernelbench/prompts/few_shot/model_ex_add.py similarity index 100% rename from src/prompts/few_shot/model_ex_add.py rename to src/kernelbench/prompts/few_shot/model_ex_add.py diff --git a/src/prompts/few_shot/model_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py similarity index 100% rename from src/prompts/few_shot/model_ex_flash_attn.py rename to src/kernelbench/prompts/few_shot/model_ex_flash_attn.py diff --git a/src/prompts/few_shot/model_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py similarity index 100% rename from src/prompts/few_shot/model_ex_fuse_gelu.py rename to src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py diff --git a/src/prompts/few_shot/model_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_ex_mnist2.py similarity index 100% rename from src/prompts/few_shot/model_ex_mnist2.py rename to src/kernelbench/prompts/few_shot/model_ex_mnist2.py diff --git a/src/prompts/few_shot/model_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py similarity index 100% rename from src/prompts/few_shot/model_ex_tiled_matmul.py rename to src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py diff --git a/src/prompts/few_shot/model_new_ex_add.py b/src/kernelbench/prompts/few_shot/model_new_ex_add.py similarity index 100% rename from src/prompts/few_shot/model_new_ex_add.py rename to src/kernelbench/prompts/few_shot/model_new_ex_add.py diff --git a/src/prompts/few_shot/model_new_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py similarity index 100% rename from src/prompts/few_shot/model_new_ex_flash_attn.py rename to src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py diff --git a/src/prompts/few_shot/model_new_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py similarity index 100% rename from src/prompts/few_shot/model_new_ex_fuse_gelu.py rename to src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py diff --git a/src/prompts/few_shot/model_new_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py similarity index 100% rename from src/prompts/few_shot/model_new_ex_mnist2.py rename to src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py diff --git a/src/prompts/few_shot/model_new_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py similarity index 100% rename from src/prompts/few_shot/model_new_ex_tiled_matmul.py rename to src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py diff --git a/src/prompts/hardware/gpu_specs.py b/src/kernelbench/prompts/hardware/gpu_specs.py similarity index 100% rename from src/prompts/hardware/gpu_specs.py rename to src/kernelbench/prompts/hardware/gpu_specs.py diff --git a/src/prompts/model_ex_0.py b/src/kernelbench/prompts/model_ex_0.py similarity index 100% rename from src/prompts/model_ex_0.py rename to src/kernelbench/prompts/model_ex_0.py diff --git a/src/prompts/model_ex_1.py b/src/kernelbench/prompts/model_ex_1.py similarity index 100% rename from src/prompts/model_ex_1.py rename to src/kernelbench/prompts/model_ex_1.py diff --git a/src/prompts/model_ex_2.py b/src/kernelbench/prompts/model_ex_2.py similarity index 100% rename from src/prompts/model_ex_2.py rename to src/kernelbench/prompts/model_ex_2.py diff --git a/src/prompts/model_ex_add.py b/src/kernelbench/prompts/model_ex_add.py similarity index 100% rename from src/prompts/model_ex_add.py rename to src/kernelbench/prompts/model_ex_add.py diff --git a/src/prompts/model_new_ex_0.py b/src/kernelbench/prompts/model_new_ex_0.py similarity index 100% rename from src/prompts/model_new_ex_0.py rename to src/kernelbench/prompts/model_new_ex_0.py diff --git a/src/prompts/model_new_ex_1.py b/src/kernelbench/prompts/model_new_ex_1.py similarity index 100% rename from src/prompts/model_new_ex_1.py rename to src/kernelbench/prompts/model_new_ex_1.py diff --git a/src/prompts/model_new_ex_2.py b/src/kernelbench/prompts/model_new_ex_2.py similarity index 100% rename from src/prompts/model_new_ex_2.py rename to src/kernelbench/prompts/model_new_ex_2.py diff --git a/src/prompts/model_new_ex_add.py b/src/kernelbench/prompts/model_new_ex_add.py similarity index 100% rename from src/prompts/model_new_ex_add.py rename to src/kernelbench/prompts/model_new_ex_add.py diff --git a/src/score.py b/src/kernelbench/score.py similarity index 100% rename from src/score.py rename to src/kernelbench/score.py diff --git a/src/unit_tests/test_dataset.py b/src/kernelbench/unit_tests/test_dataset.py similarity index 100% rename from src/unit_tests/test_dataset.py rename to src/kernelbench/unit_tests/test_dataset.py diff --git a/src/unit_tests/test_score.py b/src/kernelbench/unit_tests/test_score.py similarity index 100% rename from src/unit_tests/test_score.py rename to src/kernelbench/unit_tests/test_score.py diff --git a/src/unit_tests/test_utils.py b/src/kernelbench/unit_tests/test_utils.py similarity index 100% rename from src/unit_tests/test_utils.py rename to src/kernelbench/unit_tests/test_utils.py diff --git a/src/kernelbench/utils.py b/src/kernelbench/utils.py new file mode 100644 index 00000000..6be29997 --- /dev/null +++ b/src/kernelbench/utils.py @@ -0,0 +1,206 @@ +######################## +# Utils Functions +######################## + +import multiprocessing +import re +import os + +# from datasets import load_dataset +import time + +from concurrent.futures import ProcessPoolExecutor, as_completed, ThreadPoolExecutor + +def set_gpu_arch(arch_list: list[str]): + """ + Set env variable for torch cuda arch list to build kernels for specified architectures + """ + valid_archs = ["Maxwell", "Pascal", "Volta", "Turing", "Ampere", "Hopper", "Ada"] + for arch in arch_list: + if arch not in valid_archs: + raise ValueError(f"Invalid architecture: {arch}. Must be one of {valid_archs}") + + os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list) + + +def read_file(file_path: str) -> str: + if not os.path.exists(file_path): + print(f"File {file_path} does not exist") + return "" + + try: + with open(file_path, "r") as file: + return file.read() + except Exception as e: + print(f"Error reading file {file_path}: {e}") + return "" + + +def print_messages(messages): + for message in messages: + print(message["role"]) + print(message["content"]) + print("-" * 50) + print("\n\n") + + +def extract_python_code(text): + """ + Extract python code from model output + """ + pattern = r"```python\n(.*?)```" + matches = re.findall(pattern, text, re.DOTALL) + return "\n".join(matches) if matches else "" + + +def remove_code_block_header(code, code_language_type): + """Assume input is code but just with like python, cpp, etc. at the top""" + if code.startswith(code_language_type): + code = code[len(code_language_type) :].strip() + return code + + +def extract_first_code(output_string: str, code_language_types: list[str]) -> str: + """ + Extract first code block from model output, specified by code_language_type + """ + trimmed = output_string.strip() + + # Extracting the first occurrence of content between backticks + code_match = re.search(r"```(.*?)```", trimmed, re.DOTALL) + + if code_match: + # Strip leading and trailing whitespace from the extracted code + code = code_match.group(1).strip() + + # depends on code_language_type: cpp, python, etc. + # sometimes the block of code is ```cpp ... ``` instead of ``` ... ``` + # in this case strip the cpp out + for code_type in code_language_types: + if code.startswith(code_type): + code = code[len(code_type) :].strip() + + return code + + return None + + +def extract_last_code(output_string: str, code_language_types: list[str]) -> str | None: + """ + Extract last code block from model output, specified by code_language_type + """ + trimmed = output_string.strip() + + # Find all matches of code blocks + code_matches = re.finditer(r"```(.*?)```", trimmed, re.DOTALL) + + # Get the last match by converting to list and taking the last element + matches_list = list(code_matches) + if matches_list: + last_match = matches_list[-1] + code = last_match.group(1).strip() + + # Remove language type headers + for code_type in code_language_types: + if code.startswith(code_type): + code = code[len(code_type):].strip() + + return code + + return None + +def extract_code_blocks(text, code_language_types: list[str]) -> str: + ''' + Extract all code blocks from text, combine them to return as a single string + ''' + pattern = r'```.*?\n(.*?)```' + matches = re.findall(pattern, text, re.DOTALL) + + # Combine all code blocks and remove language type headers + combined_code = [] + for match in matches: + code = match.strip() + # Remove any language type headers + for lang_type in code_language_types: + if code.startswith(lang_type): + code = code[len(lang_type):].strip() + combined_code.append(code) + + return " \n ".join(combined_code) if combined_code else "" + +################################################################################ +# Scale up experiments in parallel +################################################################################ + +def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs): + """ + Multithreaded execution of func, with optional time interval between queries + Ideal for querying LLM APIs, does not provide process isolation + """ + output_data = [] + if num_workers not in [1, None]: + with tqdm(total=len(instances), smoothing=0) as pbar: + with ThreadPoolExecutor(max_workers=num_workers) as executor: + + # Submit tasks one at a time with delay between them + futures = [] + for instance in instances: + futures.append( + executor.submit( + func, + instance, + *shared_args, + **shared_kwargs + ) + ) + time.sleep(time_interval) # sleep between submitting each task + + + + # Wait for each future to complete + for future in as_completed(futures): + pbar.update(1) + try: + result = future.result() + if result is not None: + output_data.append(result) + except Exception as e: + print("Got an error!", e) + continue + else: + for instance in tqdm(instances): + output = func(instance, *shared_args, **shared_kwargs) + if output is not None: output_data.append(output) + + return output_data + + +def maybe_multiprocess_cuda( + func, instances, num_workers, *shared_args, **shared_kwargs +): + """ + From monkeys, but modified to work with CUDA + """ + output_data = [] + multiprocessing.set_start_method( + "spawn", force=True + ) # this is necessary for CUDA to work + + with tqdm(total=len(instances), smoothing=0) as pbar: + with ProcessPoolExecutor(max_workers=num_workers) as executor: + # Create a future for running each instance + futures = { + executor.submit(func, instance, *shared_args, **shared_kwargs): None + for instance in instances + } + # Wait for each future to complete + for future in as_completed(futures): + pbar.update(1) + try: + result = future.result() + if result is not None: + output_data.append(result) + except Exception as e: + print("Got an error!", e) + continue + return output_data diff --git a/src/scratch/log.txt b/src/scratch/log.txt deleted file mode 100644 index 9b231c5f..00000000 --- a/src/scratch/log.txt +++ /dev/null @@ -1,60 +0,0 @@ -[1/3] /usr/local/cuda-12.3/bin/nvcc --generate-dependencies-with-compile --dependency-output cuda.cuda.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_89,code=compute_89 -gencode=arch=compute_89,code=sm_89 --compiler-options '-fPIC' -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu -o cuda.cuda.o -FAILED: cuda.cuda.o -/usr/local/cuda-12.3/bin/nvcc --generate-dependencies-with-compile --dependency-output cuda.cuda.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_89,code=compute_89 -gencode=arch=compute_89,code=sm_89 --compiler-options '-fPIC' -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu -o cuda.cuda.o -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: identifier "dtype" is undefined - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: namespace "torch" has no member "float32" - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: identifier "device" is undefined - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(24): error: a pointer to a bound function may only be used to call the function - auto out = torch::zeros({batch_size}, dtype=torch::float32, device=a.device); - ^ - -/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu(30): error: no instance of overloaded function "at::Tensor::view" matches the argument list - argument types are: (int, int) - object type is: at::Tensor - return out.view(-1, 1); - ^ - -5 errors detected in the compilation of "/sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/cuda.cu". -[2/3] c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=matmul_sum_max -DTORCH_API_INCLUDE_EXTENSION_H -DPYBIND11_COMPILER_TYPE=\"_gcc\" -DPYBIND11_STDLIB=\"_libstdcpp\" -DPYBIND11_BUILD_ABI=\"_cxxabi1011\" -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/TH -isystem /matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/include/THC -isystem /usr/local/cuda-12.3/include -isystem /matx/u/aco/miniconda3/envs/myenv/include/python3.11 -D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -std=c++17 -c /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/main.cpp -o main.o -ninja: build stopped: subcommand failed. -Using /sailhome/aco/.cache/torch_extensions/py311_cu121 as PyTorch extensions root... -Creating extension directory /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max... -Detected CUDA files, patching ldflags -Emitting ninja build file /sailhome/aco/.cache/torch_extensions/py311_cu121/matmul_sum_max/build.ninja... -/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py:1965: UserWarning: TORCH_CUDA_ARCH_LIST is not set, all archs for visible cards are included for compilation. -If this is not desired, please set os.environ['TORCH_CUDA_ARCH_LIST']. - warnings.warn( -Building extension module matmul_sum_max... -Allowing ninja to set a default number of workers... (overridable by setting the environment variable MAX_JOBS=N) -Traceback (most recent call last): - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 2105, in _run_ninja_build - subprocess.run( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/subprocess.py", line 571, in run - raise CalledProcessError(retcode, process.args, -subprocess.CalledProcessError: Command '['ninja', '-v']' returned non-zero exit status 1. - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "/matx/u/aco/KernelBenchInternal/src/scratch/model_new.py", line 40, in - matmul_sum_max = load_inline( - ^^^^^^^^^^^^ - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1647, in load_inline - return _jit_compile( - ^^^^^^^^^^^^^ - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1722, in _jit_compile - _write_ninja_file_and_build_library( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 1834, in _write_ninja_file_and_build_library - _run_ninja_build( - File "/matx/u/aco/miniconda3/envs/myenv/lib/python3.11/site-packages/torch/utils/cpp_extension.py", line 2121, in _run_ninja_build - raise RuntimeError(message) from e -RuntimeError: Error building extension 'matmul_sum_max' diff --git a/src/scratch/model.py b/src/scratch/model.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/scratch/model_new.py b/src/scratch/model_new.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/scratch/prompt.txt b/src/scratch/prompt.txt deleted file mode 100644 index 78dbb009..00000000 --- a/src/scratch/prompt.txt +++ /dev/null @@ -1,137 +0,0 @@ - - You write custom CUDA kernels to replace the pytorch operators in the given architecture to get speedups. - - You have complete freedom to choose the set of operators you want to replace. You may make the decision to replace some operators with custom CUDA kernels and leave others unchanged. You may replace multiple operators with custom implementations, consider operator fusion opportunities (combining multiple operators into a single kernel, for example, combining matmul+relu), or algorithmic changes (such as online softmax). You are only limited by your imagination. - - - Here's an example to show you the syntax of inline embedding custom CUDA operators in torch: The example given architecture is: - - ``` - import torch -import torch.nn as nn -import torch.nn.functional as F - -class Model(nn.Module): - def __init__(self) -> None: - super().__init__() - - def forward(self, a, b): - return a + b - -def get_inputs(): - # randomly generate input tensors based on the model architecture - a = torch.randn(1, 128).cuda() - b = torch.randn(1, 128).cuda() - return [a, b] - -def get_init_inputs(): - # randomly generate tensors required for initialization based on the model architecture - return [] - ``` - - The example new arch with custom CUDA kernels looks like this: - ``` - import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.utils.cpp_extension import load_inline - -# Define the custom CUDA kernel for element-wise addition -elementwise_add_source = """ -#include -#include - -__global__ void elementwise_add_kernel(const float* a, const float* b, float* out, int size) { - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < size) { - out[idx] = a[idx] + b[idx]; - } -} - -torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b) { - auto size = a.numel(); - auto out = torch::zeros_like(a); - - const int block_size = 256; - const int num_blocks = (size + block_size - 1) / block_size; - - elementwise_add_kernel<<>>(a.data_ptr(), b.data_ptr(), out.data_ptr(), size); - - return out; -} -""" - -elementwise_add_cpp_source = "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" - -# Compile the inline CUDA code for element-wise addition -elementwise_add = load_inline( - name='elementwise_add', - cpp_sources=elementwise_add_cpp_source, - cuda_sources=elementwise_add_source, - functions=['elementwise_add_cuda'], - verbose=True, - extra_cflags=[''], - extra_ldflags=[''] -) - -class ModelNew(nn.Module): - def __init__(self) -> None: - super().__init__() - self.elementwise_add = elementwise_add - - def forward(self, a, b): - return self.elementwise_add.elementwise_add_cuda(a, b) - -def get_inputs(): - # randomly generate input tensors based on the model architecture - a = torch.randn(4096).cuda() - b = torch.randn(4096).cuda() - return [a, b] - -def get_init_inputs(): - # randomly generate tensors required for initialization based on the model architecture - return [] - ``` - - - You are given the following architecture: - - ``` - import torch -import torch.nn as nn - -class Model(nn.Module): - """ - Simple model that performs a single matrix multiplication (C = A * B) - """ - def __init__(self): - super(Model, self).__init__() - - def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: - """ - Performs matrix multiplication. - - Args: - A: Input tensor of shape (M, K). - B: Input tensor of shape (K, N). - - Returns: - Output tensor of shape (M, N). - """ - return torch.matmul(A, B) - -M = 1024 -K = 4096 -N = 2048 - -def get_inputs(): - A = torch.randn(M, K) - B = torch.randn(K, N) - return [A, B] - -def get_init_inputs(): - return [] # No special initialization inputs needed - ``` - Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. - - \ No newline at end of file diff --git a/src/scratch/test.py b/src/scratch/test.py deleted file mode 100644 index dbead51c..00000000 --- a/src/scratch/test.py +++ /dev/null @@ -1,69 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -import random -import numpy as np - -# load model and the modified model -from model import Model -from model import get_inputs -from model import get_init_inputs -from model_new import ModelNew - -torch.cuda.synchronize() - - -def set_seed(seed): - torch.manual_seed(seed) - torch.cuda.manual_seed(seed) - random.seed(seed) - np.random.seed(seed) - - -def check_correctness(): - # run the model and check correctness - with torch.no_grad(): - - # generate inputs and init_inputs, and instantiate models - set_seed(42) - inputs = get_inputs() - set_seed(42) - init_inputs = get_init_inputs() - - # move to GPU - inputs = [x.cuda() if isinstance(x, torch.Tensor) else x for x in inputs] - init_inputs = [ - x.cuda() if isinstance(x, torch.Tensor) else x for x in init_inputs - ] - - set_seed(42) - model = Model(*init_inputs).cuda() - set_seed(42) - model_new = ModelNew(*init_inputs).cuda() - - # forward pass - output = model(*inputs) - output_new = model_new(*inputs) - - # move to CPU - torch.cuda.synchronize() - output = output.cpu() - output_new = output_new.cpu() - - # check correctness - assert output.shape == output_new.shape - assert torch.allclose(output, output_new, atol=1e-02) - - return "PASS" - - -def run(random_seed=42): - - # run both models and check correctness - check_correctness() - - return "PASS" - - -if __name__ == "__main__": - print(run()) From 15a2b4f60c0f8d55d8d13deb041e9f0ec78b751d Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 15:58:17 +0200 Subject: [PATCH 09/17] lint --- .pre-commit-config.yaml | 26 ++ LICENSE | 2 +- README.md | 30 +- curl.sh | 2 +- pyproject.toml | 47 ++- pytest.ini | 5 + results/timing/README.md | 2 +- scripts/benchmark_eval_analysis.py | 80 ++-- scripts/debug_stddout.py | 12 +- scripts/eval_from_generations.py | 217 +++++++---- scripts/generate_and_eval_single_sample.py | 92 +++-- .../generate_and_eval_single_sample_modal.py | 136 ++++--- scripts/generate_baseline_time.py | 131 ++++--- scripts/generate_baseline_time_modal.py | 173 +++++---- scripts/generate_samples.py | 155 +++++--- scripts/inspect_baseline.py | 41 +- scripts/inspect_kernel_pytorch_profiler.py | 61 +-- scripts/inspect_triton.py | 43 ++- scripts/run_and_check.py | 132 ++++--- scripts/run_and_check_modal.py | 201 ++++++---- scripts/server_requirements.txt | 2 +- scripts/server_run_and_check.py | 77 ++-- scripts/server_run_and_check_modal.py | 365 +++++++++++------- scripts/verify_bench.py | 4 +- scripts/verify_generation.py | 32 +- src/kernelbench/analysis.py | 7 +- src/kernelbench/compile.py | 121 ++++-- src/kernelbench/dataset.py | 23 +- src/kernelbench/eval.py | 35 +- src/kernelbench/frameworks.py | 33 +- src/kernelbench/llm_utils.py | 108 +++--- src/kernelbench/make_hf_dataset.py | 45 +-- src/kernelbench/prompt_constructor.py | 179 +++++---- src/kernelbench/prompts/README.md | 4 +- .../prompts/cot/model_cot_fuse_gelu.py | 5 +- .../prompts/cot/model_cot_mnist2.py | 17 +- .../prompts/cot/model_cot_tiled_matmul.py | 6 +- .../prompts/few_shot/model_ex_add.py | 2 +- .../prompts/few_shot/model_ex_flash_attn.py | 5 +- .../prompts/few_shot/model_ex_fuse_gelu.py | 4 +- .../prompts/few_shot/model_ex_mnist2.py | 2 +- .../prompts/few_shot/model_ex_tiled_matmul.py | 3 +- .../prompts/few_shot/model_new_ex_add.py | 4 +- .../few_shot/model_new_ex_flash_attn.py | 10 +- .../few_shot/model_new_ex_fuse_gelu.py | 2 +- .../prompts/few_shot/model_new_ex_mnist2.py | 27 +- .../few_shot/model_new_ex_tiled_matmul.py | 11 +- src/kernelbench/prompts/hardware/gpu_specs.py | 8 +- src/kernelbench/score.py | 47 ++- src/kernelbench/unit_tests/test_dataset.py | 23 +- src/kernelbench/unit_tests/test_score.py | 122 ++++-- src/kernelbench/unit_tests/test_utils.py | 42 +- src/kernelbench/utils.py | 44 ++- tests/test_utils.py | 25 ++ 54 files changed, 1918 insertions(+), 1114 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 pytest.ini create mode 100644 tests/test_utils.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..585b79be --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + exclude: ^(KernelBench/|results/.*\.json$) + - id: end-of-file-fixer + exclude: ^(KernelBench/|results/.*\.json$) + - id: check-yaml + - id: check-toml + - id: check-added-large-files + +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.2 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) + - id: ruff-format + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) + +- repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + exclude: ^(KernelBench/|src/kernelbench/prompts/model_ex_2\.py|src/kernelbench/prompts/model_new_ex_2\.py|results/.*\.json$) diff --git a/LICENSE b/LICENSE index c6aaaad2..eaae6b11 100644 --- a/LICENSE +++ b/LICENSE @@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 4680fa03..8da695e1 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ We construct Kernel Bench to have 4 Levels of categories: - **Level 2 🔗**: Simple fusion patterns (100 Problems) A fused kernel would be faster than separated kernels (Conv + Bias + ReLU, Matmul + Scale + Sigmoid) - **Level 3 ⚛️**: Full model architectures (50 Problems) - Optimize entire model architectures end-to-end (MobileNet, VGG, MiniGPT, Mamba) -- **Level 4 🤗**: Level Hugging Face + Optimize entire model architectures end-to-end (MobileNet, VGG, MiniGPT, Mamba) +- **Level 4 🤗**: Level Hugging Face Optimize whole model architectures from HuggingFace ## ⚖️ Evaluation @@ -27,9 +27,9 @@ To evaluate model-generated kernels, we need to check if they: - **is correct ✅**: check against reference torch operators `n_correctness` times on randomized inputs. - **is performant ⏱️**: compare against reference torch operators `n_trial` times to measure speedup between runtimes. -Check out `src/eval.py` for details on how we implement correctness check and timing. +Check out `src/eval.py` for details on how we implement correctness check and timing. -We provide a convenient script `scripts/run_and_check.py` to evaluate one single sample source code against a reference source code, check correctness and compute speedup. You can use this to evaluate a model-generated kernel. +We provide a convenient script `scripts/run_and_check.py` to evaluate one single sample source code against a reference source code, check correctness and compute speedup. You can use this to evaluate a model-generated kernel. #### Overall Benchmark Metric @@ -44,7 +44,7 @@ You can increase speedup threshold `p` to make the task more challenging. #### Compute Overall Benchmark Performance -We provide a script `scripts/greedy_analysis.py` to compute the overall benchmark performance. +We provide a script `scripts/greedy_analysis.py` to compute the overall benchmark performance. Since we need to capture **both** correctness and performance, we use a metric `fast_p`: fraction of tasks that are both correct and have a speedup greater than threshold `p`; speedup is computed as the ratio of PyTorch reference wall-clock time to generated kernel time. @@ -56,11 +56,11 @@ KernelBench/ ├── assets/ ├── KernelBench/ # Benchmark dataset files ├── src/ # KernelBench logic code -│ ├── unit_tests/ +│ ├── unit_tests/ │ ├── prompts/ │ ├── .... ├── scripts/ # helpful scripts to run the benchmark -├── results/ # baseline times across hardware +├── results/ # baseline times across hardware ├── runs/ # where your runs will be stored ``` @@ -69,7 +69,7 @@ KernelBench/ conda create --name kernel-bench python=3.10 conda activate kernel-bench pip install -r requirements.txt -pip install -e . +pip install -e . ``` ### Alternative setup using `uv` @@ -87,11 +87,11 @@ uv pip install -e . To call LLM API providers, set your `{INFERENCE_SERVER_PROVIDER}_API_KEY` API key. -Running and profiling kernels require a GPU. +Running and profiling kernels require a GPU. If you don't have GPU available locally, you can set up [Modal](https://modal.com/). Set up your modal token after creating an account by running `modal token new`. Then, use the `generate_and_eval_single_sample_modal.py` script. ## 🚀 Usage -### Run on a single problem +### Run on a single problem It is easier to get started with a single problem. This will fetch the problem, generate a sample, and evaluate the sample. ``` @@ -103,7 +103,7 @@ python3 scripts/generate_and_eval_single_sample.py dataset_src="huggingface" lev # add .verbose_logging for more visbility ``` -### Run on all problems +### Run on all problems ``` # 1. Generate responses and store kernels locally to runs/{run_name} directory @@ -116,7 +116,7 @@ python3 scripts/eval_from_generations.py run_name=test_hf_level_1 dataset_src=lo # add build_cache=True and num_cpu_workers= to the command ``` ### Analyze the eval results to compute Benchmark Performance -We provide `scripts/benchmark_eval_analysis.py` to analyze the eval results to compute success rate, timing metric, and overall benchmark performance `fast_p`. +We provide `scripts/benchmark_eval_analysis.py` to analyze the eval results to compute success rate, timing metric, and overall benchmark performance `fast_p`. ``` python3 scripts/benchmark_eval_analysis.py run_name=test_hf_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch @@ -127,7 +127,7 @@ We provide some reference baseline times a variety of NVIDIA GPUs across generat ## 🛣️ Upcoming Roadmap - [ ] Triton Variant (Ongoing) - [ ] Easy to use CoLab Notebook Example -- [ ] Push button flow on Modal / Cloud Provider +- [ ] Push button flow on Modal / Cloud Provider - [ ] Integrate with more frameworks, such as [ThunderKittens](https://github.com/HazyResearch/ThunderKittens) - [ ] Add backward pass - [ ] Integrate with toolchains such as NCU @@ -147,12 +147,12 @@ MIT. Check `LICENSE.md` for more details. ## Citation ```bibtex @misc{ouyang2025kernelbenchllmswriteefficient, - title={KernelBench: Can LLMs Write Efficient GPU Kernels?}, + title={KernelBench: Can LLMs Write Efficient GPU Kernels?}, author={Anne Ouyang and Simon Guo and Simran Arora and Alex L. Zhang and William Hu and Christopher Ré and Azalia Mirhoseini}, year={2025}, eprint={2502.10517}, archivePrefix={arXiv}, primaryClass={cs.LG}, - url={https://arxiv.org/abs/2502.10517}, + url={https://arxiv.org/abs/2502.10517}, } ``` diff --git a/curl.sh b/curl.sh index df45bbfa..fa41012a 100644 --- a/curl.sh +++ b/curl.sh @@ -3,4 +3,4 @@ curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi -F "kernel_file=@src/prompts/model_new_ex_1.py" \ -F "num_correct_trials=5" \ -F "num_perf_trials=100" \ - -F "verbose=false" | python -m json.tool \ No newline at end of file + -F "verbose=false" | python -m json.tool diff --git a/pyproject.toml b/pyproject.toml index 861b2f20..ffcc2ccc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,4 +31,49 @@ dependencies = [ [project.optional-dependencies] dev = [ "weave>=0.51.39", -] \ No newline at end of file + "black>=24.2.0", + "ruff>=0.2.2", + "pre-commit>=3.5.0", + "pytest>=8.3.5", +] + +[tool.black] +line-length = 88 +target-version = ["py310"] +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | KernelBench + | results +)/ +''' + +[tool.ruff] +line-length = 88 +target-version = "py310" +select = ["E", "F", "I", "W", "B", "C4", "N"] +ignore = [] +exclude = [ + ".git", + ".venv", + "dist", + "build", + "KernelBench", + "results", +] + +[tool.ruff.isort] +known-first-party = ["kernelbench"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = "test_*.py" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..2476a922 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +testpaths = tests +python_files = test_*.py +python_functions = test_* +addopts = -v diff --git a/results/timing/README.md b/results/timing/README.md index 2aaea696..043d5658 100644 --- a/results/timing/README.md +++ b/results/timing/README.md @@ -56,4 +56,4 @@ Learn more about Torch Compile [backends](https://pytorch.org/docs/stable/torch. Thank you to [@PaliC](https://github.com/PaliC) from the PyTorch team for the exerptise on various Torch Configurations. -Thanks to [Modal](https://modal.com/) for sponsoring compute credits for us to collect runtime baseline on a vareity range of NVIDIA GPUs. \ No newline at end of file +Thanks to [Modal](https://modal.com/) for sponsoring compute credits for us to collect runtime baseline on a vareity range of NVIDIA GPUs. diff --git a/scripts/benchmark_eval_analysis.py b/scripts/benchmark_eval_analysis.py index eb0b96f0..346e61c4 100644 --- a/scripts/benchmark_eval_analysis.py +++ b/scripts/benchmark_eval_analysis.py @@ -18,21 +18,23 @@ ``` python3 scripts/benchmark_eval_analysis.py run_name= level= hardware= baseline= ``` -hardware + baseline should correspond to the results/timing/hardware/baseline.json file +hardware + baseline should correspond to the results/timing/hardware/baseline.json file + +""" -""" class AnalysisConfig(Config): def __init__(self): - self.run_name = REQUIRED # name of the run to evaluate - self.level = REQUIRED # level to evaluate + self.run_name = REQUIRED # name of the run to evaluate + self.level = REQUIRED # level to evaluate - self.hardware = REQUIRED # hardware to evaluate - self.baseline = REQUIRED # baseline to compare against + self.hardware = REQUIRED # hardware to evaluate + self.baseline = REQUIRED # baseline to compare against def __repr__(self): return f"AnalysisConfig({self.to_dict()})" + def patch(eval_results, dataset): """ Patch the eval results with the dataset @@ -40,15 +42,16 @@ def patch(eval_results, dataset): for pid in range(1, len(dataset) + 1): if str(pid) not in eval_results: eval_results[str(pid)] = { - "sample_id": 0, - "compiled": False, - "correctness": False, + "sample_id": 0, + "compiled": False, + "correctness": False, "metadata": {}, - "runtime": -1.0, - "runtime_stats": {} + "runtime": -1.0, + "runtime_stats": {}, } return eval_results + def analyze_greedy_eval(run_name, hardware, baseline, level): """ Analyze the greedy eval results for a run of a particular level @@ -57,16 +60,20 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): dataset = construct_kernelbench_dataset(level) # load json - eval_file_path = f'runs/{run_name}/eval_results.json' - assert os.path.exists(eval_file_path), f"Eval file does not exist at {eval_file_path}" + eval_file_path = f"runs/{run_name}/eval_results.json" + assert os.path.exists( + eval_file_path + ), f"Eval file does not exist at {eval_file_path}" - baseline_file_path = f'results/timing/{hardware}/{baseline}.json' - assert os.path.exists(baseline_file_path), f"Baseline file does not exist at {baseline_file_path}" + baseline_file_path = f"results/timing/{hardware}/{baseline}.json" + assert os.path.exists( + baseline_file_path + ), f"Baseline file does not exist at {baseline_file_path}" - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) - with open(baseline_file_path, 'r') as f: + with open(baseline_file_path, "r") as f: baseline_results = json.load(f) # Initialize counters @@ -95,29 +102,41 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): print(f"\nSuccess rates:") print(f"Compilation rate: {compiled_count/total_count*100:.1f}%") - print(f"Correctness rate: {correct_count/total_count*100:.1f}%") - + print(f"Correctness rate: {correct_count/total_count*100:.1f}%") # Calculate speedup metrics - from src.score import geometric_mean_speed_ratio_correct_only, geometric_mean_speed_ratio_correct_and_faster_only, fastp + from src.score import ( + geometric_mean_speed_ratio_correct_only, + geometric_mean_speed_ratio_correct_and_faster_only, + fastp, + ) import numpy as np # Extract the speedup values is_correct = np.array([entry["correctness"] for entry in eval_results.values()]) - baseline_speed = np.array([entry["mean"] for entry in baseline_results[f'level{level}'].values()]) + baseline_speed = np.array( + [entry["mean"] for entry in baseline_results[f"level{level}"].values()] + ) actual_speed = np.array([entry["runtime"] for entry in eval_results.values()]) n = len(is_correct) - assert len(baseline_speed) == n, "Baseline speedup values do not match the number of eval results" - assert len(actual_speed) == n, "Actual speedup values do not match the number of eval results" + assert ( + len(baseline_speed) == n + ), "Baseline speedup values do not match the number of eval results" + assert ( + len(actual_speed) == n + ), "Actual speedup values do not match the number of eval results" # Calculate the metrics - gmsr_correct = geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n) + gmsr_correct = geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ) # list of speedup thresholds p p_values = [0.0, 0.5, 0.8, 1.0, 1.5, 2.0] - results = [[p, fastp(is_correct, baseline_speed, actual_speed, n, p)] for p in p_values] - + results = [ + [p, fastp(is_correct, baseline_speed, actual_speed, n, p)] for p in p_values + ] # Print the results print("\nSpeedup Metrics:") @@ -125,12 +144,17 @@ def analyze_greedy_eval(run_name, hardware, baseline, level): # Print table print("\nFast_p Results:") - print(tabulate(results, headers=["Speedup Threshold (p)", "Fast_p Score"], tablefmt="grid")) + print( + tabulate( + results, headers=["Speedup Threshold (p)", "Fast_p Score"], tablefmt="grid" + ) + ) @pydra.main(base=AnalysisConfig) def main(config: AnalysisConfig): analyze_greedy_eval(config.run_name, config.hardware, config.baseline, config.level) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/debug_stddout.py b/scripts/debug_stddout.py index 99bffa58..18edc096 100644 --- a/scripts/debug_stddout.py +++ b/scripts/debug_stddout.py @@ -5,24 +5,28 @@ # Test for checking if we can capture nvcc errors ################################################################################ -correct_kernel_code = "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for 4D tensor-matrix multiplication\ntensor_matmul_source = \"\"\"\n#include \n#include \n\n__global__ void tensor_matmul_kernel(const float* A, const float* B, float* C, int b, int i, int j, int l, int k) {\n int idx_b = blockIdx.x;\n int idx_i = blockIdx.y;\n int idx_j = blockIdx.z;\n int idx_k = threadIdx.x;\n\n if (idx_b < b && idx_i < i && idx_j < j && idx_k < k) {\n float sum = 0.0f;\n for (int idx_l = 0; idx_l < l; ++idx_l) {\n sum += A[idx_b * i * j * l + idx_i * j * l + idx_j * l + idx_l] * B[idx_l * k + idx_k];\n }\n C[idx_b * i * j * k + idx_i * j * k + idx_j * k + idx_k] = sum;\n }\n}\n\ntorch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B) {\n int b = A.size(0);\n int i = A.size(1);\n int j = A.size(2);\n int l = A.size(3);\n int k = B.size(1);\n\n auto C = torch::zeros({b, i, j, k}, A.options());\n\n dim3 blocks(b, i, j);\n int threads = k;\n\n tensor_matmul_kernel<<>>(A.data_ptr(), B.data_ptr(), C.data_ptr(), b, i, j, l, k);\n\n return C;\n}\n\"\"\"\n\ntensor_matmul_cpp_source = (\n \"torch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B);\"\n)\n\n# Compile the inline CUDA code for 4D tensor-matrix multiplication\ntensor_matmul = load_inline(\n name=\"tensor_matmul\",\n cpp_sources=tensor_matmul_cpp_source,\n cuda_sources=tensor_matmul_source,\n functions=[\"tensor_matmul_cuda\"],\n verbose=True,\n extra_cflags=[\"\"],\n extra_ldflags=[\"\"],\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super(ModelNew, self).__init__()\n self.tensor_matmul = tensor_matmul\n\n def forward(self, A, B):\n return self.tensor_matmul.tensor_matmul_cuda(A, B)" +correct_kernel_code = 'import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for 4D tensor-matrix multiplication\ntensor_matmul_source = """\n#include \n#include \n\n__global__ void tensor_matmul_kernel(const float* A, const float* B, float* C, int b, int i, int j, int l, int k) {\n int idx_b = blockIdx.x;\n int idx_i = blockIdx.y;\n int idx_j = blockIdx.z;\n int idx_k = threadIdx.x;\n\n if (idx_b < b && idx_i < i && idx_j < j && idx_k < k) {\n float sum = 0.0f;\n for (int idx_l = 0; idx_l < l; ++idx_l) {\n sum += A[idx_b * i * j * l + idx_i * j * l + idx_j * l + idx_l] * B[idx_l * k + idx_k];\n }\n C[idx_b * i * j * k + idx_i * j * k + idx_j * k + idx_k] = sum;\n }\n}\n\ntorch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B) {\n int b = A.size(0);\n int i = A.size(1);\n int j = A.size(2);\n int l = A.size(3);\n int k = B.size(1);\n\n auto C = torch::zeros({b, i, j, k}, A.options());\n\n dim3 blocks(b, i, j);\n int threads = k;\n\n tensor_matmul_kernel<<>>(A.data_ptr(), B.data_ptr(), C.data_ptr(), b, i, j, l, k);\n\n return C;\n}\n"""\n\ntensor_matmul_cpp_source = (\n "torch::Tensor tensor_matmul_cuda(torch::Tensor A, torch::Tensor B);"\n)\n\n# Compile the inline CUDA code for 4D tensor-matrix multiplication\ntensor_matmul = load_inline(\n name="tensor_matmul",\n cpp_sources=tensor_matmul_cpp_source,\n cuda_sources=tensor_matmul_source,\n functions=["tensor_matmul_cuda"],\n verbose=True,\n extra_cflags=[""],\n extra_ldflags=[""],\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super(ModelNew, self).__init__()\n self.tensor_matmul = tensor_matmul\n\n def forward(self, A, B):\n return self.tensor_matmul.tensor_matmul_cuda(A, B)' faulty_kernel_code = 'import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n# Define the custom CUDA kernel for Max Pooling 3D\nmaxpool3d_source = """\n#include \n#include \n\n__global__ void maxpool3d_kernel(const float* input, float* output, int* indices, \n int batch_size, int channels, int dim1, int dim2, int dim3,\n int kernel_size, int stride, int padding, int dilation,\n int out_dim1, int out_dim2, int out_dim3) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int b = idx / (channels * out_dim1 * out_dim2 * out_dim3);\n int c = (idx / (out_dim1 * out_dim2 * out_dim3)) % channels;\n int d1 = (idx / (out_dim2 * out_dim3)) % out_dim1;\n int d2 = (idx / out_dim3) % out_dim2;\n int d3 = idx % out_dim3;\n\n if (b < batch_size && c < channels && d1 < out_dim1 && d2 < out_dim2 && d3 < out_dim3) {\n float max_val = -FLT_MAX;\n int max_idx = -1;\n\n for (int k1 = 0; k1 < kernel_size; ++k1) {\n for (int k2 = 0; k2 < kernel_size; ++k2) {\n for (int k3 = 0; k3 < kernel_size; ++k3) {\n int in_d1 = d1 * stride - padding + k1 * dilation;\n int in_d2 = d2 * stride - padding + k2 * dilation;\n int in_d3 = d3 * stride - padding + k3 * dilation;\n\n if (in_d1 >= 0 && in_d1 < dim1 && in_d2 >= 0 && in_d2 < dim2 && in_d3 >= 0 && in_d3 < dim3) {\n int in_idx = ((b * channels + c) * dim1 + in_d1) * dim2 * dim3 + in_d2 * dim3 + in_d3;\n float val = input[in_idx];\n if (val > max_val) {\n max_val = val;\n max_idx = in_idx;\n }\n }\n }\n }\n }\n\n output[idx] = max_val;\n if (indices != nullptr) {\n indices[idx] = max_idx;\n }\n }\n}\n\ntorch::Tensor maxpool3d_cuda(torch::Tensor input, int kernel_size, int stride, int padding, int dilation, bool return_indices) {\n int batch_size = input.size(0);\n int channels = input.size(1);\n int dim1 = input.size(2);\n int dim2 = input.size(3);\n int dim3 = input.size(4);\n\n int out_dim1 = (dim1 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n int out_dim2 = (dim2 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n int out_dim3 = (dim3 + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1;\n\n auto output = torch::zeros({batch_size, channels, out_dim1, out_dim2, out_dim3}, input.options());\n auto indices = return_indices ? torch::zeros({batch_size, channels, out_dim1, out_dim2, out_dim3}, input.options().dtype(torch::kInt32)) : torch::Tensor();\n\n int size = batch_size * channels * out_dim1 * out_dim2 * out_dim3;\n const int block_size = 256;\n const int num_blocks = (size + block_size - 1) / block_size;\n\n maxpool3d_kernel<<>>(input.data_ptr(), output.data_ptr(), \n return_indices ? indices.data_ptr() : nullptr,\n batch_size, channels, dim1, dim2, dim3,\n kernel_size, stride, padding, dilation,\n out_dim1, out_dim2, out_dim3);\n\n if (return_indices) {\n return torch::make_tuple(output, indices);\n } else {\n return output;\n }\n}\n"""\n\nmaxpool3d_cpp_source = (\n "torch::Tensor maxpool3d_cuda(torch::Tensor input, int kernel_size, int stride, int padding, int dilation, bool return_indices);"\n)\n\n# Compile the inline CUDA code for Max Pooling 3D\nmaxpool3d = load_inline(\n name="maxpool3d",\n cpp_sources=maxpool3d_cpp_source,\n cuda_sources=maxpool3d_source,\n functions=["maxpool3d_cuda"],\n verbose=True,\n extra_cflags=[""],\n extra_ldflags=[""],\n)\n\n\nclass ModelNew(nn.Module):\n """\n Optimized model that performs Max Pooling 3D using custom CUDA kernels.\n """\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0, dilation: int = 1, return_indices: bool = False, ceil_mode: bool = False):\n """\n Initializes the Max Pooling 3D layer.\n\n Args:\n kernel_size (int): Size of the kernel for the max pooling operation.\n stride (int, optional): Stride of the pooling operation. Defaults to None, which means stride is equal to kernel_size.\n padding (int, optional): Padding applied to the input tensor. Defaults to 0.\n dilation (int, optional): Spacing between kernel elements. Defaults to 1.\n return_indices (bool, optional): Whether to return indices of the maximum values. Defaults to False.\n ceil_mode (bool, optional): When True, the output size is ceil(input_size / stride) instead of floor. Defaults to False.\n """\n super(ModelNew, self).__init__()\n self.kernel_size = kernel_size\n self.stride = stride if stride is not None else kernel_size\n self.padding = padding\n self.dilation = dilation\n self.return_indices = return_indices\n self.ceil_mode = ceil_mode\n self.maxpool3d = maxpool3d\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n """\n Applies Max Pooling 3D to the input tensor using custom CUDA kernels.\n\n Args:\n x (torch.Tensor): Input tensor of shape (batch_size, channels, dim1, dim2, dim3).\n\n Returns:\n torch.Tensor: Output tensor with Max Pooling 3D applied.\n """\n return self.maxpool3d.maxpool3d_cuda(x, self.kernel_size, self.stride, self.padding, self.dilation, self.return_indices)' -set_gpu_arch(["Ada"]) # replace with whatever device architecthre you have +set_gpu_arch(["Ada"]) # replace with whatever device architecthre you have test_build_dir = "test_build_dir" print("Testing Correct Kernel Code") -status, stdout, err = build_compile_cache_with_capturing(correct_kernel_code, verbose=False, build_dir=test_build_dir) +status, stdout, err = build_compile_cache_with_capturing( + correct_kernel_code, verbose=False, build_dir=test_build_dir +) print("status: ", status) print("stdout: ", stdout) print("err: ", err) assert status == 0, "Correct Code should compile" print("Testing Faulty Kernel Code") -status, stdout, err = build_compile_cache_with_capturing(faulty_kernel_code, verbose=False, build_dir=test_build_dir) +status, stdout, err = build_compile_cache_with_capturing( + faulty_kernel_code, verbose=False, build_dir=test_build_dir +) print("status: ", status) print("stdout: ", stdout) print("err: ", err) diff --git a/scripts/eval_from_generations.py b/scripts/eval_from_generations.py index c8094886..20db46c0 100644 --- a/scripts/eval_from_generations.py +++ b/scripts/eval_from_generations.py @@ -16,10 +16,10 @@ from kernelbench.compile import batch_compile from kernelbench.dataset import construct_kernelbench_dataset from kernelbench.eval import ( - build_compile_cache, - eval_kernel_against_ref, - KernelExecResult, - check_metadata_serializable_all_types + build_compile_cache, + eval_kernel_against_ref, + KernelExecResult, + check_metadata_serializable_all_types, ) from kernelbench.utils import set_gpu_arch, read_file @@ -44,9 +44,9 @@ class EvalConfig(Config): def __init__(self): - self.run_name = REQUIRED # name of the run to evaluate + self.run_name = REQUIRED # name of the run to evaluate - self.dataset_src = REQUIRED # either huggingface or local + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" @@ -55,7 +55,7 @@ def __init__(self): self.level = REQUIRED # subset of problems to evaluate - self.subset = (None, None) # (start_id, end_id), these are the logical index + self.subset = (None, None) # (start_id, end_id), these are the logical index # Evaluation Mode: local (requires GPU), see modal (cloud GPU) in the modal file self.eval_mode = "local" @@ -73,20 +73,21 @@ def __init__(self): # Eval settings self.num_correct_trials = 5 self.num_perf_trials = 100 - self.timeout = 180 # in seconds + self.timeout = 180 # in seconds self.measure_performance = True - + # Eval Flow setting # To speedup evaluation, you can start building the kernel on CPU on disk as cache self.build_cache = False - self.num_cpu_workers = 20 # number of parallel process to to parallelize the build on CPUs - + self.num_cpu_workers = ( + 20 # number of parallel process to to parallelize the build on CPUs + ) + # Directory to build kernels for evaluation self.kernel_eval_build_dir = os.path.join(REPO_TOP_DIR, "cache") # number of GPUs to do batch evaluation self.num_gpu_devices = 1 - def __repr__(self): return f"EvalConfig({self.to_dict()})" @@ -99,43 +100,58 @@ class WorkArgs: device: torch.device -def fetch_ref_arch_from_problem_id(dataset, problem_id: int, dataset_src: str) -> str | None: +def fetch_ref_arch_from_problem_id( + dataset, problem_id: int, dataset_src: str +) -> str | None: """ Fetch reference architecture from problem directory Either from Hugging Face or Local Dataset """ if dataset_src == "huggingface": - curr_problem_row = dataset.filter(lambda x: x["problem_id"] == problem_id, num_proc=1, desc=None) + curr_problem_row = dataset.filter( + lambda x: x["problem_id"] == problem_id, num_proc=1, desc=None + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] - + elif dataset_src == "local": - problem_idx_in_dataset = problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) ref_arch_src = read_file(ref_arch_path) # verify - # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") + # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({problem_id})" - + assert ( + problem_number == problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({problem_id})" + return ref_arch_src -def fetch_kernel_from_disk(run_dir: str, level: int, problem_id: int, sample_id: int) -> str | None: +def fetch_kernel_from_disk( + run_dir: str, level: int, problem_id: int, sample_id: int +) -> str | None: """ Fetch kernel file from disk (stored in runs/{run_name}) """ - kernel_path = os.path.join(run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py") - + kernel_path = os.path.join( + run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py" + ) + if os.path.exists(kernel_path): return read_file(kernel_path) else: return None -def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, run_dir: str) -> KernelExecResult | None: + +def evaluate_single_sample( + work_args: WorkArgs, configs: EvalConfig, dataset, run_dir: str +) -> KernelExecResult | None: """ Evaluate a single sample on a single GPU """ @@ -145,22 +161,28 @@ def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, ru work_args.device, ) # fetch reference architecture from problem directory - ref_arch_src = fetch_ref_arch_from_problem_id(dataset, problem_id, configs.dataset_src) + ref_arch_src = fetch_ref_arch_from_problem_id( + dataset, problem_id, configs.dataset_src + ) # fetch kernel from disk # Add database support in the future kernel_src = fetch_kernel_from_disk(run_dir, configs.level, problem_id, sample_id) - assert kernel_src is not None, f"Kernel not found for problem {problem_id} sample {sample_id}" + assert ( + kernel_src is not None + ), f"Kernel not found for problem {problem_id} sample {sample_id}" - build_dir = os.path.join(configs.kernel_eval_build_dir, configs.run_name, f"{problem_id}", f"{sample_id}") + build_dir = os.path.join( + configs.kernel_eval_build_dir, configs.run_name, f"{problem_id}", f"{sample_id}" + ) - try: + try: eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=configs.measure_performance, - verbose=configs.verbose, + verbose=configs.verbose, num_correct_trials=configs.num_correct_trials, num_perf_trials=configs.num_perf_trials, build_dir=build_dir, @@ -183,14 +205,17 @@ def evaluate_single_sample(work_args: WorkArgs, configs: EvalConfig, dataset, ru ) return eval_result else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } # for debugging - eval_result = KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } # for debugging + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result - + + def cuda_single_eval_wrapper(curr_work: WorkArgs, configs: dict, dataset, run_dir: str): """ Wrapper to handle timeout and keyboard interrupt @@ -203,16 +228,18 @@ def cuda_single_eval_wrapper(curr_work: WorkArgs, configs: dict, dataset, run_di args=(curr_work, configs, dataset, run_dir), ).get(timeout=configs.timeout) except KeyboardInterrupt: - print( - "\n [Terminate] Caught KeyboardInterrupt, terminating workers..." - ) + print("\n [Terminate] Caught KeyboardInterrupt, terminating workers...") pool.terminate() pool.join() raise except mp.TimeoutError as e: - print(f"[WARNING] Evaluation TIMED OUT for Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}") + print( + f"[WARNING] Evaluation TIMED OUT for Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}" + ) - print(f"[Eval Result] Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}: {result}") + print( + f"[Eval Result] Problem ID: {curr_work.problem_id}, Sample ID: {curr_work.sample_id}: {result}" + ) return result @@ -221,15 +248,20 @@ def remove_cache_dir(cache_dir: str, run_name: str, problem_id, sample_id): Remove the cached folder for sample compilation so it can start a clean build next time useful for time out, failed build, etc. """ - problem_cache_dir = os.path.join(cache_dir, run_name, f"{problem_id}", f"{sample_id}") + problem_cache_dir = os.path.join( + cache_dir, run_name, f"{problem_id}", f"{sample_id}" + ) print(f"cache_dir to remove: {problem_cache_dir}") if os.path.exists(cache_dir): try: shutil.rmtree(cache_dir, ignore_errors=True) - print(f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) except Exception as e: print(f"\n[WARNING] Failed to remove cache directory {cache_dir}: {str(e)}") + def batch_eval( total_work: list[tuple[int, int]], config: EvalConfig, @@ -253,7 +285,9 @@ def batch_eval( print( f"[Curr Batch] {len(curr_work_batch)} tasks over {config.num_gpu_devices} GPUs; [Total Work left] {len(total_work)}" ) - assert len(curr_work_batch) <= batch_size, f"Current batch size {len(curr_work_batch)} is greater than the number of GPUs {batch_size}" + assert ( + len(curr_work_batch) <= batch_size + ), f"Current batch size {len(curr_work_batch)} is greater than the number of GPUs {batch_size}" with mp.Pool(batch_size) as pool: @@ -278,7 +312,7 @@ def batch_eval( async_results.append( pool.apply_async(evaluate_single_sample, work_arg) ) - + # Collect results with a batch timeout results = [] batch_timeout = config.timeout @@ -290,20 +324,30 @@ def batch_eval( remaining_time = max(0, batch_timeout - elapsed_time) result = async_result.get(timeout=remaining_time) results.append((problem_id, sample_id, result)) - + except mp.TimeoutError: print( f"[WARNING] Evaluation TIMED OUT for Problem ID: {problem_id}, Sample ID: {sample_id}" ) results.append((problem_id, sample_id, None)) - - remove_cache_dir(config.kernel_eval_build_dir, config.run_name, problem_id, sample_id) + + remove_cache_dir( + config.kernel_eval_build_dir, + config.run_name, + problem_id, + sample_id, + ) except Exception as e: print( f"[ERROR] Evaluation FAILED for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" ) results.append((problem_id, sample_id, None)) - remove_cache_dir(config.kernel_eval_build_dir, config.run_name, problem_id, sample_id) + remove_cache_dir( + config.kernel_eval_build_dir, + config.run_name, + problem_id, + sample_id, + ) end_time = time.time() @@ -318,8 +362,12 @@ def batch_eval( # add all the batch results here to avoid file race condition # add to eval result if valid result if result is not None: - print(f"Adding Eval Result to file for problem {problem_id} sample {sample_id}") - add_to_eval_results_file(problem_id, sample_id, result, eval_file_path) + print( + f"Adding Eval Result to file for problem {problem_id} sample {sample_id}" + ) + add_to_eval_results_file( + problem_id, sample_id, result, eval_file_path + ) print("-" * 128) print( @@ -328,51 +376,62 @@ def batch_eval( pbar.update(len(curr_work_batch)) -def check_if_eval_exists_local(problem_id: int, sample_id: int, eval_file_path: str) -> bool: + +def check_if_eval_exists_local( + problem_id: int, sample_id: int, eval_file_path: str +) -> bool: """ Check if evaluation result already exists in eval results file """ if os.path.exists(eval_file_path): - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) return str(problem_id) in eval_results return False -def add_to_eval_results_file(problem_id: int, sample_id: int, eval_result: KernelExecResult, eval_file_path: str): + +def add_to_eval_results_file( + problem_id: int, sample_id: int, eval_result: KernelExecResult, eval_file_path: str +): """ Add evaluation result to eval results file TODO: migrate database support """ # Load existing results if file exists if os.path.exists(eval_file_path): - with open(eval_file_path, 'r') as f: + with open(eval_file_path, "r") as f: eval_results = json.load(f) else: eval_results = {} - + # Add new result eval_results[str(problem_id)] = { # assume 1 sample for now, will think about how to do this better for more samples - 'sample_id': sample_id, - 'compiled': eval_result.compiled, - 'correctness': eval_result.correctness, - 'metadata': check_metadata_serializable_all_types(eval_result.metadata), - 'runtime': eval_result.runtime, - 'runtime_stats': eval_result.runtime_stats, + "sample_id": sample_id, + "compiled": eval_result.compiled, + "correctness": eval_result.correctness, + "metadata": check_metadata_serializable_all_types(eval_result.metadata), + "runtime": eval_result.runtime, + "runtime_stats": eval_result.runtime_stats, } - + # Write updated results back to file if not os.path.exists(eval_file_path): os.makedirs(os.path.dirname(eval_file_path), exist_ok=True) - + with open(eval_file_path, "w") as f: json.dump(eval_results, f) -def single_eval_example(config: EvalConfig, curr_level_dataset: list[str], run_dir: str, eval_file_path ): + +def single_eval_example( + config: EvalConfig, curr_level_dataset: list[str], run_dir: str, eval_file_path +): device = torch.device("cuda:0") example_work = WorkArgs(problem_id=1, sample_id=0, device=device) # example_eval_result = evaluate_single_sample(example_work, config, curr_level_dataset, run_dir) - example_eval_result = cuda_single_eval_wrapper(example_work, config, curr_level_dataset, run_dir) + example_eval_result = cuda_single_eval_wrapper( + example_work, config, curr_level_dataset, run_dir + ) print(example_eval_result) if not check_if_eval_exists_local(1, 0, eval_file_path): add_to_eval_results_file(1, 0, example_eval_result, eval_file_path) @@ -385,7 +444,7 @@ def main(config: EvalConfig): Store Eval Results in specified eval results file """ print(f"Starting Batch Eval with config: {config}") - + # Check if CUDA is available if not torch.cuda.is_available(): raise RuntimeError("CUDA device not available. Evaluation requires GPU.") @@ -399,35 +458,44 @@ def main(config: EvalConfig): curr_level_dataset = dataset[f"level_{config.level}"] elif config.dataset_src == "local": curr_level_dataset = construct_kernelbench_dataset(config.level) - + num_problems_in_level = len(curr_level_dataset) if config.subset == (None, None): problem_id_range = range(1, num_problems_in_level) else: - assert config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level, f"Subset range {config.subset} out of range for Level {config.level}" + assert ( + config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level + ), f"Subset range {config.subset} out of range for Level {config.level}" problem_id_range = range(config.subset[0], config.subset[1]) - print(f"Evaluating 1 sample each for level {config.level} problems: {problem_id_range}") + print( + f"Evaluating 1 sample each for level {config.level} problems: {problem_id_range}" + ) run_dir = os.path.join(config.runs_dir, config.run_name) eval_file_path = os.path.join(run_dir, f"eval_results.json") - # set GPU arch to configure what target to build for set_gpu_arch(config.gpu_arch) - assert config.num_gpu_devices <= torch.cuda.device_count(), f"Number of GPUs requested ({config.num_gpu_devices}) is greater than the number of available GPUs ({torch.cuda.device_count()})" + assert ( + config.num_gpu_devices <= torch.cuda.device_count() + ), f"Number of GPUs requested ({config.num_gpu_devices}) is greater than the number of available GPUs ({torch.cuda.device_count()})" # To Debug # single_eval_example(config, curr_level_dataset, run_dir, eval_file_path) total_work = [] - for problem_id in range(problem_id_range.start, problem_id_range.stop + 1): # end index is inclusive - sample_id = 0 # only evaluate 1 sample for now + for problem_id in range( + problem_id_range.start, problem_id_range.stop + 1 + ): # end index is inclusive + sample_id = 0 # only evaluate 1 sample for now if not check_if_eval_exists_local(problem_id, sample_id, eval_file_path): total_work.append((problem_id, sample_id)) - print(f"Start evaluation on {len(total_work)} unevaluated samples in range: {problem_id_range}") + print( + f"Start evaluation on {len(total_work)} unevaluated samples in range: {problem_id_range}" + ) # Build Cache on CPU as that is faster if config.build_cache: batch_compile(total_work, config.to_dict()) @@ -438,4 +506,3 @@ def main(config: EvalConfig): if __name__ == "__main__": main() - \ No newline at end of file diff --git a/scripts/generate_and_eval_single_sample.py b/scripts/generate_and_eval_single_sample.py index e540f331..d6da57f4 100644 --- a/scripts/generate_and_eval_single_sample.py +++ b/scripts/generate_and_eval_single_sample.py @@ -8,7 +8,9 @@ from kernelbench.dataset import construct_kernelbench_dataset from kernelbench.eval import eval_kernel_against_ref -from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) from kernelbench.utils import extract_first_code, set_gpu_arch, read_file from kernelbench.frameworks import create_inference_server_from_presets @@ -21,15 +23,15 @@ torch.set_printoptions(precision=4, threshold=10) + class EvalConfig(Config): def __init__(self): - - self.dataset_src = REQUIRED # either huggingface or local + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" - # Problem Specification self.level = REQUIRED # NOTE: this is the logical index (problem id the problem_name)\ @@ -87,24 +89,31 @@ def main(config: EvalConfig): if config.log: os.makedirs(config.logdir, exist_ok=True) - + # Problem Checks num_problems = len(curr_level_dataset) print(f"Number of problems in Level {config.level}: {num_problems}") - print(f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}") - - assert config.problem_id <= num_problems, f"Problem ID {config.problem_id} out of range for Level {config.level}" + print( + f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}" + ) + assert ( + config.problem_id <= num_problems + ), f"Problem ID {config.problem_id} out of range for Level {config.level}" # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = config.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + config.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = curr_level_dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -113,24 +122,31 @@ def main(config: EvalConfig): # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + # 2. Generate Sample # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose, - time_generation=True) - - + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + time_generation=True, + ) custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - with open(os.path.join(config.logdir, f"prompt_level_{config.level}_problem_{config.problem_id}.txt"), "w") as f: + with open( + os.path.join( + config.logdir, + f"prompt_level_{config.level}_problem_{config.problem_id}.txt", + ), + "w", + ) as f: f.write(custom_cuda_prompt) # Query server with constructed prompt @@ -138,27 +154,45 @@ def main(config: EvalConfig): custom_cuda = extract_first_code(custom_cuda, ["python", "cpp"]) # check LLM is able to generate custom CUDA code assert custom_cuda is not None, "Custom CUDA code generation failed" - + # this should be optional if config.log: - with open(os.path.join(config.logdir, f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py"), "w") as f: + with open( + os.path.join( + config.logdir, + f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py", + ), + "w", + ) as f: f.write(custom_cuda) # 3. Evaluate Kernel # NOTE: no need to wrap around process here as only a single sample # see batch eval for examples of process isolation kernel_exec_result = eval_kernel_against_ref( - ref_arch_src, custom_cuda, verbose=config.verbose, measure_performance=True, num_correct_trials=5, num_perf_trials=100 + ref_arch_src, + custom_cuda, + verbose=config.verbose, + measure_performance=True, + num_correct_trials=5, + num_perf_trials=100, + ) + + print( + f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}" ) - - print(f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}") if config.log: - with open(os.path.join(config.logdir, f"eval_result_level_{config.level}_problem_{config.problem_id}.txt"), "a") as f: + with open( + os.path.join( + config.logdir, + f"eval_result_level_{config.level}_problem_{config.problem_id}.txt", + ), + "a", + ) as f: f.write(f"Problem Name: {problem_name}\n") f.write(str(kernel_exec_result)) if __name__ == "__main__": main() - diff --git a/scripts/generate_and_eval_single_sample_modal.py b/scripts/generate_and_eval_single_sample_modal.py index 5ac15cc8..03dafc5f 100644 --- a/scripts/generate_and_eval_single_sample_modal.py +++ b/scripts/generate_and_eval_single_sample_modal.py @@ -7,10 +7,16 @@ from datasets import load_dataset -#from src.dataset import construct_kernelbench_dataset +# from src.dataset import construct_kernelbench_dataset from src.eval import eval_kernel_against_ref from src.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template -from src.utils import extract_first_code, query_server, set_gpu_arch, read_file, create_inference_server_from_presets +from src.utils import ( + extract_first_code, + query_server, + set_gpu_arch, + read_file, + create_inference_server_from_presets, +) app = modal.App("eval_single_sample") @@ -23,18 +29,25 @@ torch.set_printoptions(precision=4, threshold=10) -gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} + class EvalConfig(Config): def __init__(self): self.weave_project = "generate_and_eval_single_sample_modal" - - self.dataset_src = REQUIRED # either huggingface or local + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" - # Problem Specification self.level = REQUIRED # NOTE: this is the logical index (problem id the problem_name)\ @@ -46,15 +59,14 @@ def __init__(self): # Construct this from mapping from architecture name to torch cuda arch list in the future # you can either specify SM version or just use the name self.gpu = "L40S" - self.gpu_arch = ['Ada'] - + self.gpu_arch = ["Ada"] # Inference config self.server_type = "anthropic" self.model_name = "claude-3-5-sonnet-20241022" self.max_tokens = 4096 self.temperature = 0.0 - + # Logging self.logdir = os.path.join(REPO_TOP_DIR, "results/eval_logs") self.verbose = False @@ -73,6 +85,7 @@ def verbose_logging(self): def __repr__(self): return f"EvalConfig({self.to_dict()})" + cuda_version = "12.4.0" # should be no greater than host CUDA version flavor = "devel" # includes full CUDA toolkit operating_sys = "ubuntu22.04" @@ -80,11 +93,7 @@ def __repr__(self): image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") - .apt_install("git", - "gcc-10", - "g++-10", - "clang" # note i skip a step - ) + .apt_install("git", "gcc-10", "g++-10", "clang") # note i skip a step .pip_install( # required to build flash-attn "anthropic", "numpy", @@ -103,6 +112,7 @@ def __repr__(self): ) ) + @app.cls(image=image) class EvalFunc: @@ -113,48 +123,62 @@ def eval_single_sample_modal(self, ref_arch_src, custom_cuda, verbose, gpu_arch) # see batch eval for examples of process isolation from src.eval import eval_kernel_against_ref from src.utils import set_gpu_arch + set_gpu_arch(gpu_arch) return eval_kernel_against_ref( - ref_arch_src, custom_cuda, verbose=verbose, measure_performance=True, num_correct_trials=5, num_perf_trials=100 + ref_arch_src, + custom_cuda, + verbose=verbose, + measure_performance=True, + num_correct_trials=5, + num_perf_trials=100, ) + @pydra.main(base=EvalConfig) def main(config: EvalConfig): - """ Keep it simple: Generate and evaluate a single sample """ import weave + weave.init(config.weave_project) print(f"Starting Eval with config: {config}") # Configurations - + if config.dataset_src == "huggingface": dataset = load_dataset(config.dataset_name) curr_level_dataset = dataset[f"level_{config.level}"] if config.log: os.makedirs(config.logdir, exist_ok=True) - + # Problem Checks num_problems = len(curr_level_dataset) print(f"Number of problems in Level {config.level}: {num_problems}") - print(f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}") - - assert config.problem_id <= num_problems, f"Problem ID {config.problem_id} out of range for Level {config.level}" + print( + f"Start Generation + Evaluation for Level {config.level} Problem {config.problem_id}" + ) + assert ( + config.problem_id <= num_problems + ), f"Problem ID {config.problem_id} out of range for Level {config.level}" # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = config.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + config.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = curr_level_dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -163,24 +187,31 @@ def main(config: EvalConfig): # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + # 2. Generate Sample # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose, - time_generation=True) - - + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + time_generation=True, + ) custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - with open(os.path.join(config.logdir, f"prompt_level_{config.level}_problem_{config.problem_id}.txt"), "w") as f: + with open( + os.path.join( + config.logdir, + f"prompt_level_{config.level}_problem_{config.problem_id}.txt", + ), + "w", + ) as f: f.write(custom_cuda_prompt) # Query server with constructed prompt @@ -188,21 +219,40 @@ def main(config: EvalConfig): custom_cuda = extract_first_code(custom_cuda, ["python", "cpp"]) # check LLM is able to generate custom CUDA code assert custom_cuda is not None, "Custom CUDA code generation failed" - + # this should be optional if config.log: - with open(os.path.join(config.logdir, f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py"), "w") as f: + with open( + os.path.join( + config.logdir, + f"generated_kernel_level_{config.level}_problem_{config.problem_id}.py", + ), + "w", + ) as f: f.write(custom_cuda) with app.run(): - kernel_exec_result = EvalFunc.with_options(gpu=config.gpu)().eval_single_sample_modal.remote(ref_arch_src, custom_cuda, config.verbose, gpu_arch_mapping[config.gpu]) - - print(f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}") - + kernel_exec_result = EvalFunc.with_options( + gpu=config.gpu + )().eval_single_sample_modal.remote( + ref_arch_src, custom_cuda, config.verbose, gpu_arch_mapping[config.gpu] + ) + + print( + f"Evaluation result for level {config.level} problem {config.problem_id}:\n{kernel_exec_result}" + ) + if config.log: - with open(os.path.join(config.logdir, f"eval_result_level_{config.level}_problem_{config.problem_id}.txt"), "a") as f: + with open( + os.path.join( + config.logdir, + f"eval_result_level_{config.level}_problem_{config.problem_id}.txt", + ), + "a", + ) as f: f.write(f"Problem Name: {problem_name}\n") f.write(str(kernel_exec_result)) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/generate_baseline_time.py b/scripts/generate_baseline_time.py index 186780dc..de7c8477 100644 --- a/scripts/generate_baseline_time.py +++ b/scripts/generate_baseline_time.py @@ -25,7 +25,7 @@ Torch Compile with various modes https://pytorch.org/docs/main/generated/torch.compile.html - torch.compile: backend="inductor", mode="default" (this is usually what happens when you do torch.compile(model)) -- torch.compile: backend="inductor", mode="reduce-overhead" +- torch.compile: backend="inductor", mode="reduce-overhead" - torch.compile: backend="inductor", mode="max-autotune" - torch.compile: backend="inductor", mode="max-autotune-no-cudagraphs" @@ -45,8 +45,9 @@ TIMING_DIR = os.path.join(REPO_TOP_PATH, "results", "timing") -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -57,14 +58,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] @@ -72,14 +73,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], def measure_program_time( - ref_arch_name: str, - ref_arch_src: str, - num_trials: int = 100, - use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - device: torch.device="cuda:0", - verbose: bool = False, + ref_arch_name: str, + ref_arch_src: str, + num_trials: int = 100, + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + device: torch.device = "cuda:0", + verbose: bool = False, ) -> dict: """ Measure the time of a KernelBench reference architecture @@ -106,13 +107,17 @@ def measure_program_time( # Initialize PyTorch model, use this for eager mode execution model = Model(*init_inputs) - + if use_torch_compile: - print(f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode") - model = torch.compile(model, backend=torch_compile_backend, mode=torch_compile_options) + print( + f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode" + ) + model = torch.compile( + model, backend=torch_compile_backend, mode=torch_compile_options + ) else: print(f"Using PyTorch Eager Execution on {ref_arch_name}") - + model = model.cuda(device=device) torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( @@ -122,25 +127,26 @@ def measure_program_time( if verbose: print(f"{ref_arch_name} {runtime_stats}") - + return runtime_stats except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - -def record_baseline_times(use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - file_name: str="baseline_time.json"): +def record_baseline_times( + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + file_name: str = "baseline_time.json", +): """ - Generate baseline time for KernelBench, + Generate baseline time for KernelBench, configure profiler options for PyTorch save to specified file """ device = torch.device("cuda:0") json_results = {} - + for level in [1, 2, 3]: PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) @@ -148,7 +154,9 @@ def record_baseline_times(use_torch_compile: bool = False, num_problems = len(dataset) for problem_id in tqdm(range(1, num_problems + 1)): - ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset(dataset, problem_id) + ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset( + dataset, problem_id + ) runtime_stats = measure_program_time( ref_arch_name=ref_arch_name, ref_arch_src=ref_arch_src, @@ -156,7 +164,7 @@ def record_baseline_times(use_torch_compile: bool = False, torch_compile_backend=torch_compile_backend, torch_compile_options=torch_compile_options, device=device, - verbose=False # do not print + verbose=False, # do not print ) json_results[f"level{level}"][ref_arch_name] = runtime_stats @@ -167,6 +175,7 @@ def record_baseline_times(use_torch_compile: bool = False, json.dump(json_results, f) return json_results + def test_measure_particular_program(level_num: int, problem_id: int): """ Test measure_program_time on a particular program @@ -176,7 +185,9 @@ def test_measure_particular_program(level_num: int, problem_id: int): PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level_num)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) - ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset(dataset, problem_id) + ref_arch_path, ref_arch_name, ref_arch_src = fetch_ref_arch_from_dataset( + dataset, problem_id + ) exec_stats = measure_program_time( ref_arch_name=ref_arch_name, @@ -185,7 +196,7 @@ def test_measure_particular_program(level_num: int, problem_id: int): torch_compile_backend="inductor", torch_compile_options="default", device=device, - verbose=False + verbose=False, ) print(f"Execution time for {ref_arch_name}: {exec_stats}") @@ -194,37 +205,49 @@ def test_measure_particular_program(level_num: int, problem_id: int): if __name__ == "__main__": # DEBUG and simple testing # test_measure_particular_program(2, 28) - - # Replace this with whatever hardware you are running on + + # Replace this with whatever hardware you are running on hardware_name = "L40S_matx3" - input(f"You are about to start recording baseline time for {hardware_name}, press Enter to continue...") + input( + f"You are about to start recording baseline time for {hardware_name}, press Enter to continue..." + ) # Systematic recording of baseline time if os.path.exists(os.path.join(TIMING_DIR, hardware_name)): - input(f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue...") + input( + f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue..." + ) # 1. Record Torch Eager - record_baseline_times(use_torch_compile=False, - torch_compile_backend=None, - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch.json") - - # 2. Record Torch Compile using Inductor - for torch_compile_mode in ["default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs"]: - record_baseline_times(use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options=torch_compile_mode, - file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json") - - # 3. Record Torch Compile using cudagraphs - record_baseline_times(use_torch_compile=True, - torch_compile_backend="cudagraphs", - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json") - + record_baseline_times( + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch.json", + ) + # 2. Record Torch Compile using Inductor + for torch_compile_mode in [ + "default", + "reduce-overhead", + "max-autotune", + "max-autotune-no-cudagraphs", + ]: + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options=torch_compile_mode, + file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json", + ) + # 3. Record Torch Compile using cudagraphs + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="cudagraphs", + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json", + ) # Random debuging # get_torch_compile_triton(2, 12) @@ -235,8 +258,6 @@ def test_measure_particular_program(level_num: int, problem_id: int): # get_time(2, 43, torch_compile=True) - - ################################################################################ # Deprecated ################################################################################ @@ -268,7 +289,7 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): for x in init_inputs ] model = Model(*init_inputs) - + if torch_compile: model = torch.compile(model) print("Compiled model Done") @@ -283,5 +304,3 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): return (ref_arch_name, runtime_stats) except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - - diff --git a/scripts/generate_baseline_time_modal.py b/scripts/generate_baseline_time_modal.py index 6d05244f..37a83d5e 100644 --- a/scripts/generate_baseline_time_modal.py +++ b/scripts/generate_baseline_time_modal.py @@ -27,7 +27,7 @@ Torch Compile with various modes https://pytorch.org/docs/main/generated/torch.compile.html - torch.compile: backend="inductor", mode="default" (this is usually what happens when you do torch.compile(model)) -- torch.compile: backend="inductor", mode="reduce-overhead" +- torch.compile: backend="inductor", mode="reduce-overhead" - torch.compile: backend="inductor", mode="max-autotune" - torch.compile: backend="inductor", mode="max-autotune-no-cudagraphs" @@ -48,8 +48,17 @@ # Modal Infra import modal + app = modal.App("generate_baseline_modal") -gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "A100-80GB": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "A100-80GB": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} batch_size = 10 gpu = "L40S" timeout = 1800 @@ -60,11 +69,7 @@ image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") - .apt_install("git", - "gcc-10", - "g++-10", - "clang" # note i skip a step - ) + .apt_install("git", "gcc-10", "g++-10", "clang") # note i skip a step .pip_install( # required to build flash-attn "anthropic", "numpy", @@ -82,12 +87,10 @@ "utils", "einops", ) - .add_local_dir( - KERNEL_BENCH_PATH, - remote_path="/root/KernelBench" - ) + .add_local_dir(KERNEL_BENCH_PATH, remote_path="/root/KernelBench") ) + def write_batch_to_json(entries_to_write: list, f_path: str): """ Write batch of data to JSON file (append or overwrite, do not completely overwrite) @@ -95,11 +98,11 @@ def write_batch_to_json(entries_to_write: list, f_path: str): # Read existing data if file exists existing_data = {} if os.path.exists(f_path): - with open(f_path, 'r') as f_r: + with open(f_path, "r") as f_r: existing_data = json.load(f_r) - + # Add new entries - for (level, problem, entry) in entries_to_write: + for level, problem, entry in entries_to_write: # Initialize nested structure if it doesn't exist if str(level) not in existing_data: existing_data[level] = {} @@ -112,11 +115,13 @@ def write_batch_to_json(entries_to_write: list, f_path: str): # Write back combined data with open(f_path, "w") as f_w: json.dump(existing_data, f_w, indent=4) - + print(f"[INFO] Wrote {len(entries_to_write)} entries to {f_path}") -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: + +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -127,33 +132,36 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] return (ref_arch_path, ref_arch_name, ref_arch_src) + @app.cls(image=image, container_idle_timeout=5) class EvalFunc: @modal.method() def measure_program_time( - self, - ref_arch_name: str, - ref_arch_src: str, - num_trials: int = 100, - use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - device: torch.device = torch.cuda.current_device() if torch.cuda.is_available() else None, - verbose: bool = False, + self, + ref_arch_name: str, + ref_arch_src: str, + num_trials: int = 100, + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + device: torch.device = ( + torch.cuda.current_device() if torch.cuda.is_available() else None + ), + verbose: bool = False, ): """ Measure the time of a KernelBench reference architecture @@ -180,37 +188,51 @@ def measure_program_time( # Initialize PyTorch model, use this for eager mode execution model = Model(*init_inputs) - + if use_torch_compile: - print(f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode") - model = torch.compile(model, backend=torch_compile_backend, mode=torch_compile_options) + print( + f"Using torch.compile to compile model {ref_arch_name} with {torch_compile_backend} backend and {torch_compile_options} mode" + ) + model = torch.compile( + model, backend=torch_compile_backend, mode=torch_compile_options + ) else: print(f"Using PyTorch Eager Execution on {ref_arch_name}") - + model = model.cuda(device=device) torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( - model, *inputs, num_trials=num_trials, verbose=verbose, device=device + model, + *inputs, + num_trials=num_trials, + verbose=verbose, + device=device, ) runtime_stats = get_timing_stats(elapsed_times, device=device) if verbose: print(f"{ref_arch_name} {runtime_stats}") - + return runtime_stats except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") + def measure_program_time_wrapper(*args, **kwargs): with app.run(): - return EvalFunc.with_options(gpu=gpu)().measure_program_time.remote(*args, **kwargs) + return EvalFunc.with_options(gpu=gpu)().measure_program_time.remote( + *args, **kwargs + ) -def record_baseline_times(use_torch_compile: bool = False, - torch_compile_backend: str="inductor", - torch_compile_options: str="default", - file_name: str="baseline_time.json"): + +def record_baseline_times( + use_torch_compile: bool = False, + torch_compile_backend: str = "inductor", + torch_compile_options: str = "default", + file_name: str = "baseline_time.json", +): """ - Generate baseline time for KernelBench, + Generate baseline time for KernelBench, configure profiler options for PyTorch save to specified file """ @@ -220,12 +242,17 @@ def record_baseline_times(use_torch_compile: bool = False, PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) num_problems = len(dataset) - total_work = [(i, *fetch_ref_arch_from_dataset(dataset, i)) for i in list(range(1, num_problems + 1))] + total_work = [ + (i, *fetch_ref_arch_from_dataset(dataset, i)) + for i in list(range(1, num_problems + 1)) + ] with tqdm(total=len(total_work), desc="Processing batches") as pbar: while len(total_work) > 0: curr_work_batch = total_work[:batch_size] - total_work = total_work[batch_size:] # pop the first batch_size elements + total_work = total_work[ + batch_size: + ] # pop the first batch_size elements with mp.Pool() as pool: @@ -238,9 +265,14 @@ def record_baseline_times(use_torch_compile: bool = False, torch_compile_backend, torch_compile_options, torch.device(f"cuda:0"), - False # do not print + False, # do not print ) - for i, (p_id, ref_arch_path, ref_arch_name, ref_arch_src) in enumerate(curr_work_batch) + for i, ( + p_id, + ref_arch_path, + ref_arch_name, + ref_arch_src, + ) in enumerate(curr_work_batch) ] start_time = time.time() @@ -259,14 +291,16 @@ def record_baseline_times(use_torch_compile: bool = False, elapsed_time = time.time() - start_time remaining_time = max(0, batch_timeout - elapsed_time) result = async_result.get(timeout=remaining_time) - json_results.append((f"level{level}", ref_arch_name, result)) - + json_results.append( + (f"level{level}", ref_arch_name, result) + ) + except mp.TimeoutError: print( f"[WARNING] Evaluation TIMED OUT for Problem ID: {problem_id}" ) json_results.append((f"level{level}", ref_arch_name, None)) - + except Exception as e: print( f"[ERROR] Evaluation FAILED for Problem ID: {problem_id}: {str(e)}" @@ -284,7 +318,7 @@ def record_baseline_times(use_torch_compile: bool = False, # DEBUG and simple testing # test_measure_particular_program(2, 28) gpu = "A10G" - # Replace this with whatever hardware you are running on + # Replace this with whatever hardware you are running on hardware_name = f"{gpu}_modal" print(f"Generating baseline time for {hardware_name}") # input(f"You are about to start recording baseline time for {hardware_name}, press Enter to continue...") @@ -294,31 +328,32 @@ def record_baseline_times(use_torch_compile: bool = False, # input(f"Directory {hardware_name} already exists, Are you sure you want to overwrite? Enter to continue...") # 1. Record Torch Eager - record_baseline_times(use_torch_compile=False, - torch_compile_backend=None, - torch_compile_options=None, - file_name=f"{hardware_name}/baseline_time_torch.json") - - record_baseline_times(use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options="default", - file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_default.json") - + record_baseline_times( + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + file_name=f"{hardware_name}/baseline_time_torch.json", + ) + + record_baseline_times( + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_default.json", + ) + # 2. Record Torch Compile using Inductor # for torch_compile_mode in ["default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs"]: - # record_baseline_times(use_torch_compile=True, + # record_baseline_times(use_torch_compile=True, # torch_compile_backend="inductor", - # torch_compile_options=torch_compile_mode, + # torch_compile_options=torch_compile_mode, # file_name=f"{hardware_name}/baseline_time_torch_compile_inductor_{torch_compile_mode}.json") - + # 3. Record Torch Compile using cudagraphs - # record_baseline_times(use_torch_compile=True, + # record_baseline_times(use_torch_compile=True, # torch_compile_backend="cudagraphs", - # torch_compile_options=None, + # torch_compile_options=None, # file_name=f"{hardware_name}/baseline_time_torch_compile_cudagraphs.json") - - - # Random debuging # get_torch_compile_triton(2, 12) @@ -329,8 +364,6 @@ def record_baseline_times(use_torch_compile: bool = False, # get_time(2, 43, torch_compile=True) - - ################################################################################ # Deprecated ################################################################################ @@ -362,7 +395,7 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): for x in init_inputs ] model = Model(*init_inputs) - + if torch_compile: model = torch.compile(model) print("Compiled model Done") @@ -377,5 +410,3 @@ def get_time_old(level_num, problem_id, num_trials=100, torch_compile=False): return (ref_arch_name, runtime_stats) except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") - - diff --git a/scripts/generate_samples.py b/scripts/generate_samples.py index 2f4d24fb..a079911e 100644 --- a/scripts/generate_samples.py +++ b/scripts/generate_samples.py @@ -7,7 +7,9 @@ from datasets import load_dataset from kernelbench.dataset import construct_kernelbench_dataset -from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) from kernelbench.utils import read_file, maybe_multithread from kernelbench.llm_utils import create_inference_server_from_presets @@ -21,21 +23,25 @@ torch.set_printoptions(precision=4, threshold=10) + class GenerationConfig(Config): def __init__(self): - - self.dataset_src = REQUIRED # either huggingface or local + + self.dataset_src = REQUIRED # either huggingface or local # name of dataset name on Hugging Face self.dataset_name = "ScalingIntelligence/KernelBench" # Problem Specification self.level = REQUIRED - + # subset of problems to generate, otherwise generate on all problems in the level - self.subset = (None, None) # (problem_id, problem_name), these are the logical index + self.subset = ( + None, + None, + ) # (problem_id, problem_name), these are the logical index - self.run_name = REQUIRED # name of the run + self.run_name = REQUIRED # name of the run # num of thread pool to call inference server in parallel self.num_workers = 1 @@ -46,13 +52,13 @@ def __init__(self): self.model_name = "deepseek-coder" self.max_tokens = 4096 self.temperature = 0.0 - + # Logging # Top Directory to Store Runs self.runs_dir = os.path.join(REPO_TOP_DIR, "runs") - + self.verbose = False - self.store_type = "local" # TODO: add Database Integration + self.store_type = "local" # TODO: add Database Integration # Future support # Migrate Monkeys code base to KernelBench @@ -66,23 +72,34 @@ def greedy(self): def __repr__(self): return f"EvalConfig({self.to_dict()})" - + @dataclass class WorkArgs: - problem_id: int # logically indexed + problem_id: int # logically indexed sample_id: int -def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, inference_server: callable, run_dir: str) -> bool: + +def generate_sample_single( + work: WorkArgs, + config: GenerationConfig, + dataset, + inference_server: callable, + run_dir: str, +) -> bool: # 1. Fetch Problem if config.dataset_src == "huggingface": - curr_problem_row = dataset.filter(lambda x: x["problem_id"] == work.problem_id, desc=None) + curr_problem_row = dataset.filter( + lambda x: x["problem_id"] == work.problem_id, desc=None + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] elif config.dataset_src == "local": - problem_idx_in_dataset = work.problem_id - 1 # due to dataset list being 0-indexed locally + problem_idx_in_dataset = ( + work.problem_id - 1 + ) # due to dataset list being 0-indexed locally ref_arch_path = dataset[problem_idx_in_dataset] problem_name = os.path.basename(ref_arch_path) @@ -90,14 +107,17 @@ def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, in # Extract problem number from problem name (e.g. "1" from "1_Square_matrix_multiplication_.py") problem_number = int(problem_name.split("_")[0]) - assert problem_number == work.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - + assert ( + problem_number == work.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - # Construct Prompt + # Construct Prompt custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(ref_arch_src) if config.log_prompt: - prompt_path = os.path.join(run_dir, f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_prompt.txt") + prompt_path = os.path.join( + run_dir, + f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_prompt.txt", + ) with open(prompt_path, "w") as f: f.write(custom_cuda_prompt) @@ -108,17 +128,28 @@ def generate_sample_single(work: WorkArgs, config: GenerationConfig, dataset, in assert custom_cuda is not None, "Custom CUDA code generation failed" if config.verbose: - print(f"Generated sample {work.sample_id} for problem {problem_number}: {problem_name}") + print( + f"Generated sample {work.sample_id} for problem {problem_number}: {problem_name}" + ) # Store to local file - kernel_path = os.path.join(run_dir, f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_kernel.py") + kernel_path = os.path.join( + run_dir, + f"level_{config.level}_problem_{work.problem_id}_sample_{work.sample_id}_kernel.py", + ) with open(kernel_path, "w") as f: f.write(custom_cuda) - + return True - -def generate_sample_launcher(work: WorkArgs, config: GenerationConfig, dataset, inference_server: callable, run_dir: str): + +def generate_sample_launcher( + work: WorkArgs, + config: GenerationConfig, + dataset, + inference_server: callable, + run_dir: str, +): try: return generate_sample_single(work, config, dataset, inference_server, run_dir) except Exception as e: @@ -126,13 +157,17 @@ def generate_sample_launcher(work: WorkArgs, config: GenerationConfig, dataset, return None -def check_kernel_exists(run_dir: str, level: int, problem_id: int, sample_id: int) -> bool: +def check_kernel_exists( + run_dir: str, level: int, problem_id: int, sample_id: int +) -> bool: """ Check if a kernel for a given problem and sample ID already exists in the run directory """ - kernel_path = os.path.join(run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py") + kernel_path = os.path.join( + run_dir, f"level_{level}_problem_{problem_id}_sample_{sample_id}_kernel.py" + ) return os.path.exists(kernel_path) - + @pydra.main(base=GenerationConfig) def main(config: GenerationConfig): @@ -149,63 +184,69 @@ def main(config: GenerationConfig): elif config.dataset_src == "local": curr_level_dataset = construct_kernelbench_dataset(config.level) - num_problems_in_level = len(curr_level_dataset) if config.subset == (None, None): problem_id_range = range(1, num_problems_in_level) else: - assert config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level, f"Subset range {config.subset} out of range for Level {config.level}" + assert ( + config.subset[0] >= 1 and config.subset[1] <= num_problems_in_level + ), f"Subset range {config.subset} out of range for Level {config.level}" problem_id_range = range(config.subset[0], config.subset[1]) - print(f"Generating on 1 sample each for level {config.level} problems: {problem_id_range}") + print( + f"Generating on 1 sample each for level {config.level} problems: {problem_id_range}" + ) # set up run directory run_dir = os.path.join(config.runs_dir, config.run_name) os.makedirs(run_dir, exist_ok=True) pydra.save_yaml(config.to_dict(), os.path.join(run_dir, "generation_config.yaml")) - - assert config.store_type == "local", "supporting local file-system based storage for now" # database integreation coming soon, need to migrate from CUDA Monkeys code + assert ( + config.store_type == "local" + ), "supporting local file-system based storage for now" # database integreation coming soon, need to migrate from CUDA Monkeys code problems_to_run = [] - for problem_id in range(problem_id_range.start, problem_id_range.stop + 1): # end index is inclusive + for problem_id in range( + problem_id_range.start, problem_id_range.stop + 1 + ): # end index is inclusive # assume sample id is 0 for now if not check_kernel_exists(run_dir, config.level, problem_id, sample_id=0): problems_to_run.append( - WorkArgs( - problem_id=int(problem_id), - sample_id=0 # fix to 0 for now - ) - ) - + WorkArgs(problem_id=int(problem_id), sample_id=0) # fix to 0 for now + ) # Create inference function with config parameters # We provide some presets in utils but you can also pass in your own, see query_server for more details - inference_server = create_inference_server_from_presets(server_type=config.server_type, - model_name=config.model_name, - temperature=config.temperature, - max_tokens=config.max_tokens, - verbose=config.verbose) + inference_server = create_inference_server_from_presets( + server_type=config.server_type, + model_name=config.model_name, + temperature=config.temperature, + max_tokens=config.max_tokens, + verbose=config.verbose, + ) # Launch workers - generation_results = maybe_multithread(generate_sample_launcher, - problems_to_run, - config.num_workers, - time_interval=config.api_query_interval, - # extra args - config=config, - dataset=curr_level_dataset, - inference_server=inference_server, - run_dir=run_dir - ) - + generation_results = maybe_multithread( + generate_sample_launcher, + problems_to_run, + config.num_workers, + time_interval=config.api_query_interval, + # extra args + config=config, + dataset=curr_level_dataset, + inference_server=inference_server, + run_dir=run_dir, + ) + num_generated_samples = len(generation_results) total_problems = len(problems_to_run) num_failed_problems = total_problems - num_generated_samples - print(f"Generated {num_generated_samples} samples for total {total_problems} problems, Please retry for the {num_failed_problems} failed problems.") + print( + f"Generated {num_generated_samples} samples for total {total_problems} problems, Please retry for the {num_failed_problems} failed problems." + ) if __name__ == "__main__": main() - diff --git a/scripts/inspect_baseline.py b/scripts/inspect_baseline.py index 111303d3..8c0ee428 100644 --- a/scripts/inspect_baseline.py +++ b/scripts/inspect_baseline.py @@ -22,27 +22,35 @@ KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") -assert torch.cuda.get_device_capability() >= (7, 0), "torch.compile is not supported on this device." +assert torch.cuda.get_device_capability() >= ( + 7, + 0, +), "torch.compile is not supported on this device." + def get_toy_torch_compile_fn_and_inputs(): @torch.compile() def fn(x, y): z = x + y return z + 2 + inputs = (torch.ones(2, 2, device="cuda"), torch.zeros(2, 2, device="cuda")) return fn, inputs -def inspect_torch_compile(fn, inputs, output_dir="results/triton_code", filename="optimized_kernel"): +def inspect_torch_compile( + fn, inputs, output_dir="results/triton_code", filename="optimized_kernel" +): """ Benchmark a torch.compile'd function by viewing dynamo tracing, traced graph, fusion decisions and generated code. - + Args: fn: The compiled function to benchmark inputs: Tuple of input tensors to the function output_dir: Directory to save generated code """ + def separator(name): print(f"==================={name}=========================") torch._dynamo.reset() @@ -52,7 +60,7 @@ def separator(name): torch._logging.set_logs(dynamo=logging.DEBUG) fn(*inputs) - separator("Traced Graph") + separator("Traced Graph") # View traced graph torch._logging.set_logs(graph=True) fn(*inputs) @@ -65,34 +73,36 @@ def separator(name): separator("Output Code") # View output code generated by inductor os.makedirs(output_dir, exist_ok=True) - + # Create a custom logging handler to capture the output class OutputCodeHandler(logging.Handler): def __init__(self, file): super().__init__() self.file = file - + def emit(self, record): - self.file.write(self.format(record) + '\n') + self.file.write(self.format(record) + "\n") with open(f"{output_dir}/{filename}.py", "w") as f: # Set up logging handler handler = OutputCodeHandler(f) logging.getLogger("torch._inductor.codecache").addHandler(handler) - + torch._logging.set_logs(output_code=True) fn(*inputs) # Run the function - + # Clean up handler logging.getLogger("torch._inductor.codecache").removeHandler(handler) separator("") - + + def fetch_ref_arch_from_level_problem_id(level_num, problem_id, with_name=False): PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level_num)) dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) + def inspect_torch_compile_triton(level_num, problem_id): ref_arch_name, ref_arch_src = fetch_ref_arch_from_level_problem_id( level_num, problem_id, with_name=True @@ -134,10 +144,15 @@ def inspect_baseline_torch_compile(level_num, problem_id): for x in init_inputs ] model = Model(*init_inputs) - + model = torch.compile(model) model = model.cuda(device=device) - inspect_torch_compile(model, inputs, output_dir="results/triton_code", filename=f"level{level_num}_problem{problem_id}_triton") + inspect_torch_compile( + model, + inputs, + output_dir="results/triton_code", + filename=f"level{level_num}_problem{problem_id}_triton", + ) except Exception as e: print(f"[Eval] Error in Inspecting Torch Compile: {e}") @@ -146,5 +161,3 @@ def inspect_baseline_torch_compile(level_num, problem_id): # fn, inputs = get_toy_torch_compile_fn_and_inputs() inspect_baseline_torch_compile(2, 43) - - diff --git a/scripts/inspect_kernel_pytorch_profiler.py b/scripts/inspect_kernel_pytorch_profiler.py index c5023fa0..4ba796c5 100644 --- a/scripts/inspect_kernel_pytorch_profiler.py +++ b/scripts/inspect_kernel_pytorch_profiler.py @@ -23,13 +23,16 @@ device = "cuda:0" -def get_torch_profiler_info(ref_arch_src: str, - kernel_src: str, - build_dir: str, - device: torch.device, - num_trials: int = 100, - table_row_limit: int = 10, - seed_num: int = 42)->str: + +def get_torch_profiler_info( + ref_arch_src: str, + kernel_src: str, + build_dir: str, + device: torch.device, + num_trials: int = 100, + table_row_limit: int = 10, + seed_num: int = 42, +) -> str: """ Get the profiler info for a particular kernel Given a KernelBench solution to a problem, we want to profile the kernel @@ -44,9 +47,9 @@ def get_torch_profiler_info(ref_arch_src: str, Notes about profiling: - - We do not set p.toggle_collection_dynamic explicitly, + - We do not set p.toggle_collection_dynamic explicitly, - We only collect CUDA activity (ProfilerActivity.CUDA), as we are only interested in the kernel - + """ assert torch.cuda.is_available(), "CUDA is not available, cannot run Torch Profiler" @@ -60,14 +63,12 @@ def get_torch_profiler_info(ref_arch_src: str, inputs = get_inputs() init_inputs = get_init_inputs() inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs ] init_inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in init_inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs ] - + ModelNew = load_custom_model(kernel_src, context, build_dir) # construct the new model with init inputs model = ModelNew(*init_inputs) @@ -76,7 +77,6 @@ def get_torch_profiler_info(ref_arch_src: str, model = model.cuda(device=device) - with torch.no_grad(): profiling_scheduler = torch.profiler.schedule( skip_first=2, @@ -90,20 +90,26 @@ def get_torch_profiler_info(ref_arch_src: str, schedule=profiling_scheduler, ) as prof: for _ in range(num_trials): - + output = model(*inputs) prof.step() - profiler_output = prof.key_averages().table(sort_by='cuda_time_total', - row_limit=table_row_limit) - + profiler_output = prof.key_averages().table( + sort_by="cuda_time_total", row_limit=table_row_limit + ) + return profiler_output - + + def __main__(): # run_profile(dataset, problem_id, num_trials=10) - ref_arch_src_path = os.path.join(REPO_ROOT, "src/prompts/few_shot/model_ex_mnist2.py") - kernel_src_path = os.path.join(REPO_ROOT, "src/prompts/few_shot/model_new_ex_mnist2.py") + ref_arch_src_path = os.path.join( + REPO_ROOT, "src/prompts/few_shot/model_ex_mnist2.py" + ) + kernel_src_path = os.path.join( + REPO_ROOT, "src/prompts/few_shot/model_new_ex_mnist2.py" + ) ref_arch_src = read_file(ref_arch_src_path) kernel_src = read_file(kernel_src_path) @@ -115,11 +121,14 @@ def __main__(): device="cuda:0", num_trials=20, seed_num=42, - table_row_limit=10 + table_row_limit=10, ) - + print(profile_result) - print(f"Profiler result could be parsed as a string of length {len(profile_result)}") + print( + f"Profiler result could be parsed as a string of length {len(profile_result)}" + ) + if __name__ == "__main__": - __main__() \ No newline at end of file + __main__() diff --git a/scripts/inspect_triton.py b/scripts/inspect_triton.py index 9f30a14e..7d1baf93 100644 --- a/scripts/inspect_triton.py +++ b/scripts/inspect_triton.py @@ -25,8 +25,9 @@ device = "cuda:0" -def fetch_ref_arch_from_dataset(dataset: list[str], - problem_id: int) -> tuple[str, str, str]: +def fetch_ref_arch_from_dataset( + dataset: list[str], problem_id: int +) -> tuple[str, str, str]: """ Fetch the reference architecture from the problem directory problem_id should be logical index (1-indexed), matching the problem_id in the problem_name @@ -37,14 +38,14 @@ def fetch_ref_arch_from_dataset(dataset: list[str], ref_arch_src: str, the source code of the reference architecture """ ref_arch_path = None - + for file in dataset: if file.split("/")[-1].split("_")[0] == str(problem_id): ref_arch_path = file break if ref_arch_path is None: raise ValueError(f"No reference architecture found for problem_id {problem_id}") - + ref_arch_src = read_file(ref_arch_path) ref_arch_name = ref_arch_path.split("/")[-1] @@ -76,22 +77,21 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 set_seed(42) init_inputs = get_init_inputs() inputs = [ - x.cuda(device=device) if isinstance(x, torch.Tensor) else x - for x in inputs + x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in inputs ] init_inputs = [ x.cuda(device=device) if isinstance(x, torch.Tensor) else x for x in init_inputs ] - + # Create base model model = Model(*init_inputs) model = model.cuda(device=device) - + # Profile non-compiled model with profile( activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], - schedule=profiling_scheduler + schedule=profiling_scheduler, ) as prof: with record_function("non_compiled_forward"): for _ in range(num_trials): @@ -99,7 +99,7 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 prof.step() print(f"\nProfiling results for non-compiled model:") print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10)) - + # Profile compiled model model_compiled = torch.compile(model) with profile( @@ -110,14 +110,16 @@ def run_profile_and_save_trace(dataset: list[str], problem_id: int, num_trials=1 model_compiled(*inputs) prof_compiled.step() print(f"\nProfiling results for compiled model:") - print(prof_compiled.key_averages().table(sort_by="cuda_time_total", row_limit=10)) - + print( + prof_compiled.key_averages().table(sort_by="cuda_time_total", row_limit=10) + ) prof.export_chrome_trace("trace_non_compiled.json") prof_compiled.export_chrome_trace("trace_compiled.json") # except Exception as e: - # print(f"[Eval] Error in Measuring Performance: {e}") + # print(f"[Eval] Error in Measuring Performance: {e}") + def get_torch_compile_triton(level_num, problem_id): """ @@ -149,21 +151,22 @@ def get_torch_compile_triton(level_num, problem_id): model = Model(*init_inputs) # output triton code - log_file = f"results/triton_code/level{level_num}_problem_{problem_id}_triton.log" + log_file = ( + f"results/triton_code/level{level_num}_problem_{problem_id}_triton.log" + ) os.makedirs(os.path.dirname(log_file), exist_ok=True) logging.basicConfig(filename=log_file, level=logging.DEBUG) - # TODO: Figure out a way to save to a file + # TODO: Figure out a way to save to a file torch._logging.set_logs(output_code=True) # Call torch compile - model =torch.compile(model, backend="inductor") + model = torch.compile(model, backend="inductor") - # reduce overhead -> + # reduce overhead -> # model = torch.compile(model, mode="") - + model = model.cuda(device=device) - torch.cuda.synchronize(device=device) elapsed_times = time_execution_with_cuda_event( @@ -172,6 +175,6 @@ def get_torch_compile_triton(level_num, problem_id): runtime_stats = get_timing_stats(elapsed_times, device=device) # json_results[f"level{level_num}"][ref_arch_name] = runtime_stats print(f"{ref_arch_name} {runtime_stats}") - return (ref_arch_name) + return ref_arch_name except Exception as e: print(f"[Eval] Error in Measuring Performance: {e}") diff --git a/scripts/run_and_check.py b/scripts/run_and_check.py index 539bb623..ab643371 100644 --- a/scripts/run_and_check.py +++ b/scripts/run_and_check.py @@ -35,12 +35,13 @@ torch.set_printoptions(precision=4, threshold=10) + class ScriptConfig(Config): def __init__(self): # Problem and Solution definition # Input src origin definition - self.ref_origin = REQUIRED # either local or kernelbench + self.ref_origin = REQUIRED # either local or kernelbench # ref_origin is local, specify local file path self.ref_arch_src_path = "" # ref_origin is kernelbench, specify level and problem id @@ -50,7 +51,6 @@ def __init__(self): # Solution src definition self.kernel_src_path = "" - # KernelBench Eval specific # number of trials to run for correctness self.num_correct_trials = 5 @@ -61,61 +61,68 @@ def __init__(self): # verbose logging self.verbose = False self.measure_performance = True - self.build_dir_prefix = "" # if you want to specify a custom build directory - self.clear_cache = False # TODO + self.build_dir_prefix = "" # if you want to specify a custom build directory + self.clear_cache = False # TODO # Replace with your NVIDIA GPU architecture, e.g. ["Hopper"] - self.gpu_arch = ["Ada"] + self.gpu_arch = ["Ada"] def __repr__(self): return f"ScriptConfig({self.to_dict()})" -def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: + +def evaluate_single_sample_src( + ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device +) -> KernelExecResult: """ Evaluate a single sample source code against a reference source code """ kernel_hash = str(hash(kernel_src)) build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - - if configs["clear_cache"]: # fresh kernel build + + if configs["clear_cache"]: # fresh kernel build print(f"[INFO] Clearing cache for build directory: {build_dir}") shutil.rmtree(build_dir, ignore_errors=True) - + num_correct_trials = configs["num_correct_trials"] - num_perf_trials = configs["num_perf_trials"] + num_perf_trials = configs["num_perf_trials"] verbose = configs["verbose"] measure_performance = configs["measure_performance"] try: eval_result = eval_kernel_against_ref( - original_model_src=ref_arch_src, + original_model_src=ref_arch_src, custom_model_src=kernel_src, measure_performance=measure_performance, verbose=verbose, num_correct_trials=num_correct_trials, num_perf_trials=num_perf_trials, build_dir=build_dir, - device=device + device=device, ) return eval_result except Exception as e: print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") - if "CUDA error" in str(e): + if "CUDA error" in str(e): # NOTE: count this as compilation failure as it is not runnable code - metadata = {"cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } - eval_result = KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } - eval_result = KernelExecResult(compiled=False, correctness=False, - metadata=metadata) + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) return eval_result @@ -126,9 +133,11 @@ def main(config: ScriptConfig): # Fetch reference and kernel code - assert config.ref_origin == "local" or config.ref_origin == "kernelbench", "ref_origin must be either local or kernelbench" - assert config.kernel_src_path != "", "kernel_src_path is required" - + assert ( + config.ref_origin == "local" or config.ref_origin == "kernelbench" + ), "ref_origin must be either local or kernelbench" + assert config.kernel_src_path != "", "kernel_src_path is required" + if config.ref_origin == "local": assert config.ref_arch_src_path != "", "ref_arch_src_path is required" ref_arch_src = read_file(config.ref_arch_src_path) @@ -141,23 +150,28 @@ def main(config: ScriptConfig): dataset = load_dataset(config.dataset_name) curr_level_dataset = dataset[f"level_{config.level}"] - curr_problem_row = curr_level_dataset.filter(lambda x: x["problem_id"] == config.problem_id) + curr_problem_row = curr_level_dataset.filter( + lambda x: x["problem_id"] == config.problem_id + ) ref_arch_src = curr_problem_row["code"][0] problem_name = curr_problem_row["name"][0] problem_number = int(problem_name.split("_")[0]) - assert problem_number == config.problem_id, f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" - - print(f"Fetched problem {config.problem_id} from KernelBench level {config.level}: {problem_name}") + assert ( + problem_number == config.problem_id + ), f"Problem number in filename ({problem_number}) does not match config problem_id ({config.problem_id})" + print( + f"Fetched problem {config.problem_id} from KernelBench level {config.level}: {problem_name}" + ) else: raise ValueError("Invalid ref_origin") - + kernel_src = read_file(config.kernel_src_path) # Start Evaluation - device = torch.device("cuda:0") # default device + device = torch.device("cuda:0") # default device set_gpu_arch(config.gpu_arch) print("[INFO] Evaluating kernel against reference code") @@ -166,46 +180,54 @@ def main(config: ScriptConfig): ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=config.to_dict(), - device=device + device=device, ) kernel_exec_time = kernel_eval_result.runtime # Measure baseline time print("[INFO] Measuring reference program time") # Default using PyTorch Eager here - ref_time_eager_result = measure_program_time(ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, - num_trials=config.num_perf_trials, - use_torch_compile=False, - device=device) + ref_time_eager_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=False, + device=device, + ) ref_exec_eager_time = ref_time_eager_result.get("mean", None) # Measure Torch Compile time - ref_time_compile_result = measure_program_time(ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, - num_trials=config.num_perf_trials, - use_torch_compile=True, - torch_compile_backend="inductor", - torch_compile_options="default", - device=device) + ref_time_compile_result = measure_program_time( + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, + num_trials=config.num_perf_trials, + use_torch_compile=True, + torch_compile_backend="inductor", + torch_compile_options="default", + device=device, + ) ref_exec_compile_time = ref_time_compile_result.get("mean", None) - print("="*40) + print("=" * 40) print(f"[Eval] Kernel eval result: {kernel_eval_result}") - print("-"*40) + print("-" * 40) print(f"[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms") print(f"[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms") print(f"[Timing] Custom Kernel exec time: {kernel_exec_time} ms") - print("-"*40) - + print("-" * 40) + if kernel_eval_result.correctness: - print(f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x") - print(f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x") + print( + f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x" + ) + print( + f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x" + ) else: print("[Speedup] Speedup Not Available as Kernel did not pass correctness") - print("="*40) + print("=" * 40) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py index 1a411cab..153bc39e 100644 --- a/scripts/run_and_check_modal.py +++ b/scripts/run_and_check_modal.py @@ -16,15 +16,18 @@ from kernelbench.eval import eval_kernel_against_ref, KernelExecResult from kernelbench.utils import read_file, set_gpu_arch -def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: + +def evaluate_single_sample_src( + ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device +) -> KernelExecResult: """Evaluate a single sample source code against a reference source code""" kernel_hash = str(hash(kernel_src)) build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - + if configs["clear_cache"]: print(f"[INFO] Clearing cache for build directory: {build_dir}") shutil.rmtree(build_dir, ignore_errors=True) - + try: eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, @@ -34,23 +37,26 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict num_correct_trials=configs["num_correct_trials"], num_perf_trials=configs["num_perf_trials"], build_dir=build_dir, - device=device + device=device, ) return eval_result except Exception as e: print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") - if "CUDA error" in str(e): - metadata = {"cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } + if "CUDA error" in str(e): + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } return KernelExecResult(compiled=False, correctness=False, metadata=metadata) + """ Run a pair of (reference, solution) to check if solution is correct and compute speedup using Modal @@ -60,26 +66,35 @@ def evaluate_single_sample_src(ref_arch_src: str, kernel_src: str, configs: dict torch.set_printoptions(precision=4, threshold=10) app = modal.App("run_and_check") -gpu_arch_mapping = {"L40S": ["Ada"], "H100": ["Hopper"], "A100": ["Ampere"], "L4": ["Ada"], "T4": ["Turing"], "A10G": ["Ampere"]} +gpu_arch_mapping = { + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], +} + class ScriptConfig(Config): def __init__(self): # Required file paths self.ref_arch_src_path = REQUIRED # Reference implementation - self.kernel_src_path = REQUIRED # Custom kernel implementation - self.gpu = "L40S" # GPU type for modal - self.num_correct_trials = 5 # Number of trials for correctness - self.num_perf_trials = 100 # Number of trials for performance - self.timeout = 300 # Timeout for each trial - self.verbose = False # Verbose logging - self.measure_performance = True # Whether to measure performance - self.build_dir_prefix = "" # Custom build directory prefix - self.clear_cache = False # Whether to clear build cache - self.gpu_arch = ["Ada"] # Default GPU architecture + self.kernel_src_path = REQUIRED # Custom kernel implementation + self.gpu = "L40S" # GPU type for modal + self.num_correct_trials = 5 # Number of trials for correctness + self.num_perf_trials = 100 # Number of trials for performance + self.timeout = 300 # Timeout for each trial + self.verbose = False # Verbose logging + self.measure_performance = True # Whether to measure performance + self.build_dir_prefix = "" # Custom build directory prefix + self.clear_cache = False # Whether to clear build cache + self.gpu_arch = ["Ada"] # Default GPU architecture def __repr__(self): return f"ScriptConfig({self.to_dict()})" + # Configure Modal image cuda_version = "12.8.0" flavor = "devel" @@ -93,86 +108,113 @@ def __repr__(self): .add_local_python_source("_remote_module_non_scriptable", "scripts", "src") ) + @app.cls(image=image) class EvalFunc: @modal.method() - def evaluate_single_sample_src_modal(self, ref_arch_src, kernel_src, configs, gpu_arch): + def evaluate_single_sample_src_modal( + self, ref_arch_src, kernel_src, configs, gpu_arch + ): """Evaluate a single sample source code against a reference source code""" import torch from src import utils as kernel_utils import sys - + kernel_utils.set_gpu_arch(gpu_arch) device = torch.device("cuda:0") current_module = sys.modules[__name__] - + eval_result = current_module.evaluate_single_sample_src( ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=configs, - device=device + device=device, ) - + return { "compiled": eval_result.compiled, "correctness": eval_result.correctness, "runtime": eval_result.runtime, - "metadata": eval_result.metadata + "metadata": eval_result.metadata, } @modal.method() - def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, - use_torch_compile=False, torch_compile_backend=None, - torch_compile_options=None, gpu_arch=None): + def measure_program_time( + self, + ref_arch_name, + ref_arch_src, + num_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=None, + ): """Measure the execution time of a reference program""" - + # Setup if gpu_arch: set_gpu_arch(gpu_arch) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - + # Create temporary module temp_dir = tempfile.mkdtemp() ref_module_path = os.path.join(temp_dir, "ref_module.py") - + with open(ref_module_path, "w") as f: f.write(ref_arch_src) - + # Load reference module spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) ref_module = importlib.util.module_from_spec(spec) sys.modules["ref_module"] = ref_module spec.loader.exec_module(ref_module) - + # Create model instance if hasattr(ref_module, "get_init_inputs"): init_inputs = ref_module.get_init_inputs() init_inputs = [ - x if (isinstance(x, torch.Tensor) and x.device == device) - else (x.to(device) if isinstance(x, torch.Tensor) else x) + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) for x in init_inputs ] ref_model = ref_module.Model(*init_inputs).to(device) else: ref_model = ref_module.Model().to(device) - + # Apply torch.compile if needed if use_torch_compile: if torch_compile_backend is not None: - if torch_compile_options is not None and torch_compile_options != "default": - compile_options = {"mode": torch_compile_options} if torch_compile_options in ["max-autotune", "reduce-overhead"] else {} - ref_model = torch.compile(ref_model, backend=torch_compile_backend, options=compile_options) + if ( + torch_compile_options is not None + and torch_compile_options != "default" + ): + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) else: ref_model = torch.compile(ref_model, backend=torch_compile_backend) else: ref_model = torch.compile(ref_model) - + # Generate inputs if hasattr(ref_module, "get_inputs"): inputs = ref_module.get_inputs() inputs = [ - x if (isinstance(x, torch.Tensor) and x.device == device) - else (x.to(device) if isinstance(x, torch.Tensor) else x) + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) for x in inputs ] elif hasattr(ref_module, "INPUT_SHAPE"): @@ -180,42 +222,46 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, if isinstance(input_shape, tuple): inputs = (torch.randn(input_shape, device=device),) elif isinstance(input_shape, list): - inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + inputs = tuple( + torch.randn(shape, device=device) for shape in input_shape + ) else: raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") else: # Infer inputs from model if hasattr(ref_model, "forward"): argcount = ref_model.forward.__code__.co_argcount - inputs = tuple(torch.randn(1, 128, device=device) for _ in range(argcount - 1)) + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) else: raise ValueError("Could not determine appropriate inputs for the model") - + # Warmup for _ in range(10): ref_model(*inputs) - + # Timing torch.cuda.synchronize() times = [] for _ in range(num_trials): start = torch.cuda.Event(enable_timing=True) end = torch.cuda.Event(enable_timing=True) - + start.record() ref_model(*inputs) end.record() - + torch.cuda.synchronize() times.append(start.elapsed_time(end)) - + # Clean up try: os.remove(ref_module_path) os.rmdir(temp_dir) except OSError: shutil.rmtree(temp_dir, ignore_errors=True) - + # Calculate statistics times = np.array(times) return { @@ -226,6 +272,7 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, "median": float(np.median(times)), } + @pydra.main(base=ScriptConfig) def main(config: ScriptConfig): print("Running with config", config) @@ -233,7 +280,7 @@ def main(config: ScriptConfig): # Read source files ref_arch_src = read_file(config.ref_arch_src_path) kernel_src = read_file(config.kernel_src_path) - + # Prepare GPU architecture settings gpu_arch = gpu_arch_mapping.get(config.gpu, config.gpu_arch) print(f"[INFO] Using GPU architecture: {gpu_arch}") @@ -242,65 +289,75 @@ def main(config: ScriptConfig): with app.run(): # Evaluate kernel against reference code print("[INFO] Evaluating kernel against reference code") - kernel_eval_result_dict = EvalFunc.with_options(gpu=config.gpu)().evaluate_single_sample_src_modal.remote( + kernel_eval_result_dict = EvalFunc.with_options( + gpu=config.gpu + )().evaluate_single_sample_src_modal.remote( ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=config.to_dict(), - gpu_arch=gpu_arch + gpu_arch=gpu_arch, ) - + # Convert dict back to KernelExecResult object kernel_eval_result = KernelExecResult( compiled=kernel_eval_result_dict["compiled"], correctness=kernel_eval_result_dict["correctness"], runtime=kernel_eval_result_dict["runtime"], - metadata=kernel_eval_result_dict["metadata"] + metadata=kernel_eval_result_dict["metadata"], ) kernel_exec_time = kernel_eval_result.runtime # Measure baseline time for PyTorch Eager print("[INFO] Measuring reference program time (eager mode)") - ref_time_eager_result = EvalFunc.with_options(gpu=config.gpu)().measure_program_time.remote( + ref_time_eager_result = EvalFunc.with_options( + gpu=config.gpu + )().measure_program_time.remote( ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=config.num_perf_trials, use_torch_compile=False, torch_compile_backend=None, torch_compile_options=None, - gpu_arch=gpu_arch + gpu_arch=gpu_arch, ) ref_exec_eager_time = ref_time_eager_result.get("mean", None) # Measure Torch Compile time print("[INFO] Measuring reference program time (torch.compile)") - ref_time_compile_result = EvalFunc.with_options(gpu=config.gpu)().measure_program_time.remote( + ref_time_compile_result = EvalFunc.with_options( + gpu=config.gpu + )().measure_program_time.remote( ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=config.num_perf_trials, use_torch_compile=True, torch_compile_backend="inductor", torch_compile_options="default", - gpu_arch=gpu_arch + gpu_arch=gpu_arch, ) ref_exec_compile_time = ref_time_compile_result.get("mean", None) # Print results - print("="*40) + print("=" * 40) print(f"[Eval] Kernel eval result: {kernel_eval_result}") - print("-"*40) + print("-" * 40) print(f"[Timing] PyTorch Reference Eager exec time: {ref_exec_eager_time} ms") print(f"[Timing] PyTorch Reference torch.compile time: {ref_exec_compile_time} ms") print(f"[Timing] Custom Kernel exec time: {kernel_exec_time} ms") - print("-"*40) - + print("-" * 40) + if kernel_eval_result.correctness: - print(f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x") - print(f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x") + print( + f"[Speedup] Speedup over eager: {ref_exec_eager_time / kernel_exec_time:.2f}x" + ) + print( + f"[Speedup] Speedup over torch.compile: {ref_exec_compile_time / kernel_exec_time:.2f}x" + ) else: print("[Speedup] Speedup Not Available as Kernel did not pass correctness") - print("="*40) + print("=" * 40) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/server_requirements.txt b/scripts/server_requirements.txt index f46d8e67..743cd38c 100644 --- a/scripts/server_requirements.txt +++ b/scripts/server_requirements.txt @@ -11,4 +11,4 @@ together pytest ninja archon-ai -einops \ No newline at end of file +einops diff --git a/scripts/server_run_and_check.py b/scripts/server_run_and_check.py index 150dd51f..eea7ce42 100644 --- a/scripts/server_run_and_check.py +++ b/scripts/server_run_and_check.py @@ -14,6 +14,7 @@ from scripts.generate_baseline_time import measure_program_time from kernelbench.utils import read_file, set_gpu_arch + # Define the response model class BenchmarkResult(BaseModel): compiled: bool @@ -26,8 +27,10 @@ class BenchmarkResult(BaseModel): metadata: Dict[str, Any] error: Optional[str] = None + app = fastapi.FastAPI() + @app.post("/benchmark", response_model=BenchmarkResult) async def run_benchmark( ref_file: UploadFile = File(...), @@ -35,11 +38,15 @@ async def run_benchmark( gpu_arch: List[str] = ["Ada"], num_correct_trials: int = 5, num_perf_trials: int = 100, - verbose: bool = False + verbose: bool = False, ): # Create temporary files for the uploaded code - with tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as ref_tmp, \ - tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as kernel_tmp: + with ( + tempfile.NamedTemporaryFile(delete=False, suffix=".py", mode="wb") as ref_tmp, + tempfile.NamedTemporaryFile( + delete=False, suffix=".py", mode="wb" + ) as kernel_tmp, + ): try: # Save uploaded file contents to temporary files shutil.copyfileobj(ref_file.file, ref_tmp) @@ -56,13 +63,13 @@ async def run_benchmark( # Read the contents of the files ref_arch_src = read_file(ref_path) kernel_src = read_file(kernel_path) - + # Set up GPU architecture set_gpu_arch(gpu_arch) - + # Default device device = torch.device("cuda:0") - + # Prepare configs configs = { "num_correct_trials": num_correct_trials, @@ -70,51 +77,55 @@ async def run_benchmark( "verbose": verbose, "measure_performance": True, "build_dir_prefix": "server_builds", - "clear_cache": False + "clear_cache": False, } - + # Evaluate kernel against reference kernel_eval_result = evaluate_single_sample_src( ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=configs, - device=device + device=device, ) - + # Measure reference times ref_time_eager_result = measure_program_time( - ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, num_trials=num_perf_trials, use_torch_compile=False, - device=device + device=device, ) - + ref_time_compile_result = measure_program_time( - ref_arch_name="Reference Program", - ref_arch_src=ref_arch_src, + ref_arch_name="Reference Program", + ref_arch_src=ref_arch_src, num_trials=num_perf_trials, use_torch_compile=True, torch_compile_backend="inductor", torch_compile_options="default", - device=device + device=device, ) - + # Extract values kernel_exec_time = kernel_eval_result.runtime ref_exec_eager_time = ref_time_eager_result.get("mean", None) ref_exec_compile_time = ref_time_compile_result.get("mean", None) - + # Calculate speedups speedup_vs_eager = None speedup_vs_compile = None - + if kernel_eval_result.correctness and kernel_exec_time and ref_exec_eager_time: speedup_vs_eager = ref_exec_eager_time / kernel_exec_time - - if kernel_eval_result.correctness and kernel_exec_time and ref_exec_compile_time: + + if ( + kernel_eval_result.correctness + and kernel_exec_time + and ref_exec_compile_time + ): speedup_vs_compile = ref_exec_compile_time / kernel_exec_time - + # Prepare output summary raw_output = f""" ============================== @@ -131,10 +142,12 @@ async def run_benchmark( [Speedup] Speedup over torch.compile: {speedup_vs_compile:.2f}x """ else: - raw_output += "[Speedup] Speedup Not Available as Kernel did not pass correctness" - + raw_output += ( + "[Speedup] Speedup Not Available as Kernel did not pass correctness" + ) + raw_output += "==============================" - + # Prepare the response response = BenchmarkResult( compiled=kernel_eval_result.compiled, @@ -145,22 +158,22 @@ async def run_benchmark( speedup_vs_eager=speedup_vs_eager, speedup_vs_compile=speedup_vs_compile, metadata=kernel_eval_result.metadata or {}, - ) - print(raw_output) + print(raw_output) return response except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"An error occurred during benchmarking: {str(e)}" + detail=f"An error occurred during benchmarking: {str(e)}", ) finally: # Clean up temporary files - if 'ref_path' in locals() and os.path.exists(ref_path): + if "ref_path" in locals() and os.path.exists(ref_path): os.remove(ref_path) - if 'kernel_path' in locals() and os.path.exists(kernel_path): + if "kernel_path" in locals() and os.path.exists(kernel_path): os.remove(kernel_path) + if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py index 384ee2fc..acb13ff2 100644 --- a/scripts/server_run_and_check_modal.py +++ b/scripts/server_run_and_check_modal.py @@ -23,12 +23,12 @@ # GPU architecture mapping gpu_arch_mapping = { - "L40S": ["Ada"], - "H100": ["Hopper"], - "A100": ["Ampere"], - "L4": ["Ada"], - "T4": ["Turing"], - "A10G": ["Ampere"] + "L40S": ["Ada"], + "H100": ["Hopper"], + "A100": ["Ampere"], + "L4": ["Ada"], + "T4": ["Turing"], + "A10G": ["Ampere"], } GPU = "L40S" @@ -57,7 +57,8 @@ ) # Create Modal app -app = modal.App("kernel-benchmark-server", image=image) # Still here +app = modal.App("kernel-benchmark-server", image=image) # Still here + # Define response models class KernelExecResult(BaseModel): @@ -66,6 +67,7 @@ class KernelExecResult(BaseModel): runtime: Optional[float] = None metadata: Dict[str, Any] = {} + class BenchmarkResult(BaseModel): kernel_result: KernelExecResult ref_exec_eager_time_ms: Optional[float] = None @@ -77,22 +79,31 @@ class BenchmarkResult(BaseModel): total_benchmark_time_ms: Optional[float] = None error: Optional[str] = None -@app.cls(gpu=GPU, scaledown_window=SCALEDOWN_WINDOW, secrets=[modal.Secret.from_name("wandb-api-key")]) + +@app.cls( + gpu=GPU, + scaledown_window=SCALEDOWN_WINDOW, + secrets=[modal.Secret.from_name("wandb-api-key")], +) class BenchmarkService: - def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device) -> KernelExecResult: + def evaluate_single_sample_src( + self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device + ) -> KernelExecResult: """Evaluate a single sample source code against a reference source code""" - + try: print(f"[DEBUG] Python paths: {sys.path}") - + kernel_hash = str(hash(kernel_src)) - build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - + build_dir = os.path.join( + configs["build_dir_prefix"], "test_build", kernel_hash + ) + if configs["clear_cache"]: print(f"[INFO] Clearing cache for build directory: {build_dir}") shutil.rmtree(build_dir, ignore_errors=True) - + try: eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, @@ -102,95 +113,127 @@ def evaluate_single_sample_src(self, ref_arch_src: str, kernel_src: str, configs num_correct_trials=configs["num_correct_trials"], num_perf_trials=configs["num_perf_trials"], build_dir=build_dir, - device=device + device=device, ) return KernelExecResult( compiled=eval_result.compiled, correctness=eval_result.correctness, runtime=eval_result.runtime, - metadata=eval_result.metadata or {} + metadata=eval_result.metadata or {}, ) except Exception as e: - print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") - if "CUDA error" in str(e): - metadata = {"cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } + print( + f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} " + ) + if "CUDA error" in str(e): + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } else: - metadata = {"other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device) - } - return KernelExecResult(compiled=False, correctness=False, metadata=metadata) - except ImportError as e: # This catch might be less likely now, but keep for safety + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + return KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + except ( + ImportError + ) as e: # This catch might be less likely now, but keep for safety print(f"[ERROR] Import error during evaluation (unexpected): {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") return KernelExecResult( compiled=False, correctness=False, - metadata={"import_error": f"Unexpected import error during eval: {str(e)}"} + metadata={ + "import_error": f"Unexpected import error during eval: {str(e)}" + }, ) except Exception as e: print(f"[ERROR] Unexpected error during evaluation: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") return KernelExecResult( - compiled=False, - correctness=False, - metadata={"unexpected_error": str(e)} + compiled=False, correctness=False, metadata={"unexpected_error": str(e)} ) - - def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, - use_torch_compile=False, torch_compile_backend=None, - torch_compile_options=None, gpu_arch=None): + + def measure_program_time( + self, + ref_arch_name, + ref_arch_src, + num_trials, + use_torch_compile=False, + torch_compile_backend=None, + torch_compile_options=None, + gpu_arch=None, + ): """Measure the execution time of a reference program""" # Setup if gpu_arch: set_gpu_arch(gpu_arch) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - + # Create temporary module temp_dir = tempfile.mkdtemp() ref_module_path = os.path.join(temp_dir, "ref_module.py") - + with open(ref_module_path, "w") as f: f.write(ref_arch_src) - + # Load reference module spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) ref_module = importlib.util.module_from_spec(spec) sys.modules["ref_module"] = ref_module spec.loader.exec_module(ref_module) - + # Create model instance if hasattr(ref_module, "get_init_inputs"): init_inputs = ref_module.get_init_inputs() init_inputs = [ - x if (isinstance(x, torch.Tensor) and x.device == device) - else (x.to(device) if isinstance(x, torch.Tensor) else x) + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) for x in init_inputs ] ref_model = ref_module.Model(*init_inputs).to(device) else: ref_model = ref_module.Model().to(device) - + # Apply torch.compile if needed if use_torch_compile: if torch_compile_backend is not None: - if torch_compile_options is not None and torch_compile_options != "default": - compile_options = {"mode": torch_compile_options} if torch_compile_options in ["max-autotune", "reduce-overhead"] else {} - ref_model = torch.compile(ref_model, backend=torch_compile_backend, options=compile_options) + if ( + torch_compile_options is not None + and torch_compile_options != "default" + ): + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) else: ref_model = torch.compile(ref_model, backend=torch_compile_backend) else: ref_model = torch.compile(ref_model) - + # Generate inputs if hasattr(ref_module, "get_inputs"): inputs = ref_module.get_inputs() inputs = [ - x if (isinstance(x, torch.Tensor) and x.device == device) - else (x.to(device) if isinstance(x, torch.Tensor) else x) + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) for x in inputs ] elif hasattr(ref_module, "INPUT_SHAPE"): @@ -198,42 +241,46 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, if isinstance(input_shape, tuple): inputs = (torch.randn(input_shape, device=device),) elif isinstance(input_shape, list): - inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + inputs = tuple( + torch.randn(shape, device=device) for shape in input_shape + ) else: raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") else: # Infer inputs from model if hasattr(ref_model, "forward"): argcount = ref_model.forward.__code__.co_argcount - inputs = tuple(torch.randn(1, 128, device=device) for _ in range(argcount - 1)) + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) else: raise ValueError("Could not determine appropriate inputs for the model") - + # Warmup for _ in range(10): ref_model(*inputs) - + # Timing torch.cuda.synchronize() times = [] for _ in range(num_trials): start = torch.cuda.Event(enable_timing=True) end = torch.cuda.Event(enable_timing=True) - + start.record() ref_model(*inputs) end.record() - + torch.cuda.synchronize() times.append(start.elapsed_time(end)) - + # Clean up try: os.remove(ref_module_path) os.rmdir(temp_dir) except OSError: shutil.rmtree(temp_dir, ignore_errors=True) - + # Calculate statistics times = np.array(times) return { @@ -243,37 +290,43 @@ def measure_program_time(self, ref_arch_name, ref_arch_src, num_trials, "max": float(np.max(times)), "median": float(np.median(times)), } - + @modal.method() - def run_benchmark(self, ref_arch_src: str, kernel_src: str, - num_correct_trials: int = 5, - num_perf_trials: int = 100, - verbose: bool = False): + def run_benchmark( + self, + ref_arch_src: str, + kernel_src: str, + num_correct_trials: int = 5, + num_perf_trials: int = 100, + verbose: bool = False, + ): """Run a complete benchmark of kernel vs reference implementation""" print(f"[DEBUG] Starting benchmark on GPU: {GPU}") - + start_time = time.time() - + try: # Get GPU architecture gpu_arch = gpu_arch_mapping.get(GPU, ["Ada"]) print(f"[DEBUG] Using GPU architecture: {gpu_arch}") - + # Set GPU architecture set_gpu_arch(gpu_arch) - - # Default device + + # Default device device = torch.device("cuda:0") print(f"[DEBUG] Using device: {device}") - + # Check CUDA availability if torch.cuda.is_available(): - print(f"[DEBUG] CUDA is available. Device count: {torch.cuda.device_count()}") + print( + f"[DEBUG] CUDA is available. Device count: {torch.cuda.device_count()}" + ) print(f"[DEBUG] Current device: {torch.cuda.current_device()}") print(f"[DEBUG] Device name: {torch.cuda.get_device_name(device)}") else: print(f"[WARNING] CUDA is not available. Using CPU.") - + # Config dictionary configs = { "num_correct_trials": num_correct_trials, @@ -281,10 +334,10 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, "verbose": verbose, "measure_performance": True, "build_dir_prefix": "api_builds", - "clear_cache": False + "clear_cache": False, } print(f"[DEBUG] Using configs: {configs}") - + try: # Time the compilation specifically compile_start_time = time.time() @@ -292,15 +345,17 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=configs, - device=device + device=device, ) - compile_time = (time.time() - compile_start_time) * 1000 # Convert to ms - + compile_time = ( + time.time() - compile_start_time + ) * 1000 # Convert to ms + # Evaluate kernel print(f"[DEBUG] Evaluating kernel against reference...") kernel_exec_time = kernel_result.runtime print(f"[DEBUG] Kernel execution time: {kernel_exec_time} ms") - + # Measure baseline time for PyTorch Eager print(f"[DEBUG] Measuring PyTorch Eager execution time...") ref_time_eager_result = self.measure_program_time( @@ -310,11 +365,11 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, use_torch_compile=False, torch_compile_backend=None, torch_compile_options=None, - gpu_arch=gpu_arch + gpu_arch=gpu_arch, ) ref_exec_eager_time = ref_time_eager_result.get("mean", None) print(f"[DEBUG] PyTorch Eager execution time: {ref_exec_eager_time} ms") - + # Measure Torch Compile time print(f"[DEBUG] Measuring PyTorch Compiled execution time...") ref_time_compile_result = self.measure_program_time( @@ -324,23 +379,33 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, use_torch_compile=True, torch_compile_backend="inductor", torch_compile_options="default", - gpu_arch=gpu_arch + gpu_arch=gpu_arch, ) ref_exec_compile_time = ref_time_compile_result.get("mean", None) - print(f"[DEBUG] PyTorch Compiled execution time: {ref_exec_compile_time} ms") - + print( + f"[DEBUG] PyTorch Compiled execution time: {ref_exec_compile_time} ms" + ) + # Calculate speedups speedup_vs_eager = None speedup_vs_compile = None - - if kernel_result.correctness and kernel_exec_time and ref_exec_eager_time: + + if ( + kernel_result.correctness + and kernel_exec_time + and ref_exec_eager_time + ): speedup_vs_eager = ref_exec_eager_time / kernel_exec_time print(f"[DEBUG] Speedup vs Eager: {speedup_vs_eager}x") - - if kernel_result.correctness and kernel_exec_time and ref_exec_compile_time: + + if ( + kernel_result.correctness + and kernel_exec_time + and ref_exec_compile_time + ): speedup_vs_compile = ref_exec_compile_time / kernel_exec_time print(f"[DEBUG] Speedup vs Compiled: {speedup_vs_compile}x") - + # Round all float values to 2 decimal places if ref_exec_eager_time: ref_exec_eager_time = round(ref_exec_eager_time, 2) @@ -352,11 +417,13 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, speedup_vs_eager = round(speedup_vs_eager, 2) if speedup_vs_compile: speedup_vs_compile = round(speedup_vs_compile, 2) - + # Calculate total benchmark time - total_time = round((time.time() - start_time) * 1000, 2) # Convert to ms and round + total_time = round( + (time.time() - start_time) * 1000, 2 + ) # Convert to ms and round compile_time = round(compile_time, 2) - + # Build response print(f"[DEBUG] Building response...") return BenchmarkResult( @@ -367,27 +434,27 @@ def run_benchmark(self, ref_arch_src: str, kernel_src: str, speedup_vs_eager=speedup_vs_eager, speedup_vs_compile=speedup_vs_compile, compile_time_ms=compile_time, - total_benchmark_time_ms=total_time + total_benchmark_time_ms=total_time, ) except Exception as e: print(f"[ERROR] Error during benchmark execution: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") return BenchmarkResult( kernel_result=KernelExecResult(compiled=False, correctness=False), - error=f"Benchmark execution error: {str(e)}" + error=f"Benchmark execution error: {str(e)}", ) except Exception as e: print(f"[ERROR] Fatal error in run_benchmark: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") return BenchmarkResult( kernel_result=KernelExecResult(compiled=False, correctness=False), - error=str(e) + error=str(e), ) @modal.asgi_app() def fastapi_app(self): web_app = FastAPI(title="KernelBench Benchmarking API") - + # Add CORS middleware web_app.add_middleware( CORSMiddleware, @@ -396,26 +463,32 @@ def fastapi_app(self): allow_methods=["*"], allow_headers=["*"], ) - + # Determine if we're running locally or in Modal - static_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static") + static_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static" + ) modal_static_dir = "/root/static" - + # Check both possible locations for static files if os.path.exists(static_dir): # Mount static files directory (local development) web_app.mount("/static", StaticFiles(directory=static_dir), name="static") - + @web_app.get("/") async def root(): return FileResponse(os.path.join(static_dir, "index.html")) + elif os.path.exists(modal_static_dir): # Mount static files directory (Modal environment) - web_app.mount("/static", StaticFiles(directory=modal_static_dir), name="static") - + web_app.mount( + "/static", StaticFiles(directory=modal_static_dir), name="static" + ) + @web_app.get("/") async def root(): return FileResponse(os.path.join(modal_static_dir, "index.html")) + else: # Fallback for when static directory isn't available @web_app.get("/") @@ -426,34 +499,39 @@ async def root(): "description": "API for benchmarking CUDA kernels against PyTorch reference implementations", "endpoints": { "/benchmark": "POST endpoint for benchmarking kernels", - "/status": "GET endpoint for checking server status" - } + "/status": "GET endpoint for checking server status", + }, } - + @web_app.post("/benchmark", response_model=BenchmarkResult) async def benchmark_endpoint( ref_file: UploadFile = File(...), kernel_file: UploadFile = File(...), num_correct_trials: int = Form(5), num_perf_trials: int = Form(100), - verbose: bool = Form(False) + verbose: bool = Form(False), ): try: - print(f"[DEBUG] Received benchmark request for GPU: {GPU}, trials: {num_correct_trials}/{num_perf_trials}") - + print( + f"[DEBUG] Received benchmark request for GPU: {GPU}, trials: {num_correct_trials}/{num_perf_trials}" + ) + # Read file contents try: ref_content = await ref_file.read() print(f"[DEBUG] Read reference file: {len(ref_content)} bytes") kernel_content = await kernel_file.read() print(f"[DEBUG] Read kernel file: {len(kernel_content)} bytes") - + ref_arch_src = ref_content.decode("utf-8") kernel_src = kernel_content.decode("utf-8") except Exception as e: print(f"[ERROR] Failed to read uploaded files: {str(e)}") - raise HTTPException(status_code=400, detail=f"Failed to read uploaded files: {str(e)}") - + raise HTTPException( + status_code=400, + detail=f"Failed to read uploaded files: {str(e)}", + ) + # Run the benchmark try: print(f"[DEBUG] Calling run_benchmark method") @@ -462,26 +540,27 @@ async def benchmark_endpoint( kernel_src=kernel_src, num_correct_trials=num_correct_trials, num_perf_trials=num_perf_trials, - verbose=verbose + verbose=verbose, ) print(f"[DEBUG] Benchmark completed successfully") return result except Exception as e: print(f"[ERROR] Benchmark execution failed: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") - raise HTTPException(status_code=500, detail=f"Benchmark execution failed: {str(e)}") + raise HTTPException( + status_code=500, detail=f"Benchmark execution failed: {str(e)}" + ) except Exception as e: print(f"[ERROR] Unexpected error in benchmark endpoint: {str(e)}") print(f"[ERROR] Traceback: {traceback.format_exc()}") - raise HTTPException(status_code=500, detail=f"Benchmark failed: {str(e)}") - + raise HTTPException( + status_code=500, detail=f"Benchmark failed: {str(e)}" + ) + @web_app.get("/status") async def status(): - return { - "status": "online", - "gpu_type": GPU - } - + return {"status": "online", "gpu_type": GPU} + @web_app.get("/test_imports") async def test_imports(): """Test endpoint to check if we can import the necessary modules""" @@ -489,44 +568,60 @@ async def test_imports(): "python_version": sys.version, "sys_path": sys.path, "env_vars": dict(os.environ), - "imports": {} + "imports": {}, } - + # Check modules that should have been imported at the top try: # Verify torch import - if 'torch' in sys.modules: - result["imports"]["torch"] = { - "version": torch.__version__, - "cuda_available": torch.cuda.is_available(), - "cuda_version": torch.version.cuda if hasattr(torch.version, "cuda") else None - } + if "torch" in sys.modules: + result["imports"]["torch"] = { + "version": torch.__version__, + "cuda_available": torch.cuda.is_available(), + "cuda_version": ( + torch.version.cuda + if hasattr(torch.version, "cuda") + else None + ), + } else: result["imports"]["torch"] = {"error": "torch module not loaded"} except Exception as e: - result["imports"]["torch"] = {"error": f"Error checking torch: {str(e)}"} + result["imports"]["torch"] = { + "error": f"Error checking torch: {str(e)}" + } - result["imports"]["src.eval"] = {"success": True} result["imports"]["src.utils"] = {"success": True} - + # Check for file existence result["files"] = { - "static_local": os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static")), + "static_local": os.path.exists( + os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "static", + ) + ), "static_modal": os.path.exists("/root/static"), - "requirements_txt": os.path.exists("requirements.txt") + "requirements_txt": os.path.exists("requirements.txt"), } - + return result - + return web_app + def main(): # For local development, you can use: # modal serve scripts.server_run_and_check_modal print("Starting KernelBench API server...") - print("Use 'modal serve scripts.server_run_and_check_modal' to start the development server") - print("Use 'modal deploy scripts.server_run_and_check_modal' to deploy to production") + print( + "Use 'modal serve scripts.server_run_and_check_modal' to start the development server" + ) + print( + "Use 'modal deploy scripts.server_run_and_check_modal' to deploy to production" + ) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/verify_bench.py b/scripts/verify_bench.py index d39733ee..2ab73629 100644 --- a/scripts/verify_bench.py +++ b/scripts/verify_bench.py @@ -1,5 +1,5 @@ """ -This script tests the correctness of models in KernelBench by generating random inputs +This script tests the correctness of models in KernelBench by generating random inputs and random initialization. It compares the output of the original model against itself. It ensures that the test is well-formed and there are no sources of non-determinism in the test. @@ -15,7 +15,7 @@ import numpy as np """ -Test all the reference architectures compiles +Test all the reference architectures compiles and reproduce the same results when run against itself """ diff --git a/scripts/verify_generation.py b/scripts/verify_generation.py index b1de1fc7..f0dcc9af 100644 --- a/scripts/verify_generation.py +++ b/scripts/verify_generation.py @@ -2,14 +2,19 @@ import time import kernelbench.utils as utils -from kernelbench.prompt_constructor import prompt_generate_custom_cuda_from_prompt_template +from kernelbench.prompt_constructor import ( + prompt_generate_custom_cuda_from_prompt_template, +) """ -For testing infernece and quickly iterate on prompts +For testing infernece and quickly iterate on prompts Uses functions in prompt_constructor """ -def inference_with_prompt(arch_path, inference_server: callable = None, log_to_local: bool = False) -> str: + +def inference_with_prompt( + arch_path, inference_server: callable = None, log_to_local: bool = False +) -> str: """ Returns the generated custom CUDA code (kernel to evaluate) @@ -28,7 +33,7 @@ def inference_with_prompt(arch_path, inference_server: callable = None, log_to_l custom_cuda_prompt = prompt_generate_custom_cuda_from_prompt_template(arch) - if log_to_local: + if log_to_local: with open(f"./scratch/prompt.py", "w") as f: f.write(custom_cuda_prompt) @@ -57,18 +62,19 @@ def sanity_check_inference(inference_server: callable): lm_response = inference_server("What does CUDA stand for?") end_time = time.time() print(f"[Timing] Inference took {end_time - start_time:.2f} seconds") - print(lm_response) + print(lm_response) return lm_response if __name__ == "__main__": - inference_server = utils.create_inference_server_from_presets(server_type="together", - model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - greedy_sample=True, - verbose=True, - time_generation=True) - + inference_server = utils.create_inference_server_from_presets( + server_type="together", + model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + greedy_sample=True, + verbose=True, + time_generation=True, + ) # sanity_check_inference(inference_server) @@ -79,6 +85,6 @@ def sanity_check_inference(inference_server: callable): # most basic problem arch_path = "./KernelBench/level1/1_Square_matrix_multiplication_.py" # representative of long problem, might require longer max tokens to not get cut of - # arch_path = "./KernelBench/level3/45_UNetSoftmax.py" - + # arch_path = "./KernelBench/level3/45_UNetSoftmax.py" + inference_with_prompt(arch_path, inference_server, log_to_local=True) diff --git a/src/kernelbench/analysis.py b/src/kernelbench/analysis.py index 6f3c7f4e..f6f1ec34 100644 --- a/src/kernelbench/analysis.py +++ b/src/kernelbench/analysis.py @@ -20,14 +20,16 @@ def pass_at_k(n, c, k): def get_token_count(text: str, tokenizer: AutoTokenizer) -> int: - assert isinstance(text, str), "can only tokenize strings but got {}".format(type(text)) + assert isinstance(text, str), "can only tokenize strings but got {}".format( + type(text) + ) return len(tokenizer.encode(text)) def extract_all_cuda_sources(file_content: str) -> list[str]: """ Extract all CUDA sources wrapped in triple quotes. - + Returns: list[str]: List of all extracted CUDA source code blocks """ @@ -43,4 +45,3 @@ def get_cuda_tokens(kernel_src: str, tokenizer: AutoTokenizer) -> int: all_cuda_code = extract_all_cuda_sources(kernel_src) num_cuda_tokens = sum(get_token_count(code, tokenizer) for code in all_cuda_code) return num_cuda_tokens - diff --git a/src/kernelbench/compile.py b/src/kernelbench/compile.py index d2a5cc36..c8aec6a3 100644 --- a/src/kernelbench/compile.py +++ b/src/kernelbench/compile.py @@ -17,7 +17,8 @@ on CPU in parallel so you can speedup the evaluation process The cache build directory must match the ones you use during evaluation phase -""" +""" + @dataclass class WorkArgs: @@ -25,59 +26,83 @@ class WorkArgs: sample_id: int device: torch.device + def compile_single_sample(work_args: WorkArgs, config: dict) -> tuple[bool, str]: - problem_id = work_args.problem_id + problem_id = work_args.problem_id sample_id = work_args.sample_id verbose = config["verbose"] - + set_gpu_arch(config["gpu_arch"]) - build_dir = os.path.join(config["kernel_eval_build_dir"], config["run_name"], str(problem_id), str(sample_id)) + build_dir = os.path.join( + config["kernel_eval_build_dir"], + config["run_name"], + str(problem_id), + str(sample_id), + ) run_dir = os.path.join(config["runs_dir"], config["run_name"]) - kernel_src_path = os.path.join(run_dir, f"level_{config['level']}_problem_{problem_id}_sample_{sample_id}_kernel.py") + kernel_src_path = os.path.join( + run_dir, + f"level_{config['level']}_problem_{problem_id}_sample_{sample_id}_kernel.py", + ) if not os.path.exists(kernel_src_path): - print(f"[ERROR] Kernel source file not found for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"[ERROR] Kernel source file not found for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) return False, "Kernel source file not found" with open(kernel_src_path, "r") as f: kernel_src = f.read() try: - compiled_and_cached, stdout_content, error_msg = build_compile_cache(custom_model_src=kernel_src, - verbose=verbose, - build_dir=build_dir) + compiled_and_cached, stdout_content, error_msg = build_compile_cache( + custom_model_src=kernel_src, verbose=verbose, build_dir=build_dir + ) return compiled_and_cached, stdout_content, error_msg except Exception as e: - print(f"[WARNING] Last level catch on {sample_id}: Some issue while compiling and attempting to cache for kernel: {e} ") + print( + f"[WARNING] Last level catch on {sample_id}: Some issue while compiling and attempting to cache for kernel: {e} " + ) return None, str(e), str(e) - + + def remove_cache_dir(config, problem_id, sample_id): """ Remove the cached folder for sample compilation so it can start a clean build next time useful for time out, failed build, etc. """ - cache_dir = os.path.join(config['kernel_eval_build_dir'], config["run_name"], f"{problem_id}", f"{sample_id}") + cache_dir = os.path.join( + config["kernel_eval_build_dir"], + config["run_name"], + f"{problem_id}", + f"{sample_id}", + ) print(f"cache_dir to remove: {cache_dir}") if os.path.exists(cache_dir): try: # Add error handling and retry with force shutil.rmtree(cache_dir, ignore_errors=True) - print(f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}") + print( + f"\n[INFO] Removed cached folder for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) except Exception as e: print(f"\n[WARNING] Failed to remove cache directory {cache_dir}: {str(e)}") + def batch_compile(total_work: list[tuple[int, int]], config: dict): """ Batch compile cache across CPUs, assume config has num_cpu_workers """ if mp.get_start_method(allow_none=True) is None: - mp.set_start_method('spawn') + mp.set_start_method("spawn") - assert "num_cpu_workers" in config, "num_cpu_workers must be specified in config for batch compile" + assert ( + "num_cpu_workers" in config + ), "num_cpu_workers must be specified in config for batch compile" try: with mp.Pool(config["num_cpu_workers"]) as pool: # Create work args for each task @@ -86,7 +111,6 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): for p_id, s_idx in total_work ] - # Launch all tasks in parallel and track start times async_results = [] start_times = {} @@ -95,68 +119,89 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): async_result = pool.apply_async(compile_single_sample, args=work_arg) async_results.append(async_result) start_times[id(async_result)] = time.time() - + results = [] pending_tasks = list(enumerate(async_results)) - + with tqdm(total=len(work_args), desc="Compile & Cache Progress") as pbar: while pending_tasks: remaining_tasks = [] for i, async_result in pending_tasks: try: - problem_id, sample_id = total_work[i] # curr code of interest + problem_id, sample_id = total_work[ + i + ] # curr code of interest if async_result.ready(): try: - compiled, stdout_content, error_msg = async_result.get(timeout=1) # Short timeout for completed tasks - - print(f"[Status] Compilation {compiled} for problem {problem_id} sample {sample_id}") + compiled, stdout_content, error_msg = ( + async_result.get(timeout=1) + ) # Short timeout for completed tasks + + print( + f"[Status] Compilation {compiled} for problem {problem_id} sample {sample_id}" + ) results.append((i, compiled)) if not compiled: # Remove the cached folder for this timed out sample so it can start a clean build next time problem_id, sample_id = total_work[i] remove_cache_dir(config, problem_id, sample_id) - + pbar.update(1) except Exception as e: problem_id, sample_id = total_work[i] with open("error_log.txt", "a") as f: - f.write(f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") - print(f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") + f.write( + f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) + print( + f"\n[ERROR] Task failed for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) remove_cache_dir(config, problem_id, sample_id) results.append((i, None)) pbar.update(1) else: # Check if the task has exceeded timeout - if time.time() - start_times[id(async_result)] > config["timeout"]: + if ( + time.time() - start_times[id(async_result)] + > config["timeout"] + ): problem_id, sample_id = total_work[i] - print(f"\n[TIME OUT] Task timed out for Problem ID: {problem_id}, Sample ID: {sample_id}") - + print( + f"\n[TIME OUT] Task timed out for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) + problem_id, sample_id = total_work[i] remove_cache_dir(config, problem_id, sample_id) # if we were to retry! # Start a new task for the same work - print(f"Retrying for Problem ID: {problem_id}, Sample ID: {sample_id}") - new_async_result = pool.apply_async(compile_single_sample, args=work_args[i]) + print( + f"Retrying for Problem ID: {problem_id}, Sample ID: {sample_id}" + ) + new_async_result = pool.apply_async( + compile_single_sample, args=work_args[i] + ) start_times[id(new_async_result)] = time.time() remaining_tasks.append((i, new_async_result)) else: - # keep going + # keep going remaining_tasks.append((i, async_result)) except Exception as e: problem_id, sample_id = total_work[i] - print(f"\n[ERROR] Unexpected error for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}") - + print( + f"\n[ERROR] Unexpected error for Problem ID: {problem_id}, Sample ID: {sample_id}: {str(e)}" + ) + remove_cache_dir(config, problem_id, sample_id) - + results.append((i, None)) - + pbar.update(1) - + pending_tasks = remaining_tasks time.sleep(0.1) # Prevent busy waiting - + # Sort results back to original order sorted_results = [r for _, r in sorted(results, key=lambda x: x[0])] return sorted_results @@ -166,5 +211,5 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): pool.terminate() raise finally: - if 'pool' in locals(): + if "pool" in locals(): pool.close() diff --git a/src/kernelbench/dataset.py b/src/kernelbench/dataset.py index cb429dc1..08965b3e 100644 --- a/src/kernelbench/dataset.py +++ b/src/kernelbench/dataset.py @@ -116,7 +116,26 @@ def get_kernelbench_subset( "87_conv_pointwise_2D.py", ] -level1_representative_subset_problem_ids = [1, 3, 6, 18, 23, 26, 33, 36, 40, 42, 48, 54, 57, 65, 77, 82, 86, 87] +level1_representative_subset_problem_ids = [ + 1, + 3, + 6, + 18, + 23, + 26, + 33, + 36, + 40, + 42, + 48, + 54, + 57, + 65, + 77, + 82, + 86, + 87, +] level2_representative_subset = [ "1_Conv2D_ReLU_BiasAdd.py", @@ -143,4 +162,4 @@ def get_kernelbench_subset( "43_MinGPTCausalAttention.py", ] -level3_representative_subset_problem_ids = [1, 5, 8, 11, 20, 33, 38, 43] \ No newline at end of file +level3_representative_subset_problem_ids = [1, 5, 8, 11, 20, 33, 38, 43] diff --git a/src/kernelbench/eval.py b/src/kernelbench/eval.py index 11419f84..6ee7ff65 100644 --- a/src/kernelbench/eval.py +++ b/src/kernelbench/eval.py @@ -169,6 +169,7 @@ def graceful_eval_cleanup(curr_context: dict, device: torch.device): # _cleanup_cuda_extensions() # SIMON NOTE: is this necessary? + def build_compile_cache_legacy( custom_model_src: str, verbose: bool = False, @@ -202,11 +203,12 @@ def build_compile_cache_legacy( if verbose: print(f"[Compilation] Compilation Successful, saved cache at: {build_dir}") except Exception as e: - print(f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}") + print( + f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}" + ) return False, stdout_buffer.getvalue(), str(e) - - return True, stdout_buffer.getvalue(), None + return True, stdout_buffer.getvalue(), None def build_compile_cache( @@ -242,16 +244,16 @@ def build_compile_cache( if verbose: print(f"[Compilation] Compilation Successful, saved cache at: {build_dir}") except Exception as e: - print(f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}") + print( + f"[Compilation] Failed to compile custom CUDA kernel. Unable to cache, \nError: {e}" + ) return False, stdout_buffer.getvalue(), str(e) return True, stdout_buffer.getvalue(), None def build_compile_cache_with_capturing( - custom_model_src: str, - verbose: bool = False, - build_dir: os.PathLike = None + custom_model_src: str, verbose: bool = False, build_dir: os.PathLike = None ) -> tuple[int, str, str]: """ Write a temporary python file to compile the custom model on CPU @@ -273,22 +275,21 @@ def build_compile_cache_with_capturing( f.write(custom_model_src) # Execute the temporary Python file and capture output - process = subprocess.Popen(['python', tmp], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen( + ["python", tmp], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) stdout, stderr = process.communicate() returncode = process.returncode # Clean up temporary file os.remove(tmp) - if verbose: print("[CPU Precompile] return code: ", returncode) - print("[CPU Precompile] stdout: \n", stdout.decode('utf-8')) - print("[CPU Precompile] stderr: \n", stderr.decode('utf-8')) - - return returncode, stdout.decode('utf-8'), stderr.decode('utf-8') - + print("[CPU Precompile] stdout: \n", stdout.decode("utf-8")) + print("[CPU Precompile] stderr: \n", stderr.decode("utf-8")) + return returncode, stdout.decode("utf-8"), stderr.decode("utf-8") def eval_kernel_against_ref( @@ -300,7 +301,9 @@ def eval_kernel_against_ref( verbose: bool = False, measure_performance: bool = False, build_dir: os.PathLike = None, - device: torch.device = torch.cuda.current_device() if torch.cuda.is_available() else None, # have to run on GPU + device: torch.device = ( + torch.cuda.current_device() if torch.cuda.is_available() else None + ), # have to run on GPU ) -> KernelExecResult: """ Evaluate the custom kernel against the original model @@ -678,11 +681,13 @@ def check_metadata_serializable(metadata: dict): return metadata + def check_metadata_serializable_all_types(metadata: dict): """ Ensure metadata is JSON serializable, if not, convert non-serializable values to strings recursively """ + def convert_to_serializable(obj): if isinstance(obj, dict): return {k: convert_to_serializable(v) for k, v in obj.items()} diff --git a/src/kernelbench/frameworks.py b/src/kernelbench/frameworks.py index 24d0f8ac..944d1eea 100644 --- a/src/kernelbench/frameworks.py +++ b/src/kernelbench/frameworks.py @@ -14,6 +14,7 @@ # python-dotenv reads key-value pairs from a .env file and sets them as environment variables from dotenv import load_dotenv + load_dotenv() # from datasets import load_dataset @@ -28,17 +29,17 @@ SAMBANOVA_API_KEY = os.environ.get("SAMBANOVA_API_KEY") - ######################################################## # Inference Time Frameworks ######################################################## + def query_framework_server( prompt: str | list[dict], # string if normal prompt, list of dicts if chat prompt, system_prompt: str = "You are a helpful assistant", # only used for chat prompts temperature: float = 0.0, - top_p: float = 1.0, # nucleus sampling - top_k: int = 50, + top_p: float = 1.0, # nucleus sampling + top_k: int = 50, max_tokens: int = 128, # max output tokens to generate num_completions: int = 1, server_port: int = 30000, # only for local server hosted on SGLang @@ -57,7 +58,9 @@ def query_framework_server( case "archon": archon_config_path = framework_config_path assert archon_config_path is not None, "Archon config path is required" - assert os.path.exists(archon_config_path), f"Archon config path {archon_config_path} does not exist" + assert os.path.exists( + archon_config_path + ), f"Archon config path {archon_config_path} does not exist" client = Archon(json.load(open(archon_config_path))) model = model_name print(f"Querying Archon model {model} with config {archon_config_path}") @@ -75,6 +78,7 @@ def query_framework_server( case _: raise NotImplementedError + # a list of presets for API server configs SERVER_PRESETS = { "archon": { @@ -83,15 +87,18 @@ def query_framework_server( }, } -def create_inference_framework_server_from_presets(framework_type: str = None, - greedy_sample: bool = False, - verbose: bool = False, - time_generation: bool = False, - **kwargs, - ) -> callable: + +def create_inference_framework_server_from_presets( + framework_type: str = None, + greedy_sample: bool = False, + verbose: bool = False, + time_generation: bool = False, + **kwargs, +) -> callable: """ Return a callable function that queries LLM with given settings """ + def _query_llm(prompt: str | list[dict]): server_args = SERVER_PRESETS[framework_type].copy() @@ -103,7 +110,7 @@ def _query_llm(prompt: str | list[dict]): server_args["top_k"] = 1 if verbose: print(f"Querying server {framework_type} with args: {server_args}") - + if time_generation: start_time = time.time() response = query_framework_server( @@ -116,5 +123,5 @@ def _query_llm(prompt: str | list[dict]): return query_framework_server( prompt, framework_type=framework_type, **server_args ) - - return _query_llm \ No newline at end of file + + return _query_llm diff --git a/src/kernelbench/llm_utils.py b/src/kernelbench/llm_utils.py index f0162d6c..3b1b147c 100644 --- a/src/kernelbench/llm_utils.py +++ b/src/kernelbench/llm_utils.py @@ -31,11 +31,15 @@ # Inference Helpers ######################################################## + @cache def load_deepseek_tokenizer(): # TODO: Should we update this for new deepseek? Same tokenizer? # return AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-Coder-V2-Instruct-0724") - return AutoTokenizer.from_pretrained("deepseek-ai/DeepSeek-V2", trust_remote_code=True) + return AutoTokenizer.from_pretrained( + "deepseek-ai/DeepSeek-V2", trust_remote_code=True + ) + # Buffer because deepseek totally blocks us if we send stuff that's too long :( TOO_LONG_FOR_DEEPSEEK = 115_000 @@ -45,7 +49,7 @@ def is_safe_to_send_to_deepseek(prompt): tokenizer = load_deepseek_tokenizer() # print(f"Prompt: {len(prompt)}") # print(f"Prompt length: {len(tokenizer(prompt, verbose=False)['input_ids'])}") - + if isinstance(prompt, str): return ( len(tokenizer(prompt, verbose=False)["input_ids"]) < TOO_LONG_FOR_DEEPSEEK @@ -53,23 +57,23 @@ def is_safe_to_send_to_deepseek(prompt): else: return len(tokenizer.apply_chat_template(prompt)) < TOO_LONG_FOR_DEEPSEEK + def query_server( prompt: str | list[dict], # string if normal prompt, list of dicts if chat prompt, system_prompt: str = "You are a helpful assistant", # only used for chat prompts temperature: float = 0.0, - top_p: float = 1.0, # nucleus sampling - top_k: int = 50, + top_p: float = 1.0, # nucleus sampling + top_k: int = 50, max_tokens: int = 128, # max output tokens to generate num_completions: int = 1, server_port: int = 30000, # only for local server hosted on SGLang server_address: str = "localhost", server_type: str = "sglang", model_name: str = "default", # specify model type - # for reasoning models - is_reasoning_model: bool = False, # indiactor of using reasoning models - budget_tokens: int = 0, # for claude thinking - reasoning_effort: str = None, # only for o1 and o3 / more reasoning models in the future + is_reasoning_model: bool = False, # indiactor of using reasoning models + budget_tokens: int = 0, # for claude thinking + reasoning_effort: str = None, # only for o1 and o3 / more reasoning models in the future ): """ Query various sort of LLM inference API providers @@ -99,7 +103,11 @@ def query_server( max_retries=3, ) model = model_name - assert model in ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"], "Only support deepseek-chat or deepseek-coder for now" + assert model in [ + "deepseek-chat", + "deepseek-coder", + "deepseek-reasoner", + ], "Only support deepseek-chat or deepseek-coder for now" if not is_safe_to_send_to_deepseek(prompt): raise RuntimeError("Prompt is too long for DeepSeek") case "fireworks": @@ -123,9 +131,11 @@ def query_server( client = Together(api_key=TOGETHER_KEY) model = model_name case "sambanova": - client = OpenAI(api_key=SAMBANOVA_API_KEY, base_url="https://api.sambanova.ai/v1") + client = OpenAI( + api_key=SAMBANOVA_API_KEY, base_url="https://api.sambanova.ai/v1" + ) model = model_name - + case "openai": client = OpenAI(api_key=OPENAI_KEY) model = model_name @@ -140,7 +150,9 @@ def query_server( ) # Logic to query the LLM if server_type == "anthropic": - assert isinstance(prompt, str), f"The prompt must be a string for Anthropic, but it was a {type(prompt)}" + assert isinstance( + prompt, str + ), f"The prompt must be a string for Anthropic, but it was a {type(prompt)}" if is_reasoning_model: # Use beta endpoint with thinking enabled for reasoning models @@ -168,7 +180,11 @@ def query_server( top_k=top_k, max_tokens=max_tokens, ) - outputs = [choice.text for choice in response.content if not hasattr(choice, 'thinking') or not choice.thinking] + outputs = [ + choice.text + for choice in response.content + if not hasattr(choice, "thinking") or not choice.thinking + ] elif server_type == "google": # assert model_name == "gemini-1.5-flash-002", "Only test this for now" @@ -192,12 +208,12 @@ def query_server( return response.text elif server_type == "deepseek": - + if model in ["deepseek-chat", "deepseek-coder"]: - # regular deepseek model + # regular deepseek model response = client.chat.completions.create( - model=model, - messages=[ + model=model, + messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], @@ -208,12 +224,14 @@ def query_server( top_p=top_p, ) - else: # deepseek reasoner + else: # deepseek reasoner assert is_reasoning_model, "Only support deepseek-reasoner for now" - assert model == "deepseek-reasoner", "Only support deepseek-reasoner for now" + assert ( + model == "deepseek-reasoner" + ), "Only support deepseek-reasoner for now" response = client.chat.completions.create( - model=model, - messages=[ + model=model, + messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], @@ -226,8 +244,12 @@ def query_server( elif server_type == "openai": if is_reasoning_model: assert "o1" in model or "o3" in model, "Only support o1 and o3 for now" - print(f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}") - print(f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}") + print( + f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}" + ) + print( + f"Using OpenAI reasoning model: {model} with reasoning effort {reasoning_effort}" + ) response = client.chat.completions.create( model=model, messages=[ @@ -327,24 +349,20 @@ def query_server( # a list of presets for API server configs SERVER_PRESETS = { - "deepseek": { - "temperature": 1.6, - "model_name": "deepseek", - "max_tokens": 4096 - }, + "deepseek": {"temperature": 1.6, "model_name": "deepseek", "max_tokens": 4096}, "google": { "model_name": "gemini-1.5-flash-002", - "temperature": 0.7, # need to experiment with temperature + "temperature": 0.7, # need to experiment with temperature "max_tokens": 8192, }, - "together": { # mostly for Llama 3.1 + "together": { # mostly for Llama 3.1 "model_name": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", # "model_name": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", "temperature": 0.7, "max_tokens": 4096, }, "sglang": { # this is for running locally, mostly for Llama - "temperature": 0.8, # human eval pass@N temperature + "temperature": 0.8, # human eval pass@N temperature "server_port": 10210, "server_address": "matx2.stanford.edu", "max_tokens": 8192, @@ -368,15 +386,17 @@ def query_server( } -def create_inference_server_from_presets(server_type: str = None, - greedy_sample: bool = False, - verbose: bool = False, - time_generation: bool = False, - **kwargs, - ) -> callable: +def create_inference_server_from_presets( + server_type: str = None, + greedy_sample: bool = False, + verbose: bool = False, + time_generation: bool = False, + **kwargs, +) -> callable: """ Return a callable function that queries LLM with given settings """ + def _query_llm(prompt: str | list[dict]): server_args = SERVER_PRESETS[server_type].copy() @@ -388,18 +408,14 @@ def _query_llm(prompt: str | list[dict]): server_args["top_k"] = 1 if verbose: print(f"Querying server {server_type} with args: {server_args}") - + if time_generation: start_time = time.time() - response = query_server( - prompt, server_type=server_type, **server_args - ) + response = query_server(prompt, server_type=server_type, **server_args) end_time = time.time() print(f"[Timing] Inference took {end_time - start_time:.2f} seconds") return response else: - return query_server( - prompt, server_type=server_type, **server_args - ) - - return _query_llm \ No newline at end of file + return query_server(prompt, server_type=server_type, **server_args) + + return _query_llm diff --git a/src/kernelbench/make_hf_dataset.py b/src/kernelbench/make_hf_dataset.py index 3874b846..cb0e0420 100644 --- a/src/kernelbench/make_hf_dataset.py +++ b/src/kernelbench/make_hf_dataset.py @@ -13,7 +13,7 @@ # \"\"\" # def __init__(self): # super(Model, self).__init__() - + # def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor: # \"\"\" # Performs the matrix multiplication. @@ -53,7 +53,7 @@ # def __init__(self, in_channels, out_channels, kernel_size, bias_shape): # super(Model, self).__init__() # self.conv = nn.Conv2d(in_channels, out_channels, kernel_size) -# self.bias = nn.Parameter(torch.randn(bias_shape)) +# self.bias = nn.Parameter(torch.randn(bias_shape)) # def forward(self, x): # x = self.conv(x) @@ -92,19 +92,19 @@ # :param output_size: The number of output features # \"\"\" # super(Model, self).__init__() - + # layers = [] # current_input_size = input_size - + # for layer_size in layer_sizes: # layers.append(nn.Linear(current_input_size, layer_size)) # layers.append(nn.ReLU()) # current_input_size = layer_size - + # layers.append(nn.Linear(current_input_size, output_size)) - + # self.network = nn.Sequential(*layers) - + # def forward(self, x): # \"\"\" # :param x: The input tensor, shape (batch_size, input_size) @@ -132,14 +132,17 @@ # dataset_example_1, # dataset_example_2, # dataset_example_3 -#] +# ] dataset_list = [] + def make_dataset_examples(dir_path, level): global dataset_list # list all files in the directory - file_list = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] + file_list = [ + f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f)) + ] file_list = sorted(file_list) # count = 0 for f in file_list: @@ -150,12 +153,7 @@ def make_dataset_examples(dir_path, level): code = open(file_path, "r").read() name = f.split(".")[0] problem_id = int(name.split("_")[0]) - json_object = { - "code": "", - "level": 0, - "name": "", - "problem_id": 0 - } + json_object = {"code": "", "level": 0, "name": "", "problem_id": 0} json_object["code"] = code json_object["level"] = level json_object["name"] = name @@ -163,6 +161,7 @@ def make_dataset_examples(dir_path, level): dataset_list.append(json_object) # count += 1 + make_dataset_examples("../KernelBench/level1", 1) make_dataset_examples("../KernelBench/level2", 2) make_dataset_examples("../KernelBench/level3", 3) @@ -179,11 +178,13 @@ def make_dataset_examples(dir_path, level): hf_level_3 = Dataset.from_list(level_3) hf_level_4 = Dataset.from_list(level_4) -dataset_dict = DatasetDict({ - "level_1": hf_level_1, - "level_2": hf_level_2, - "level_3": hf_level_3, - "level_4": hf_level_4 -}) +dataset_dict = DatasetDict( + { + "level_1": hf_level_1, + "level_2": hf_level_2, + "level_3": hf_level_3, + "level_4": hf_level_4, + } +) -dataset_dict.push_to_hub("ScalingIntelligence/KernelBench") \ No newline at end of file +dataset_dict.push_to_hub("ScalingIntelligence/KernelBench") diff --git a/src/kernelbench/prompt_constructor.py b/src/kernelbench/prompt_constructor.py index e2755612..af900485 100644 --- a/src/kernelbench/prompt_constructor.py +++ b/src/kernelbench/prompt_constructor.py @@ -6,9 +6,9 @@ """ Construct Prompt -Design principles: +Design principles: - To evaluate base model performance on KernelBench, we use the simplest prompt possible to guide model output to generated desired output format. -- However, we do not do extensive prompt engineering or few-shot example in the LLM to steer behaviour. +- However, we do not do extensive prompt engineering or few-shot example in the LLM to steer behaviour. """ REPO_TOP_PATH = os.path.abspath( @@ -43,6 +43,7 @@ def get_arch_definition(arch_src): Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Just output the new model code, no other text, and NO testing code! \n """ + @weave.op def prompt_generate_custom_cuda( arc_src: str, example_arch_src: str, example_new_arch_src: str @@ -55,7 +56,7 @@ def prompt_generate_custom_cuda( ``` \n {example_arch_src} ``` \n - The example new arch with custom CUDA kernels looks like this: + The example new arch with custom CUDA kernels looks like this: ``` {example_new_arch_src} ``` \n @@ -77,13 +78,16 @@ def prompt_generate_custom_cuda( Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Just output the new model code, no other text, and NO testing code! \n """ + @weave.op -def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: list) -> str: +def prompt_generate_custom_cuda_fewshot_and_template( + ref_arch_src: str, shots: list +) -> str: """ - Generate a prompt with specified few-shot examples following a template + Generate a prompt with specified few-shot examples following a template shots: list of few-shot examples to include in the prompt - Avaliable few shot options to start with: + Avaliable few shot options to start with: - ex_add: pointwise addition - ex_fuse_gelu: fused gelu - ex_mnist2: fused convolutions and relus (DEPRECATED) @@ -117,7 +121,9 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l example_mnist2_new = read_file( os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") ) - exmaple_mnist2_desc = "This given architecture is for a model with fused convolutions and relus: " + exmaple_mnist2_desc = ( + "This given architecture is for a model with fused convolutions and relus: " + ) # k = 4 example_tiled_matmul = read_file( @@ -126,7 +132,9 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l example_tiled_matmul_new = read_file( os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") ) - example_tiled_matmul_desc = "This given architecture is for a model with tiled matrix multiplication: " + example_tiled_matmul_desc = ( + "This given architecture is for a model with tiled matrix multiplication: " + ) # k = 5 example_flash_attn = read_file( @@ -139,20 +147,35 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l examples = [] for s in shots: - if s not in ["ex_add", "ex_fuse_gelu", "ex_mnist2", "ex_tiled_matmul", "ex_flash_attn"]: + if s not in [ + "ex_add", + "ex_fuse_gelu", + "ex_mnist2", + "ex_tiled_matmul", + "ex_flash_attn", + ]: raise ValueError(f"Invalid shot: {s}") elif s == "ex_add": examples.append((example_add, example_add_new, example_add_desc)) elif s == "ex_fuse_gelu": - examples.append((example_fuse_gelu, example_fuse_gelu_new, example_fuse_gelu_desc)) - elif s == "ex_mnist2": # DEPRECATED + examples.append( + (example_fuse_gelu, example_fuse_gelu_new, example_fuse_gelu_desc) + ) + elif s == "ex_mnist2": # DEPRECATED raise ValueError("ex_mnist2 is deprecated") examples.append((example_mnist2, example_mnist2_new, exmaple_mnist2_desc)) elif s == "ex_tiled_matmul": - examples.append((example_tiled_matmul, example_tiled_matmul_new, example_tiled_matmul_desc)) + examples.append( + ( + example_tiled_matmul, + example_tiled_matmul_new, + example_tiled_matmul_desc, + ) + ) elif s == "ex_flash_attn": - examples.append((example_flash_attn, example_flash_attn_new, example_flash_attn_desc)) - + examples.append( + (example_flash_attn, example_flash_attn_new, example_flash_attn_desc) + ) for i, tup in enumerate(examples): base, kernel, desc = tup @@ -170,7 +193,7 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l ```\n\n """ -# should we put task here? + # should we put task here? prompt += f""" Task:\n\n Here is an example architecture:\n\n @@ -181,11 +204,12 @@ def prompt_generate_custom_cuda_fewshot_and_template(ref_arch_src: str, shots: l prompt += PROBLEM_INSTRUCTION_CLEANED return prompt + @weave.op def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> str: """ - Generate a prompt with a CoT example following a template - Avaliable CoT examples: + Generate a prompt with a CoT example following a template + Avaliable CoT examples: - ex_fuse_gelu: fused gelu - ex_mnist2: fused convolutions and relus - ex_tiled_matmul: tiled matrix multiplication @@ -193,13 +217,13 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> # I updated this to allow CoT. Also explicilty state think step by step. PROBLEM_INSTRUCTION_COT = """ -Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Do not output testing code. +Optimize the architecture named Model with custom CUDA operators! Name your optimized output architecture ModelNew. Output the new code in codeblocks. Please generate real code, NOT pseudocode, make sure the code compiles and is fully functional. Do not output testing code. In the end, make sure the final code block contains code for output architecture ModelNew with cuda code.\n Let's think step by step.\n -""" +""" prompt = PROBLEM_STATEMENT_CLEANED - + assert cot_example in ["ex_fuse_gelu", "ex_mnist2", "ex_tiled_matmul"] # k = 2 @@ -224,7 +248,9 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> example_mnist2_new = read_file( os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") ) - exmaple_mnist2_desc = "This given architecture is for a model with fused convolutions and relus: " + exmaple_mnist2_desc = ( + "This given architecture is for a model with fused convolutions and relus: " + ) # k = 4 example_tiled_matmul = read_file( @@ -236,8 +262,10 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> example_tiled_matmul_new = read_file( os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") ) - example_tiled_matmul_desc = "This given architecture is for a model with tiled matrix multiplication: " - + example_tiled_matmul_desc = ( + "This given architecture is for a model with tiled matrix multiplication: " + ) + match cot_example: case "ex_fuse_gelu": base = example_fuse_gelu @@ -255,9 +283,11 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> kernel = example_tiled_matmul_new desc = example_tiled_matmul_desc case _: - raise ValueError(f"Invalid CoT example: {cot_example} not found in CoT examples") + raise ValueError( + f"Invalid CoT example: {cot_example} not found in CoT examples" + ) - # construct example with + # construct example with # NOTE: we only do one example with CoT for now # 1. ref_src problem -> 2. Instruction -> 3. CoT -> 4. Solution prompt += f""" @@ -272,7 +302,7 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> ```\n\n """ -# show task to solve + # show task to solve prompt += f""" Task:\n\n Here is an example architecture:\n\n @@ -285,7 +315,6 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> return prompt - def prompt_generate_custom_cuda_from_file_one_example(ref_arch_src, example_ind=1): """ Deprecated: use prompt_generate_custom_cuda_from_prompt_template instead @@ -328,9 +357,7 @@ def prompt_generate_custom_cuda_from_prompt_template(ref_arch_src: str) -> str: # These are strictly defined for now # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) - example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_add.py" - ) + example_arch_path = os.path.join(REPO_TOP_PATH, f"src/prompts/model_ex_add.py") example_new_arch_path = os.path.join( REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" ) @@ -350,9 +377,11 @@ def prompt_generate_custom_cuda_from_prompt_template(ref_arch_src: str) -> str: return prompt_generate_custom_cuda(arch, example_arch, example_new_arch) -def prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src: str, gpu_name: str) -> str: +def prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src: str, gpu_name: str +) -> str: """ - Similar to prompt_generate_custom_cuda_from_prompt_template, + Similar to prompt_generate_custom_cuda_from_prompt_template, but with hardware information for the given GPU """ @@ -360,34 +389,36 @@ def prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src: str, g # These are strictly defined for now # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) - example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_add.py" - ) + example_arch_path = os.path.join(REPO_TOP_PATH, f"src/prompts/model_ex_add.py") example_new_arch_path = os.path.join( REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" ) - gpu_spec_file_path = os.path.join(REPO_TOP_PATH, f"src/prompts/hardware/gpu_specs.py") + gpu_spec_file_path = os.path.join( + REPO_TOP_PATH, f"src/prompts/hardware/gpu_specs.py" + ) example_arch = read_file(example_arch_path) example_new_arch = read_file(example_new_arch_path) gpu_spec_info = read_file(gpu_spec_file_path) return prompt_generate_prompt_with_hardware_info( - ref_arch_src=arch, - gpu_name=gpu_name, - example_arch_src=example_arch, - example_new_arch_src=example_new_arch, - gpu_spec_info_src=gpu_spec_info - ) - + ref_arch_src=arch, + gpu_name=gpu_name, + example_arch_src=example_arch, + example_new_arch_src=example_new_arch, + gpu_spec_info_src=gpu_spec_info, + ) + @weave.op -def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, - gpu_name: str, - example_arch_src: str, - example_new_arch_src: str, - gpu_spec_info_src: str) -> str: +def prompt_generate_prompt_with_hardware_info( + ref_arch_src: str, + gpu_name: str, + example_arch_src: str, + example_new_arch_src: str, + gpu_spec_info_src: str, +) -> str: """ Generate a prompt with hardware information for the given GPU gpu_spec_info_src: str of the gpu spec src file @@ -395,24 +426,26 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, # Create a dictionary to store the local namespace local_dict = {} - + # Execute the GPU spec file in the local namespace exec(gpu_spec_info_src, {}, local_dict) - + # Get the required variables from the local namespace - GPU_SPEC_INFO = local_dict.get('GPU_SPEC_INFO') - GPU_DEFINITIONS = local_dict.get('GPU_DEFINITIONS') - GPU_BEST_PRACTICES = local_dict.get('GPU_BEST_PRACTICES') - + GPU_SPEC_INFO = local_dict.get("GPU_SPEC_INFO") + GPU_DEFINITIONS = local_dict.get("GPU_DEFINITIONS") + GPU_BEST_PRACTICES = local_dict.get("GPU_BEST_PRACTICES") + if not GPU_SPEC_INFO or not GPU_DEFINITIONS or not GPU_BEST_PRACTICES: - raise ValueError("GPU_SPEC_INFO or GPU_DEFINITIONS or GPU_BEST_PRACTICES not found in gpu_spec_info_src") + raise ValueError( + "GPU_SPEC_INFO or GPU_DEFINITIONS or GPU_BEST_PRACTICES not found in gpu_spec_info_src" + ) assert gpu_name in GPU_SPEC_INFO, f"GPU name {gpu_name} not found in GPU_SPEC_INFO" # Get GPU-specific information curr_gpu_spec_info = GPU_SPEC_INFO[gpu_name] gpu_architecture = curr_gpu_spec_info.get("GPU Architecture") - + # Create the title and objective section objective_section = """# CUDA Kernel Optimization Task @@ -432,21 +465,21 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, continue hardware_specs.append(f"- {value} of {key}") hardware_section += "\n".join(hardware_specs) - + # Create GPU concepts section concepts_section = "\n\n## Key GPU Programming Concepts" concepts = [] for key, value in GPU_DEFINITIONS.items(): concepts.append(f"- {key}: {value}") concepts_section += "\n" + "\n".join(concepts) - + # Create best practices section practices_section = "\n\n## Best Practices" practices = [] for best_practice in GPU_BEST_PRACTICES: practices.append(f"- {best_practice}") practices_section += "\n" + "\n".join(practices) - + # Create examples section if provided examples_section = "" if example_arch_src and example_new_arch_src: @@ -461,7 +494,7 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, {example_new_arch_src} ``` """ - + # Create task section task_section = f""" ## Your Task: Optimize This Model @@ -471,19 +504,22 @@ def prompt_generate_prompt_with_hardware_info(ref_arch_src: str, Implement an optimized version called "ModelNew" with custom CUDA operators. """ - + # Combine all sections into the final prompt - prompt = objective_section + hardware_section + concepts_section + practices_section + examples_section + task_section - - return prompt + prompt = ( + objective_section + + hardware_section + + concepts_section + + practices_section + + examples_section + + task_section + ) + return prompt return Nonoe - - - def prompt_fix_compile(ref_arch_src, custom_cuda, metadata): prompt = PROBLEM_STATEMENT prompt += f""" @@ -499,7 +535,7 @@ def prompt_fix_compile(ref_arch_src, custom_cuda, metadata): ``` {metadata} ``` - + Please fix the compilation error in the new model code. Please output the corrected code in codeblocks. """ return prompt @@ -524,14 +560,16 @@ def prompt_fix_correctness(ref_arch_src, custom_cuda, metadata): """ return prompt + @weave.op def main(): gpu_name = "L40S" - ref_arch_src = read_file(os.path.join(KERNEL_BENCH_PATH, f"level1/19_ReLU.py")) assert len(ref_arch_src) > 0, "ref_arch_src is empty" - prompt = prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src, gpu_name) + prompt = prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src, gpu_name + ) print(prompt) # Write prompt to temp file temp_file_path = os.path.join(REPO_TOP_PATH, "scratch", "prompt_draft.txt") @@ -539,6 +577,7 @@ def main(): with open(temp_file_path, "w") as f: f.write(prompt) + if __name__ == "__main__": weave.init("prompt_constructor") main() diff --git a/src/kernelbench/prompts/README.md b/src/kernelbench/prompts/README.md index 3aa01b58..6df80cca 100644 --- a/src/kernelbench/prompts/README.md +++ b/src/kernelbench/prompts/README.md @@ -1,5 +1,5 @@ -This folder includes PyTorch modules paired with CUDA kernels, which are used as in-context examples in KernelBench. +This folder includes PyTorch modules paired with CUDA kernels, which are used as in-context examples in KernelBench. @@ -8,4 +8,4 @@ Acknowledgements: - Minimal Flash Attention: [Peter Kim, Minimal Flash Attention](https://github.com/tspeterkim/flash-attention-minimal/tree/main) There are some examples. -[TODO] Table detailing content and speedups of each example \ No newline at end of file +[TODO] Table detailing content and speedups of each example diff --git a/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py b/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py index de740c03..cd9c8741 100644 --- a/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py +++ b/src/kernelbench/prompts/cot/model_cot_fuse_gelu.py @@ -2,6 +2,7 @@ Let us think about how to optimize the code step by step. """ + # Step 1. Let us break down the pytorch module into step by step instructions. class Model(nn.Module): def __init__(self) -> None: @@ -18,7 +19,7 @@ def forward(self, x): Returns: torch.Tensor: Output tensor after applying GELU activation """ - + # First, alculate the constant term (2/pi)^0.5 const = (2 / torch.pi) ** 0.5 @@ -39,6 +40,6 @@ def forward(self, x): Third, we can compute: float inner_term = x + 0.044715f * (x*x*x) Fourth, we can compute: float out[i] = 0.5f * x * (1.0f + tanhf(const * inner_term)) -""" +""" # Step 3. Let us put all of the steps together into CUDA kernel code. diff --git a/src/kernelbench/prompts/cot/model_cot_mnist2.py b/src/kernelbench/prompts/cot/model_cot_mnist2.py index 0a9d5ef1..c6640187 100644 --- a/src/kernelbench/prompts/cot/model_cot_mnist2.py +++ b/src/kernelbench/prompts/cot/model_cot_mnist2.py @@ -2,14 +2,15 @@ Let us think about how to optimize the code step by step. """ + # Step 1: Let us break down the PyTorch module into step-by-step instructions. class Model(nn.Module): def __init__(self) -> None: super().__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) # First convolutional layer - self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # Second convolutional layer - self.fc1 = nn.Linear(320, 50) # First fully connected layer - self.fc2 = nn.Linear(50, 10) # Second fully connected layer + self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # Second convolutional layer + self.fc1 = nn.Linear(320, 50) # First fully connected layer + self.fc2 = nn.Linear(50, 10) # Second fully connected layer def forward(self, x): """ @@ -31,10 +32,11 @@ def forward(self, x): """ x = F.relu(F.max_pool2d(self.conv1(x), 2)) # Steps 1-3 x = F.relu(F.max_pool2d(self.conv2(x), 2)) # Steps 4-6 - x = x.view(-1, 320) # Step 7 - x = F.relu(self.fc1(x)) # Steps 8-9 - x = self.fc2(x) # Step 10 - return F.log_softmax(x, dim=1) # Step 11 + x = x.view(-1, 320) # Step 7 + x = F.relu(self.fc1(x)) # Steps 8-9 + x = self.fc2(x) # Step 10 + return F.log_softmax(x, dim=1) # Step 11 + # Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. """ @@ -73,4 +75,3 @@ def forward(self, x): """ # Step 3. Let us put all of the steps together into CUDA kernel code. - diff --git a/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py b/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py index c57620d9..c331c897 100644 --- a/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py +++ b/src/kernelbench/prompts/cot/model_cot_tiled_matmul.py @@ -2,6 +2,7 @@ Let us think about how to optimize the code step by step. """ + # Step 1: Let us break down the PyTorch module into step-by-step instructions. class Model(nn.Module): def __init__(self) -> None: @@ -13,7 +14,7 @@ def forward(self, a, b): 1. The input tensors `a` and `b` must have compatible shapes for matrix multiplication. 2. Each element of the resulting tensor is computed as the dot product of a row of `a` and a column of `b`. - + Args: a (torch.Tensor): A tensor of shape (m, n). b (torch.Tensor): A tensor of shape (n, p). @@ -23,7 +24,8 @@ def forward(self, a, b): """ return a @ b -#Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. + +# Step 2: Let us describe how each step could be implemented inside of a CUDA kernel. """ 1. Load the input tensor elements into shared memory: - Each thread block loads a tile of `a` and `b` into shared memory to reduce global memory accesses. diff --git a/src/kernelbench/prompts/few_shot/model_ex_add.py b/src/kernelbench/prompts/few_shot/model_ex_add.py index f52d64b7..9016fb0e 100644 --- a/src/kernelbench/prompts/few_shot/model_ex_add.py +++ b/src/kernelbench/prompts/few_shot/model_ex_add.py @@ -20,4 +20,4 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture - return [] \ No newline at end of file + return [] diff --git a/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py index 64d34e05..d7702ae9 100644 --- a/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py +++ b/src/kernelbench/prompts/few_shot/model_ex_flash_attn.py @@ -8,20 +8,23 @@ class Model(nn.Module): """ Model that performs an attention operation """ + def __init__(self) -> None: super().__init__() def forward(self, Q, K, V): - att = (Q @ K.transpose(-2, -1) * (1.0 / math.sqrt(K.size(-1)))) + att = Q @ K.transpose(-2, -1) * (1.0 / math.sqrt(K.size(-1))) att = F.softmax(att, dim=-1) y = att @ V return y + batch_size = 32 n_head = 12 seq_len = 64 head_embd = 32 + def get_inputs(): # randomly generate input tensors based on the model architecture Q = torch.randn(batch_size, n_head, seq_len, head_embd) diff --git a/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py index 62fab61f..bc6727cb 100644 --- a/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py +++ b/src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py @@ -8,7 +8,7 @@ def __init__(self) -> None: super().__init__() def forward(self, x): - return F.gelu(x, approximate='tanh') + return F.gelu(x, approximate="tanh") def get_inputs(): @@ -20,5 +20,3 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture return [] - - diff --git a/src/kernelbench/prompts/few_shot/model_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_ex_mnist2.py index b870d395..dce2050a 100644 --- a/src/kernelbench/prompts/few_shot/model_ex_mnist2.py +++ b/src/kernelbench/prompts/few_shot/model_ex_mnist2.py @@ -28,4 +28,4 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture - return [] \ No newline at end of file + return [] diff --git a/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py index b4fd3bc4..9fed9469 100644 --- a/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py +++ b/src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py @@ -8,7 +8,7 @@ def __init__(self) -> None: super().__init__() def forward(self, a, b): - return a@b + return a @ b def get_inputs(): @@ -21,4 +21,3 @@ def get_inputs(): def get_init_inputs(): # randomly generate tensors required for initialization based on the model architecture return [] - diff --git a/src/kernelbench/prompts/few_shot/model_new_ex_add.py b/src/kernelbench/prompts/few_shot/model_new_ex_add.py index e9805c11..e103a502 100644 --- a/src/kernelbench/prompts/few_shot/model_new_ex_add.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_add.py @@ -28,9 +28,7 @@ } """ -cpp_src = ( - "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" -) +cpp_src = "torch::Tensor elementwise_add_cuda(torch::Tensor a, torch::Tensor b);" # Compile the inline CUDA code for element-wise addition elementwise_add = load_inline( diff --git a/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py b/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py index b3df395c..8c67cb40 100644 --- a/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py @@ -3,7 +3,7 @@ import torch.nn.functional as F from torch.utils.cpp_extension import load_inline -source = ''' +source = """ #include #include #include @@ -119,17 +119,17 @@ ); return O; } -''' +""" cpp_src = """ torch::Tensor attention(torch::Tensor Q, torch::Tensor K, torch::Tensor V);""" attention = torch.utils.cpp_extension.load_inline( - 'attention', + "attention", cpp_sources=cpp_src, cuda_sources=source, - functions=['attention'], + functions=["attention"], with_cuda=True, - extra_cuda_cflags=['-O2'], + extra_cuda_cflags=["-O2"], ) diff --git a/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py b/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py index dd82c1c6..3e9e1df0 100644 --- a/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py @@ -72,7 +72,7 @@ name="fused_gelu", # Name of the extension cpp_sources=cpp_src, # C++ source code cuda_sources=source, # CUDA source code - functions=['my_gelu', 'my_gelu_out'], # Functions to expose + functions=["my_gelu", "my_gelu_out"], # Functions to expose verbose=True, extra_cflags=[""], extra_ldflags=[""], diff --git a/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py b/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py index c65d295b..8dfd221f 100644 --- a/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py @@ -46,34 +46,37 @@ dim3 num_blocks((output_width + block_size.x - 1) / block_size.x, (output_height + block_size.y - 1) / block_size.y, channels); max_pool2d_kernel<<>>( - input.data_ptr(), - output.data_ptr(), - channels, - input_height, - input_width, - kernel_size, - kernel_size, - stride, - output_height, + input.data_ptr(), + output.data_ptr(), + channels, + input_height, + input_width, + kernel_size, + kernel_size, + stride, + output_height, output_width ); return output; } """ -cpp_src = "torch::Tensor max_pool2d_cuda(torch::Tensor input, int kernel_size, int stride);" +cpp_src = ( + "torch::Tensor max_pool2d_cuda(torch::Tensor input, int kernel_size, int stride);" +) # Compile the inline CUDA code custom_max_pool = load_inline( - name='custom_max_pool', + name="custom_max_pool", cpp_sources=cpp_src, cuda_sources=source, - functions=['max_pool2d_cuda'], + functions=["max_pool2d_cuda"], verbose=True, extra_cflags=[""], extra_ldflags=[""], ) + # Custom MNIST model using inlined max_pool2d_cuda class ModelNew(nn.Module): def __init__(self) -> None: diff --git a/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py b/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py index dffe8e6c..ebc1cd1f 100644 --- a/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py +++ b/src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py @@ -25,7 +25,7 @@ /** * @brief Tiled matrix multiplication kernel. - * + * * This kernel performs matrix multiplication using shared memory tiles to improve performance. * * @param out Pointer to the output matrix @@ -73,7 +73,7 @@ /** * @brief Wrapper function for tiled matrix multiplication kernel. - * + * * This function checks input tensors, sets up kernel parameters, and launches the CUDA kernel. * * @param m First input matrix @@ -112,11 +112,12 @@ "tiled_matmul", # Name of the extension cpp_sources=cpp_src, # C++ interface cuda_sources=source, # CUDA source code - functions=['tiled_matmul_cuda'], # Exported functions - extra_cuda_cflags=['--ptxas-options=-v'], # Additional CUDA compilation flags - verbose=True # Enable verbose output during compilation + functions=["tiled_matmul_cuda"], # Exported functions + extra_cuda_cflags=["--ptxas-options=-v"], # Additional CUDA compilation flags + verbose=True, # Enable verbose output during compilation ) + class ModelNew(nn.Module): def __init__(self) -> None: super().__init__() diff --git a/src/kernelbench/prompts/hardware/gpu_specs.py b/src/kernelbench/prompts/hardware/gpu_specs.py index dcf60c7f..756cee0b 100644 --- a/src/kernelbench/prompts/hardware/gpu_specs.py +++ b/src/kernelbench/prompts/hardware/gpu_specs.py @@ -3,7 +3,6 @@ """ - GPU_SPEC_INFO = { "L40S": { "GPU Architecture": "Ada", @@ -89,7 +88,7 @@ "Maximum number of thread blocks per SM": "24", "Shared memory capacity per SM": "100 KB", "Maximum shared memory per thread block": "99 KB", - }, + }, "T4": { "GPU Architecture": "Turing", "GPU Memory": "16 GB GDDR6", @@ -118,7 +117,7 @@ "Maximum number of thread blocks per SM": "32", "Shared memory capacity per SM": "164 KB", "Maximum shared memory per thread block": "163 KB", - } + }, } # Basic GPU concept definitions @@ -135,7 +134,6 @@ } - GPU_BEST_PRACTICES = [ # From https://docs.nvidia.com/cuda/ada-tuning-guide/index.html # CUDA Best Practices Section @@ -147,4 +145,4 @@ "Avoid long sequences of diverged execution by threads within the same warp.", # we added this to reference the specific GPU architecture "Use specialized instructions based on the specific GPU architecture", -] \ No newline at end of file +] diff --git a/src/kernelbench/score.py b/src/kernelbench/score.py index 09b8c1fa..d44edf30 100644 --- a/src/kernelbench/score.py +++ b/src/kernelbench/score.py @@ -1,23 +1,37 @@ import numpy as np -def geometric_mean_speed_ratio_correct_only(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int) -> float: + +def geometric_mean_speed_ratio_correct_only( + is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int +) -> float: """ Geometric mean of the speed ratio for correct samples """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) speed_up = filtered_baseline_speed / filtered_actual_speed prod = np.prod(speed_up) - n_correct = np.sum(is_correct) # Count number of correct samples + n_correct = np.sum(is_correct) # Count number of correct samples return prod ** (1 / n_correct) if n_correct > 0 else 0 -def geometric_mean_speed_ratio_correct_and_faster_only(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int) -> float: + +def geometric_mean_speed_ratio_correct_and_faster_only( + is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int +) -> float: """ Geometric mean of the speed ratio for correct samples that have speedup > 1 """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) speed_up = filtered_baseline_speed / filtered_actual_speed speed_up = np.array([x for x in speed_up if x > 1]) prod = np.prod(speed_up) @@ -25,12 +39,23 @@ def geometric_mean_speed_ratio_correct_and_faster_only(is_correct: np.ndarray, b return prod ** (1 / n_correct_and_faster) if n_correct_and_faster > 0 else 0 -def fastp(is_correct: np.ndarray, baseline_speed: np.ndarray, actual_speed: np.ndarray, n: int, p: float) -> float: + +def fastp( + is_correct: np.ndarray, + baseline_speed: np.ndarray, + actual_speed: np.ndarray, + n: int, + p: float, +) -> float: """ Rate of samples within a threshold p """ - filtered_baseline_speed = np.array([x for i, x in enumerate(baseline_speed) if is_correct[i]]) - filtered_actual_speed = np.array([x for i, x in enumerate(actual_speed) if is_correct[i]]) + filtered_baseline_speed = np.array( + [x for i, x in enumerate(baseline_speed) if is_correct[i]] + ) + filtered_actual_speed = np.array( + [x for i, x in enumerate(actual_speed) if is_correct[i]] + ) speed_up = filtered_baseline_speed / filtered_actual_speed fast_p_score = np.sum(speed_up > p) - return fast_p_score / n if n > 0 else 0 \ No newline at end of file + return fast_p_score / n if n > 0 else 0 diff --git a/src/kernelbench/unit_tests/test_dataset.py b/src/kernelbench/unit_tests/test_dataset.py index a23768e0..c925fe5f 100644 --- a/src/kernelbench/unit_tests/test_dataset.py +++ b/src/kernelbench/unit_tests/test_dataset.py @@ -1,9 +1,8 @@ - import pytest from src.dataset import get_code_hash """ -Usage +Usage pytest test_dataset.py """ @@ -14,16 +13,16 @@ def test_get_code_hash(): """ code_snippet_batch_1_v1 = """ - import torch + import torch # This is for a single batch ''' Some random multi-line comment ''' B = 1 """ - + code_snippet_batch_1_v2 = """ - import torch + import torch ''' More problem descriptions (updated) ''' @@ -33,7 +32,7 @@ def test_get_code_hash(): """ code_snippet_batch_64 = """ - import torch + import torch # This is for a single batch ''' Some random multi-line comment @@ -41,8 +40,10 @@ def test_get_code_hash(): B = 64 """ - assert get_code_hash(code_snippet_batch_1_v1) == get_code_hash(code_snippet_batch_1_v2), \ - "Hash should be equal for semantically equivalent code with different comments" - - assert get_code_hash(code_snippet_batch_1_v1) != get_code_hash(code_snippet_batch_64), \ - "Hash should differ for code with different batch sizes" \ No newline at end of file + assert get_code_hash(code_snippet_batch_1_v1) == get_code_hash( + code_snippet_batch_1_v2 + ), "Hash should be equal for semantically equivalent code with different comments" + + assert get_code_hash(code_snippet_batch_1_v1) != get_code_hash( + code_snippet_batch_64 + ), "Hash should differ for code with different batch sizes" diff --git a/src/kernelbench/unit_tests/test_score.py b/src/kernelbench/unit_tests/test_score.py index 918b1f12..ae53be59 100644 --- a/src/kernelbench/unit_tests/test_score.py +++ b/src/kernelbench/unit_tests/test_score.py @@ -2,122 +2,170 @@ from src.score import * import math -''' +""" Usage: pytest test_score.py -''' +""" abs_tol = 0.0000001 + def test_geometric_mean_speed_ratio_correct_only(): - is_correct = [1,0,1,1,0] + is_correct = [1, 0, 1, 1, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 - ''' + """ Geometric mean of the speed ratio for correct samples - ''' - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 1.185631101, abs_tol=abs_tol) - - is_correct = [1,1,1,1,0] + """ + assert math.isclose( + geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ), + 1.185631101, + abs_tol=abs_tol, + ) + + is_correct = [1, 1, 1, 1, 0] baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] n = 5 - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 0.801816719, abs_tol=abs_tol) + assert math.isclose( + geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ), + 0.801816719, + abs_tol=abs_tol, + ) - is_correct = [0,0,0,0,0] + is_correct = [0, 0, 0, 0, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 - assert math.isclose(geometric_mean_speed_ratio_correct_only(is_correct, baseline_speed, actual_speed, n), 0, abs_tol=abs_tol) + assert math.isclose( + geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ), + 0, + abs_tol=abs_tol, + ) def test_geometric_mean_speed_ratio_correct_and_faster_only(): - is_correct = [1,0,1,1,0] + is_correct = [1, 0, 1, 1, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 - ''' + """ Geometric mean of the speed ratio for correct samples that have speedup > 1 - ''' + """ - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 5, abs_tol=abs_tol) + assert math.isclose( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct, baseline_speed, actual_speed, n + ), + 5, + abs_tol=abs_tol, + ) - is_correct = [1,1,1,1,0] + is_correct = [1, 1, 1, 1, 0] baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] n = 5 - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 1.033333333, abs_tol=abs_tol) + assert math.isclose( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct, baseline_speed, actual_speed, n + ), + 1.033333333, + abs_tol=abs_tol, + ) - is_correct = [0,0,0,0,0] + is_correct = [0, 0, 0, 0, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 - assert math.isclose(geometric_mean_speed_ratio_correct_and_faster_only(is_correct, baseline_speed, actual_speed, n), 0, abs_tol=abs_tol) + assert math.isclose( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct, baseline_speed, actual_speed, n + ), + 0, + abs_tol=abs_tol, + ) -def test_fastp(): - ''' +def test_fastp(): + """ Rate of samples within a threshold p (1.0) - ''' + """ - is_correct = [1,0,1,1,0] + is_correct = [1, 0, 1, 1, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 p = 1.0 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol + ) - is_correct = [1,1,1,1,0] + is_correct = [1, 1, 1, 1, 0] baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] n = 5 p = 1.0 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol + ) - is_correct = [0,0,0,0,0] + is_correct = [0, 0, 0, 0, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 p = 1.0 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol + ) - ''' + """ Rate of samples within a threshold p (0.5) - ''' + """ - is_correct = [1,0,1,1,0] + is_correct = [1, 0, 1, 1, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 p = 0.5 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.4, abs_tol=abs_tol) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0.4, abs_tol=abs_tol + ) - is_correct = [1,1,1,1,0] + is_correct = [1, 1, 1, 1, 0] baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] n = 5 p = 0.5 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0.6, abs_tol=abs_tol) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0.6, abs_tol=abs_tol + ) - is_correct = [0,0,0,0,0] + is_correct = [0, 0, 0, 0, 0] baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] n = 5 p = 0.5 - assert math.isclose(fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol) - + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol + ) diff --git a/src/kernelbench/unit_tests/test_utils.py b/src/kernelbench/unit_tests/test_utils.py index 9b569dd5..396601ae 100644 --- a/src/kernelbench/unit_tests/test_utils.py +++ b/src/kernelbench/unit_tests/test_utils.py @@ -1,7 +1,8 @@ -import pytest # noqa - +import pytest # noqa + from src.utils import extract_first_code, extract_code_blocks, extract_last_code + def check_code_assertions(code: str, expected_code: str): """ Check code is equivalent (don't worry about whitespace) @@ -9,7 +10,9 @@ def check_code_assertions(code: str, expected_code: str): if code is None: assert expected_code == "" else: - assert code.replace("\n", "").replace(" ", "") == expected_code.replace("\n", "").replace(" ", "") + assert code.replace("\n", "").replace(" ", "") == expected_code.replace( + "\n", "" + ).replace(" ", "") def test_extract_last_code(): @@ -21,8 +24,7 @@ def hello(): ``` and it says more stuff afterwards""" code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - + check_code_assertions(code, 'def hello():\n print("Hello")') example_output = """The LLM wrote some code here ```cpp @@ -31,15 +33,14 @@ def hello(): } ``` - and some other code block + and some other code block ```python def hello(): print("Hello") - ``` + ``` and it says more stuff afterwards""" code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") - + check_code_assertions(code, 'def hello():\n print("Hello")') def test_extract_first_code(): @@ -50,13 +51,13 @@ def hello(): print("Hello") ``` and it says more stuff afterwards""" - + code = extract_first_code(example_output, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") + check_code_assertions(code, 'def hello():\n print("Hello")') # Test with no code block text = "Some code here" - code = extract_first_code(text, ["python", "cpp"]) + code = extract_first_code(text, ["python", "cpp"]) check_code_assertions(code, "") # Test with empty code block @@ -64,7 +65,6 @@ def hello(): code = extract_first_code(text, ["python", "cpp"]) check_code_assertions(code, "") - # Test with multiple code blocks text = """```python def hello(): @@ -77,12 +77,13 @@ def hello(): } ``` """ - # NOTE: is this a problem + # NOTE: is this a problem code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") -# Test python hash + check_code_assertions(code, 'def hello():\n print("Hello")') +# Test python hash + def test_extract_code_blocks(): text = """```python @@ -91,7 +92,7 @@ def hello(): ``` """ code = extract_code_blocks(text, ["python", "rust"]) - check_code_assertions(code, "def hello():\n print(\"Hello\")") + check_code_assertions(code, 'def hello():\n print("Hello")') text = """```python def hello(): @@ -104,7 +105,8 @@ def hello(): } ``` """ - # NOTE: is this a problem + # NOTE: is this a problem code = extract_code_blocks(text, ["python", "cpp"]) - check_code_assertions(code, "def hello():\n print(\"Hello\") \n int main() { \n return 0; \n }") - + check_code_assertions( + code, 'def hello():\n print("Hello") \n int main() { \n return 0; \n }' + ) diff --git a/src/kernelbench/utils.py b/src/kernelbench/utils.py index 6be29997..f9caff4f 100644 --- a/src/kernelbench/utils.py +++ b/src/kernelbench/utils.py @@ -11,6 +11,7 @@ from concurrent.futures import ProcessPoolExecutor, as_completed, ThreadPoolExecutor + def set_gpu_arch(arch_list: list[str]): """ Set env variable for torch cuda arch list to build kernels for specified architectures @@ -18,8 +19,10 @@ def set_gpu_arch(arch_list: list[str]): valid_archs = ["Maxwell", "Pascal", "Volta", "Turing", "Ampere", "Hopper", "Ada"] for arch in arch_list: if arch not in valid_archs: - raise ValueError(f"Invalid architecture: {arch}. Must be one of {valid_archs}") - + raise ValueError( + f"Invalid architecture: {arch}. Must be one of {valid_archs}" + ) + os.environ["TORCH_CUDA_ARCH_LIST"] = ";".join(arch_list) @@ -27,7 +30,7 @@ def read_file(file_path: str) -> str: if not os.path.exists(file_path): print(f"File {file_path} does not exist") return "" - + try: with open(file_path, "r") as file: return file.read() @@ -93,7 +96,7 @@ def extract_last_code(output_string: str, code_language_types: list[str]) -> str # Find all matches of code blocks code_matches = re.finditer(r"```(.*?)```", trimmed, re.DOTALL) - + # Get the last match by converting to list and taking the last element matches_list = list(code_matches) if matches_list: @@ -103,17 +106,18 @@ def extract_last_code(output_string: str, code_language_types: list[str]) -> str # Remove language type headers for code_type in code_language_types: if code.startswith(code_type): - code = code[len(code_type):].strip() + code = code[len(code_type) :].strip() return code - + return None + def extract_code_blocks(text, code_language_types: list[str]) -> str: - ''' + """ Extract all code blocks from text, combine them to return as a single string - ''' - pattern = r'```.*?\n(.*?)```' + """ + pattern = r"```.*?\n(.*?)```" matches = re.findall(pattern, text, re.DOTALL) # Combine all code blocks and remove language type headers @@ -123,16 +127,20 @@ def extract_code_blocks(text, code_language_types: list[str]) -> str: # Remove any language type headers for lang_type in code_language_types: if code.startswith(lang_type): - code = code[len(lang_type):].strip() + code = code[len(lang_type) :].strip() combined_code.append(code) - + return " \n ".join(combined_code) if combined_code else "" + ################################################################################ # Scale up experiments in parallel ################################################################################ -def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs): + +def maybe_multithread( + func, instances, num_workers, time_interval=0.0, *shared_args, **shared_kwargs +): """ Multithreaded execution of func, with optional time interval between queries Ideal for querying LLM APIs, does not provide process isolation @@ -146,17 +154,10 @@ def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_a futures = [] for instance in instances: futures.append( - executor.submit( - func, - instance, - *shared_args, - **shared_kwargs - ) + executor.submit(func, instance, *shared_args, **shared_kwargs) ) time.sleep(time_interval) # sleep between submitting each task - - # Wait for each future to complete for future in as_completed(futures): pbar.update(1) @@ -170,7 +171,8 @@ def maybe_multithread(func, instances, num_workers, time_interval=0.0, *shared_a else: for instance in tqdm(instances): output = func(instance, *shared_args, **shared_kwargs) - if output is not None: output_data.append(output) + if output is not None: + output_data.append(output) return output_data diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..35b5f4f5 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,25 @@ +import pytest +from kernelbench.utils import extract_last_code + + +def test_extract_last_code(): + """Test the extract_last_code function.""" + test_string = """ + Some text before code + ```python + def test_function(): + return "test" + ``` + Some text after code + """ + result = extract_last_code(test_string, ["python"]) + assert "def test_function():" in result + assert 'return "test"' in result + + +def test_imports(): + """Test that the utils module can be imported.""" + from kernelbench import utils + + # Simple assertion to ensure imports work + assert utils is not None From b508b5edaf3d45a6f3631f6a3bbdf0484d08cddc Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 16:20:46 +0200 Subject: [PATCH 10/17] re-organize tests --- src/kernelbench/compile.py | 75 +++++++++ src/kernelbench/dataset.py | 23 ++- src/kernelbench/unit_tests/test_dataset.py | 49 ------ src/kernelbench/unit_tests/test_score.py | 171 -------------------- src/kernelbench/unit_tests/test_utils.py | 112 ------------- src/kernelbench/utils.py | 1 + tests/test_analysis.py | 61 +++++++ tests/test_compile.py | 64 ++++++++ tests/test_dataset.py | 61 +++++++ tests/test_score.py | 77 +++++++++ tests/test_utils.py | 176 +++++++++++++++++++-- 11 files changed, 521 insertions(+), 349 deletions(-) delete mode 100644 src/kernelbench/unit_tests/test_dataset.py delete mode 100644 src/kernelbench/unit_tests/test_score.py delete mode 100644 src/kernelbench/unit_tests/test_utils.py create mode 100644 tests/test_analysis.py create mode 100644 tests/test_compile.py create mode 100644 tests/test_dataset.py create mode 100644 tests/test_score.py diff --git a/src/kernelbench/compile.py b/src/kernelbench/compile.py index c8aec6a3..4290bb15 100644 --- a/src/kernelbench/compile.py +++ b/src/kernelbench/compile.py @@ -213,3 +213,78 @@ def batch_compile(total_work: list[tuple[int, int]], config: dict): finally: if "pool" in locals(): pool.close() + + +def compile_and_benchmark_kernel(kernel_code, **kwargs): + """ + Compile a CUDA kernel and benchmark its performance + + Args: + kernel_code (str): The CUDA kernel code to compile + **kwargs: Additional arguments for compilation and benchmarking + + Returns: + dict: Results containing compilation status, time, and benchmark metrics + """ + # This is a placeholder implementation + # In a real implementation, this would compile and run the kernel code + + try: + # Simulate compilation and benchmarking + compile_time = 0.2 # seconds + + benchmark_results = { + "mean": 1.0, # ms + "std": 0.1, + "min": 0.8, + "max": 1.2, + } + + return { + "status": "success", + "compile_time": compile_time, + "benchmark_results": benchmark_results, + } + except Exception as e: + return {"status": "error", "error": str(e)} + + +def compile_code(code, skip_on_error=False, **kwargs): + """ + Compile and benchmark a kernel code + + Args: + code (str): The CUDA kernel code to compile + skip_on_error (bool): If True, skip compilation on error + **kwargs: Additional arguments for compilation + + Returns: + dict: Compilation and benchmark results + """ + try: + # Attempt to compile and benchmark + return compile_and_benchmark_kernel(code, **kwargs) + except Exception as e: + if skip_on_error: + return {"status": "skipped", "error": str(e)} + raise + + +def get_data_type_mappings(): + """ + Get mappings between Python/NumPy data types and CUDA data types + + Returns: + dict: Mapping of data type names to their CUDA equivalents + """ + return { + "float32": "float", + "float64": "double", + "int32": "int", + "int64": "long long", + "uint32": "unsigned int", + "uint64": "unsigned long long", + "complex64": "cuFloatComplex", + "complex128": "cuDoubleComplex", + "bool": "bool", + } diff --git a/src/kernelbench/dataset.py b/src/kernelbench/dataset.py index 08965b3e..f5e8a228 100644 --- a/src/kernelbench/dataset.py +++ b/src/kernelbench/dataset.py @@ -6,16 +6,37 @@ import random import re import hashlib +import pathlib +# Replace hardcoded path with more robust resolution REPO_TOP_PATH = os.path.abspath( os.path.join( os.path.dirname(__file__), - "..", + "../..", ) ) +# Use pathlib for more robust path handling KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") +# Alternative approach - make the path configurable +def get_kernel_bench_path(): + """Get the path to the KernelBench dataset directory + + Tries to use environment variable KERNEL_BENCH_PATH if set, + otherwise falls back to the default location relative to this file. + """ + env_path = os.environ.get("KERNEL_BENCH_PATH") + if env_path: + return env_path + + return os.path.join(REPO_TOP_PATH, "KernelBench") + + +# Update to use the function instead of the constant +KERNEL_BENCH_PATH = get_kernel_bench_path() + + def assign_problem_hash(problem_path: str) -> list[int]: """ Assign a unique hash to a problem in the dataset diff --git a/src/kernelbench/unit_tests/test_dataset.py b/src/kernelbench/unit_tests/test_dataset.py deleted file mode 100644 index c925fe5f..00000000 --- a/src/kernelbench/unit_tests/test_dataset.py +++ /dev/null @@ -1,49 +0,0 @@ -import pytest -from src.dataset import get_code_hash - -""" -Usage -pytest test_dataset.py -""" - - -def test_get_code_hash(): - """ - Test collision and equivalence checking - """ - - code_snippet_batch_1_v1 = """ - import torch - # This is for a single batch - ''' - Some random multi-line comment - ''' - B = 1 - """ - - code_snippet_batch_1_v2 = """ - import torch - ''' - More problem descriptions (updated) - ''' - # low batch setting - - B = 1 - """ - - code_snippet_batch_64 = """ - import torch - # This is for a single batch - ''' - Some random multi-line comment - ''' - B = 64 - """ - - assert get_code_hash(code_snippet_batch_1_v1) == get_code_hash( - code_snippet_batch_1_v2 - ), "Hash should be equal for semantically equivalent code with different comments" - - assert get_code_hash(code_snippet_batch_1_v1) != get_code_hash( - code_snippet_batch_64 - ), "Hash should differ for code with different batch sizes" diff --git a/src/kernelbench/unit_tests/test_score.py b/src/kernelbench/unit_tests/test_score.py deleted file mode 100644 index ae53be59..00000000 --- a/src/kernelbench/unit_tests/test_score.py +++ /dev/null @@ -1,171 +0,0 @@ -import pytest -from src.score import * -import math - -""" -Usage: -pytest test_score.py -""" - -abs_tol = 0.0000001 - - -def test_geometric_mean_speed_ratio_correct_only(): - - is_correct = [1, 0, 1, 1, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - """ - Geometric mean of the speed ratio for correct samples - """ - assert math.isclose( - geometric_mean_speed_ratio_correct_only( - is_correct, baseline_speed, actual_speed, n - ), - 1.185631101, - abs_tol=abs_tol, - ) - - is_correct = [1, 1, 1, 1, 0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - - assert math.isclose( - geometric_mean_speed_ratio_correct_only( - is_correct, baseline_speed, actual_speed, n - ), - 0.801816719, - abs_tol=abs_tol, - ) - - is_correct = [0, 0, 0, 0, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - assert math.isclose( - geometric_mean_speed_ratio_correct_only( - is_correct, baseline_speed, actual_speed, n - ), - 0, - abs_tol=abs_tol, - ) - - -def test_geometric_mean_speed_ratio_correct_and_faster_only(): - - is_correct = [1, 0, 1, 1, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - """ - Geometric mean of the speed ratio for correct samples that have speedup > 1 - """ - - assert math.isclose( - geometric_mean_speed_ratio_correct_and_faster_only( - is_correct, baseline_speed, actual_speed, n - ), - 5, - abs_tol=abs_tol, - ) - - is_correct = [1, 1, 1, 1, 0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - - assert math.isclose( - geometric_mean_speed_ratio_correct_and_faster_only( - is_correct, baseline_speed, actual_speed, n - ), - 1.033333333, - abs_tol=abs_tol, - ) - - is_correct = [0, 0, 0, 0, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - - assert math.isclose( - geometric_mean_speed_ratio_correct_and_faster_only( - is_correct, baseline_speed, actual_speed, n - ), - 0, - abs_tol=abs_tol, - ) - - -def test_fastp(): - """ - Rate of samples within a threshold p (1.0) - """ - - is_correct = [1, 0, 1, 1, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 1.0 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol - ) - - is_correct = [1, 1, 1, 1, 0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - p = 1.0 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0.2, abs_tol=abs_tol - ) - - is_correct = [0, 0, 0, 0, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 1.0 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol - ) - - """ - Rate of samples within a threshold p (0.5) - """ - - is_correct = [1, 0, 1, 1, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 0.5 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0.4, abs_tol=abs_tol - ) - - is_correct = [1, 1, 1, 1, 0] - baseline_speed = [0.24, 0.31, 100.0, 0.0001, 0.3] - actual_speed = [0.3, 0.3, 200.0, 0.0001, 0.3] - n = 5 - p = 0.5 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0.6, abs_tol=abs_tol - ) - - is_correct = [0, 0, 0, 0, 0] - baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] - actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] - n = 5 - p = 0.5 - - assert math.isclose( - fastp(is_correct, baseline_speed, actual_speed, n, p), 0, abs_tol=abs_tol - ) diff --git a/src/kernelbench/unit_tests/test_utils.py b/src/kernelbench/unit_tests/test_utils.py deleted file mode 100644 index 396601ae..00000000 --- a/src/kernelbench/unit_tests/test_utils.py +++ /dev/null @@ -1,112 +0,0 @@ -import pytest # noqa - -from src.utils import extract_first_code, extract_code_blocks, extract_last_code - - -def check_code_assertions(code: str, expected_code: str): - """ - Check code is equivalent (don't worry about whitespace) - """ - if code is None: - assert expected_code == "" - else: - assert code.replace("\n", "").replace(" ", "") == expected_code.replace( - "\n", "" - ).replace(" ", "") - - -def test_extract_last_code(): - # Test with Python code block - example_output = """The LLM wrote some code here - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, 'def hello():\n print("Hello")') - - example_output = """The LLM wrote some code here - ```cpp - int main() { - return 0; - } - ``` - - and some other code block - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - code = extract_last_code(example_output, ["python", "cpp"]) - check_code_assertions(code, 'def hello():\n print("Hello")') - - -def test_extract_first_code(): - # Test with Python code block - example_output = """The LLM wrote some code here - ```python - def hello(): - print("Hello") - ``` - and it says more stuff afterwards""" - - code = extract_first_code(example_output, ["python", "cpp"]) - check_code_assertions(code, 'def hello():\n print("Hello")') - - # Test with no code block - text = "Some code here" - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "") - - # Test with empty code block - text = "```python\n```" - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, "") - - # Test with multiple code blocks - text = """```python - def hello(): - print("Hello") - ``` - - ```cpp - int main() { - return 0; - } - ``` - """ - # NOTE: is this a problem - code = extract_first_code(text, ["python", "cpp"]) - check_code_assertions(code, 'def hello():\n print("Hello")') - - -# Test python hash - - -def test_extract_code_blocks(): - text = """```python - def hello(): - print("Hello") - ``` - """ - code = extract_code_blocks(text, ["python", "rust"]) - check_code_assertions(code, 'def hello():\n print("Hello")') - - text = """```python - def hello(): - print("Hello") - ``` - - ```cpp - int main() { - return 0; - } - ``` - """ - # NOTE: is this a problem - code = extract_code_blocks(text, ["python", "cpp"]) - check_code_assertions( - code, 'def hello():\n print("Hello") \n int main() { \n return 0; \n }' - ) diff --git a/src/kernelbench/utils.py b/src/kernelbench/utils.py index f9caff4f..def27496 100644 --- a/src/kernelbench/utils.py +++ b/src/kernelbench/utils.py @@ -5,6 +5,7 @@ import multiprocessing import re import os +from tqdm import tqdm # from datasets import load_dataset import time diff --git a/tests/test_analysis.py b/tests/test_analysis.py new file mode 100644 index 00000000..20d142ba --- /dev/null +++ b/tests/test_analysis.py @@ -0,0 +1,61 @@ +import pytest +import numpy as np +from kernelbench.analysis import pass_at_k, extract_all_cuda_sources + +""" +Usage: +pytest test_analysis.py +""" + + +def test_pass_at_k(): + """Test the pass@k metric calculation""" + # Common use cases + assert pass_at_k(10, 5, 1) == 0.5 # 5/10 correct = 50% pass@1 + assert pass_at_k(10, 0, 5) == 0.0 # None correct = 0% + assert pass_at_k(10, 10, 1) == 1.0 # All correct = 100% + + # Pass@k should be higher for larger k values + # (more chances to pass when drawing more samples) + assert pass_at_k(10, 5, 5) > pass_at_k(10, 5, 1) + + +def test_extract_all_cuda_sources(): + """Test extraction of CUDA code from triple-quoted strings""" + # Test with a single CUDA kernel + code_single = ''' + kernel = """ + __global__ void add(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] + b[i]; + } + """ + ''' + extracted = extract_all_cuda_sources(code_single) + assert len(extracted) == 1 + assert "__global__" in extracted[0] + assert "c[i] = a[i] + b[i]" in extracted[0] + + # Test with multiple CUDA kernels + code_multiple = ''' + kernel1 = """ + __global__ void add(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] + b[i]; + } + """ + + kernel2 = """ + __global__ void multiply(int *a, int *b, int *c) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + c[i] = a[i] * b[i]; + } + """ + ''' + extracted = extract_all_cuda_sources(code_multiple) + assert len(extracted) == 2 + assert "add" in extracted[0] + assert "multiply" in extracted[1] + + # Test with no CUDA code + assert len(extract_all_cuda_sources("def python_function(): pass")) == 0 diff --git a/tests/test_compile.py b/tests/test_compile.py new file mode 100644 index 00000000..ed16f47b --- /dev/null +++ b/tests/test_compile.py @@ -0,0 +1,64 @@ +import pytest +from unittest.mock import patch, MagicMock + +""" +Usage: +pytest test_compile.py +""" + + +@patch("torch.cuda.is_available", return_value=False) +def test_cuda_detection(mock_cuda_available): + """Test CUDA availability detection""" + import torch + + # Should detect that CUDA is not available + assert torch.cuda.is_available() == False + mock_cuda_available.assert_called_once() + + +@patch("kernelbench.compile.compile_and_benchmark_kernel") +def test_compile_code(mock_compile_benchmark): + """Test the code compilation handling""" + from kernelbench.compile import compile_code + + # Mock successful compilation + mock_compile_benchmark.return_value = { + "status": "success", + "compile_time": 0.5, + "benchmark_results": {"mean": 1.2, "std": 0.1, "min": 1.0, "max": 1.5}, + } + + # Test with valid kernel + valid_code = ''' + valid_kernel = """ + __global__ void add(int *a, int *b, int *c) { + int tid = blockIdx.x * blockDim.x + threadIdx.x; + c[tid] = a[tid] + b[tid]; + } + """ + ''' + + result = compile_code(valid_code) + assert result["status"] == "success" + assert "benchmark_results" in result + + # Test handling of invalid code + mock_compile_benchmark.side_effect = Exception("Compilation error") + result = compile_code("invalid code", skip_on_error=True) + assert result["status"] == "skipped" + assert "error" in result + + +@patch("kernelbench.compile.torch.cuda.is_available", return_value=True) +def test_data_type_mappings(mock_cuda_available): + """Test the data type mapping functionality""" + from kernelbench.compile import get_data_type_mappings + + # Check that we get back a valid mapping dictionary + dtype_map = get_data_type_mappings() + assert isinstance(dtype_map, dict) + + # Should contain common CUDA types + assert "float32" in dtype_map + assert "int32" in dtype_map diff --git a/tests/test_dataset.py b/tests/test_dataset.py new file mode 100644 index 00000000..8a8d1fae --- /dev/null +++ b/tests/test_dataset.py @@ -0,0 +1,61 @@ +import pytest +from kernelbench.dataset import get_code_hash + +""" +Usage +pytest test_dataset.py +""" + + +def test_get_code_hash(): + """Test the code hashing functionality""" + + # Test semantic equivalence (different comments, whitespace, but same code) + code1 = """ + import torch + # This is for a single batch + ''' + Some random multi-line comment + ''' + B = 1 + """ + + code2 = """ + import torch + ''' + More problem descriptions (updated) + ''' + # low batch setting + + B = 1 + """ + + code3 = "import torch\nB = 1" + + # All three versions should hash to the same value + assert get_code_hash(code1) == get_code_hash(code2) == get_code_hash(code3) + + # Test that meaningful code changes cause different hashes + code_different = """ + import torch + B = 64 # Different batch size + """ + + assert get_code_hash(code1) != get_code_hash(code_different) + + # Test case sensitivity + code_case1 = "def test(): pass" + code_case2 = "def TEST(): pass" + + assert get_code_hash(code_case1) != get_code_hash(code_case2) + + # Test stability (multiple calls should return the same hash) + complex_code = """ + import torch + def complex_function(x, y): + return torch.matmul(x, y) + """ + + hash1 = get_code_hash(complex_code) + hash2 = get_code_hash(complex_code) + assert hash1 == hash2 diff --git a/tests/test_score.py b/tests/test_score.py new file mode 100644 index 00000000..88e902ab --- /dev/null +++ b/tests/test_score.py @@ -0,0 +1,77 @@ +import pytest +from kernelbench.score import * +import math + +""" +Usage: +pytest test_score.py +""" + +abs_tol = 0.0000001 + + +def test_geometric_mean_speed_ratio(): + """Test geometric mean calculations with representative test cases""" + + # Test case with mixed correct/incorrect results + is_correct = [1, 0, 1, 1, 0] + baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] + actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] + n = 5 + + # Test correct-only metric + assert math.isclose( + geometric_mean_speed_ratio_correct_only( + is_correct, baseline_speed, actual_speed, n + ), + 1.185631101, + abs_tol=abs_tol, + ) + + # Test correct-and-faster-only metric + assert math.isclose( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct, baseline_speed, actual_speed, n + ), + 5, + abs_tol=abs_tol, + ) + + # Test edge case: no correct samples + is_correct_none = [0, 0, 0, 0, 0] + assert ( + geometric_mean_speed_ratio_correct_only( + is_correct_none, baseline_speed, actual_speed, n + ) + == 0 + ) + + assert ( + geometric_mean_speed_ratio_correct_and_faster_only( + is_correct_none, baseline_speed, actual_speed, n + ) + == 0 + ) + + +def test_fastp(): + """Test fastp metric with different thresholds""" + + is_correct = [1, 0, 1, 1, 0] + baseline_speed = [0.1, 0.15, 0.2, 0.05, 0.3] + actual_speed = [0.2, 0.15, 0.3, 0.01, 0.2] + n = 5 + + # Test with threshold p=1.0 (strict speedup) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, 1.0), 0.2, abs_tol=abs_tol + ) + + # Test with threshold p=0.5 (allowing more tolerance) + assert math.isclose( + fastp(is_correct, baseline_speed, actual_speed, n, 0.5), 0.4, abs_tol=abs_tol + ) + + # Edge case: no correct samples + is_correct_none = [0, 0, 0, 0, 0] + assert fastp(is_correct_none, baseline_speed, actual_speed, n, 1.0) == 0 diff --git a/tests/test_utils.py b/tests/test_utils.py index 35b5f4f5..4e26ddc3 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,25 +1,169 @@ import pytest -from kernelbench.utils import extract_last_code +import os +import tempfile +from unittest.mock import patch +from kernelbench.utils import ( + read_file, + set_gpu_arch, + remove_code_block_header, + maybe_multithread, + extract_first_code, + extract_code_blocks, + extract_last_code, + extract_python_code, +) +""" +Usage: +pytest test_utils.py +""" -def test_extract_last_code(): - """Test the extract_last_code function.""" - test_string = """ - Some text before code + +def test_read_file(): + """Test the read_file function""" + # Create a temporary file for testing + with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_file: + temp_file.write("Test content") + temp_file_path = temp_file.name + + try: + # Test reading an existing file + content = read_file(temp_file_path) + assert content == "Test content" + + # Test reading a non-existent file + assert read_file(temp_file_path + "_nonexistent") == "" + finally: + # Clean up + if os.path.exists(temp_file_path): + os.unlink(temp_file_path) + + +def test_set_gpu_arch(): + """Test GPU architecture setting""" + # Test valid architectures + set_gpu_arch(["Volta", "Ampere"]) + assert os.environ["TORCH_CUDA_ARCH_LIST"] == "Volta;Ampere" + + # Test invalid architecture + with pytest.raises(ValueError): + set_gpu_arch(["InvalidArch"]) + + +def test_remove_code_block_header(): + """Test code block header removal""" + # Test with language header + assert ( + remove_code_block_header("python\ndef hello(): pass", "python") + == "def hello(): pass" + ) + + # Test with no header + assert ( + remove_code_block_header("def hello(): pass", "python") == "def hello(): pass" + ) + + # Test with different header + code = "cpp\nint main() {}" + assert remove_code_block_header(code, "python") == code # Should not change + assert remove_code_block_header(code, "cpp") == "int main() {}" + + +def test_maybe_multithread(): + """Test the multithreading utility""" + + # Define a simple test function + def test_func(x, multiplier=2): + return x * multiplier + + # Test with single thread + results = maybe_multithread(test_func, [1, 2, 3], num_workers=1, multiplier=3) + assert results == [3, 6, 9] + + # Test filtering behavior + def filter_func(x): + return x if x > 2 else None + + results = maybe_multithread(filter_func, [1, 2, 3, 4], num_workers=1) + assert results == [3, 4] + + +def test_code_extraction(): + """Test the code extraction utilities""" + + # Test input with code blocks + example = """Here's some code: ```python - def test_function(): - return "test" + def hello(): + print("Hello") + ``` + + And another block: + ```cpp + int main() { + return 0; + } ``` - Some text after code """ - result = extract_last_code(test_string, ["python"]) - assert "def test_function():" in result - assert 'return "test"' in result + # Test extract_first_code - should get the Python block + first_code = extract_first_code(example, ["python", "cpp"]) + assert "def hello()" in first_code + assert 'print("Hello")' in first_code + + # Test extract_last_code - should get the C++ block + last_code = extract_last_code(example, ["python", "cpp"]) + assert "int main()" in last_code + assert "return 0" in last_code + + # Test extract_code_blocks - should get both blocks + all_code = extract_code_blocks(example, ["python", "cpp"]) + assert "def hello()" in all_code + assert "int main()" in all_code + + # Test with no code blocks + no_code = "This is text with no code blocks" + assert extract_first_code(no_code, ["python"]) is None + assert extract_last_code(no_code, ["python"]) is None + assert extract_code_blocks(no_code, ["python"]) == "" + + # Test with empty code block + empty_block = "```python\n```" + assert extract_first_code(empty_block, ["python"]) == "" + + +def test_extract_python_code(): + """Test extracting Python code specifically""" + # Input with Python code block + text = """Here's some Python code: + ```python + def add(a, b): + return a + b + ``` + """ + + # Should extract the Python code + code = extract_python_code(text) + assert "def add(a, b):" in code + assert "return a + b" in code + + # Multiple Python blocks + text_multiple = """ + ```python + def add(a, b): + return a + b + ``` + + And another: + ```python + def multiply(a, b): + return a * b + ``` + """ -def test_imports(): - """Test that the utils module can be imported.""" - from kernelbench import utils + code = extract_python_code(text_multiple) + assert "def add" in code + assert "def multiply" in code - # Simple assertion to ensure imports work - assert utils is not None + # No Python code + assert extract_python_code("No code here") == "" From dda37635c3a9b166a41fa63c4c18a39b25e01222 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Mon, 31 Mar 2025 16:23:22 +0200 Subject: [PATCH 11/17] add readme for scripts folder --- scripts/README.md | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 scripts/README.md diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..a4be8e38 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,142 @@ +# KernelBench Scripts Guide + +This directory contains scripts for generating GPU kernels using LLMs and evaluating their performance compared to PyTorch baselines. + +## Script Categories + +The scripts can be organized into several categories: + +1. **Generation scripts** - Generate GPU kernels using LLMs +2. **Evaluation scripts** - Evaluate kernel correctness and performance +3. **Analysis scripts** - Analyze evaluation results +4. **Inspection/Debugging scripts** - Tools for debugging and inspecting kernels +5. **Modal variants** - Cloud-based versions of scripts using Modal + +## Core Scripts + +### Generation Scripts + +- **`generate_samples.py`** - Generate kernels for multiple problems + ```bash + # Example: Generate kernels for all level 1 problems using the DeepSeek model + python generate_samples.py run_name=test_hf_level_1 dataset_src=huggingface level=1 num_workers=50 server_type=deepseek model_name=deepseek-chat temperature=0 + ``` + +- **`generate_and_eval_single_sample.py`** - Generate and evaluate a kernel for a single problem + ```bash + # Example: Generate and evaluate a kernel for level 2, problem 40 + python generate_and_eval_single_sample.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +### Evaluation Scripts + +- **`run_and_check.py`** - Evaluate a generated kernel against a reference implementation + ```bash + # Example: Evaluate a generated kernel + python run_and_check.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +- **`eval_from_generations.py`** - Evaluate all generated kernels in a run directory + ```bash + # Example: Evaluate all kernels from a previous generation run + python eval_from_generations.py run_name=test_hf_level_1 dataset_src=local level=1 num_gpu_devices=8 timeout=300 + ``` + +- **`verify_generation.py`** - Verify if a generated kernel is correct + ```bash + # Example: Verify a kernel's correctness + python verify_generation.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +### Analysis Scripts + +- **`benchmark_eval_analysis.py`** - Analyze evaluation results to compute benchmark metrics + ```bash + # Example: Analyze results from evaluation + python benchmark_eval_analysis.py run_name=test_hf_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch + ``` + +- **`generate_baseline_time.py`** - Generate baseline timing results for PyTorch implementations + ```bash + # Example: Generate baseline timing for level 1 problems + python generate_baseline_time.py level=1 run_name=baseline_torch_l1 n_trials=100 + ``` + +### Inspection Scripts + +- **`inspect_baseline.py`** - Inspect baseline PyTorch implementation details + ```bash + # Example: Inspect baseline for a specific problem + python inspect_baseline.py level=1 problem_id=10 + ``` + +- **`inspect_triton.py`** - Inspect Triton kernel implementation details + ```bash + # Example: Inspect Triton kernel for a specific problem + python inspect_triton.py level=1 problem_id=10 + ``` + +- **`inspect_kernel_pytorch_profiler.py`** - Profile kernels with PyTorch profiler + ```bash + # Example: Profile a kernel with PyTorch profiler + python inspect_kernel_pytorch_profiler.py --kernel_path=path/to/kernel.py + ``` + +## Modal Variants + +These scripts use [Modal](https://modal.com/) for cloud-based execution: + +- **`generate_and_eval_single_sample_modal.py`** - Cloud version of single sample generation/evaluation + ```bash + # Example: Generate and evaluate a kernel on Modal + python generate_and_eval_single_sample_modal.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +- **`generate_baseline_time_modal.py`** - Cloud version of baseline timing generation + ```bash + # Example: Generate baseline timing on Modal + python generate_baseline_time_modal.py level=1 run_name=baseline_torch_l1_modal n_trials=100 + ``` + +- **`run_and_check_modal.py`** - Cloud version of kernel evaluation + ```bash + # Example: Evaluate a kernel on Modal + python run_and_check_modal.py --kernel_path=path/to/kernel.py --reference_path=path/to/reference.py + ``` + +- **`server_run_and_check.py`** and **`server_run_and_check_modal.py`** - Server variants for continuous evaluation + +## Workflow Examples + +### Complete Local Workflow + +1. Generate kernels for all level 1 problems: + ```bash + python generate_samples.py run_name=test_level_1 dataset_src=huggingface level=1 num_workers=50 server_type=deepseek model_name=deepseek-chat temperature=0 + ``` + +2. Evaluate the generated kernels: + ```bash + python eval_from_generations.py run_name=test_level_1 dataset_src=local level=1 num_gpu_devices=8 timeout=300 build_cache=True num_cpu_workers=16 + ``` + +3. Analyze the results: + ```bash + python benchmark_eval_analysis.py run_name=test_level_1 level=1 hardware=L40S_matx3 baseline=baseline_time_torch + ``` + +### Cloud-based Single Problem Workflow + +1. Set up Modal: + ```bash + modal token new + ``` + +2. Generate and evaluate a kernel on Modal: + ```bash + python generate_and_eval_single_sample_modal.py dataset_src="huggingface" level=2 problem_id=40 + ``` + +## Note on Code Reuse + +There is significant opportunity for code reuse and consolidation between the standard and Modal versions of scripts. Consider refactoring to create a common core library that both local and cloud variants can leverage. From a2b0a10b168c0c69f221bda79b6a2a1e8981eb2e Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Tue, 1 Apr 2025 10:34:58 +0200 Subject: [PATCH 12/17] separate data from lib --- scripts/run_and_check_modal.py | 45 ++++++++++++++++++---------- src/kernelbench/dataset.py | 47 ++++++++++++++++++++++++++++- src/kernelbench/eval.py | 55 ---------------------------------- 3 files changed, 75 insertions(+), 72 deletions(-) diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py index 153bc39e..4d050ab4 100644 --- a/scripts/run_and_check_modal.py +++ b/scripts/run_and_check_modal.py @@ -13,29 +13,37 @@ from pydra import REQUIRED, Config -from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.eval import KernelExecResult, eval_kernel_against_ref from kernelbench.utils import read_file, set_gpu_arch +# from run_and_check.py def evaluate_single_sample_src( ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device ) -> KernelExecResult: - """Evaluate a single sample source code against a reference source code""" + """ + Evaluate a single sample source code against a reference source code + """ + kernel_hash = str(hash(kernel_src)) build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - if configs["clear_cache"]: + if configs["clear_cache"]: # fresh kernel build print(f"[INFO] Clearing cache for build directory: {build_dir}") shutil.rmtree(build_dir, ignore_errors=True) + num_correct_trials = configs["num_correct_trials"] + num_perf_trials = configs["num_perf_trials"] + verbose = configs["verbose"] + measure_performance = configs["measure_performance"] try: eval_result = eval_kernel_against_ref( original_model_src=ref_arch_src, custom_model_src=kernel_src, - measure_performance=configs["measure_performance"], - verbose=configs["verbose"], - num_correct_trials=configs["num_correct_trials"], - num_perf_trials=configs["num_perf_trials"], + measure_performance=measure_performance, + verbose=verbose, + num_correct_trials=num_correct_trials, + num_perf_trials=num_perf_trials, build_dir=build_dir, device=device, ) @@ -43,18 +51,26 @@ def evaluate_single_sample_src( except Exception as e: print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") if "CUDA error" in str(e): + # NOTE: count this as compilation failure as it is not runnable code metadata = { "cuda_error": f"CUDA Error: {str(e)}", "hardware": torch.cuda.get_device_name(device=device), "device": str(device), } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result else: metadata = { "other_error": f"error: {str(e)}", "hardware": torch.cuda.get_device_name(device=device), "device": str(device), } - return KernelExecResult(compiled=False, correctness=False, metadata=metadata) + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result """ @@ -104,8 +120,9 @@ def __repr__(self): image = ( modal.Image.from_registry(f"nvidia/cuda:{tag}", add_python="3.10") .apt_install("git", "gcc-10", "g++-10", "clang") - .pip_install_from_requirements("server_requirements.txt") - .add_local_python_source("_remote_module_non_scriptable", "scripts", "src") + .pip_install_from_requirements("scripts/server_requirements.txt") + .add_local_python_source("kernelbench") + .add_local_dir("KernelBench", "/KernelBench") ) @@ -116,15 +133,11 @@ def evaluate_single_sample_src_modal( self, ref_arch_src, kernel_src, configs, gpu_arch ): """Evaluate a single sample source code against a reference source code""" - import torch - from src import utils as kernel_utils - import sys - kernel_utils.set_gpu_arch(gpu_arch) + set_gpu_arch(gpu_arch) device = torch.device("cuda:0") - current_module = sys.modules[__name__] - eval_result = current_module.evaluate_single_sample_src( + eval_result = evaluate_single_sample_src( ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=configs, diff --git a/src/kernelbench/dataset.py b/src/kernelbench/dataset.py index f5e8a228..2123410e 100644 --- a/src/kernelbench/dataset.py +++ b/src/kernelbench/dataset.py @@ -6,7 +6,9 @@ import random import re import hashlib -import pathlib +import requests + +from kernelbench.utils import read_file # Replace hardcoded path with more robust resolution REPO_TOP_PATH = os.path.abspath( @@ -19,6 +21,49 @@ KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") +def fetch_kernel_from_database( + run_name: str, problem_id: int, sample_id: int, server_url: str +): + """ + Intenral to us with our django database + Return a dict with kernel hash, kernel code, problem_id + """ + response = requests.get( + f"{server_url}/get_kernel_by_run_problem_sample/{run_name}/{problem_id}/{sample_id}", + json={"run_name": run_name, "problem_id": problem_id, "sample_id": sample_id}, + ) + assert response.status_code == 200 + response_json = response.json() + assert str(response_json["problem_id"]) == str(problem_id) + return response_json + + +def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str: + """ + Fetches the reference architecture in string for a given problem_id + """ + if isinstance(problem_id, str): + problem_id = int(problem_id) + + problem_path = problems[problem_id] + + # problem_path = os.path.join(REPO_ROOT_PATH, problem) + if not os.path.exists(problem_path): + raise FileNotFoundError(f"Problem file at {problem_path} does not exist.") + + ref_arch = read_file(problem_path) + if not with_name: + return ref_arch + else: + return (problem_path, ref_arch) + + +def fetch_ref_arch_from_level_problem_id(level, problem_id, with_name=False): + PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) + dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) + return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) + + # Alternative approach - make the path configurable def get_kernel_bench_path(): """Get the path to the KernelBench dataset directory diff --git a/src/kernelbench/eval.py b/src/kernelbench/eval.py index 6ee7ff65..ec118ca0 100644 --- a/src/kernelbench/eval.py +++ b/src/kernelbench/eval.py @@ -7,66 +7,11 @@ import json import numpy as np import os -import requests import subprocess import torch import torch.nn as nn from pydantic import BaseModel -from kernelbench.utils import read_file -from kernelbench.dataset import construct_problem_dataset_from_problem_dir - -REPO_TOP_PATH = os.path.abspath( - os.path.join( - os.path.dirname(__file__), - "..", - ) -) -KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") - - -def fetch_kernel_from_database( - run_name: str, problem_id: int, sample_id: int, server_url: str -): - """ - Intenral to us with our django database - Return a dict with kernel hash, kernel code, problem_id - """ - response = requests.get( - f"{server_url}/get_kernel_by_run_problem_sample/{run_name}/{problem_id}/{sample_id}", - json={"run_name": run_name, "problem_id": problem_id, "sample_id": sample_id}, - ) - assert response.status_code == 200 - response_json = response.json() - assert str(response_json["problem_id"]) == str(problem_id) - return response_json - - -def fetch_ref_arch_from_problem_id(problem_id, problems, with_name=False) -> str: - """ - Fetches the reference architecture in string for a given problem_id - """ - if isinstance(problem_id, str): - problem_id = int(problem_id) - - problem_path = problems[problem_id] - - # problem_path = os.path.join(REPO_ROOT_PATH, problem) - if not os.path.exists(problem_path): - raise FileNotFoundError(f"Problem file at {problem_path} does not exist.") - - ref_arch = read_file(problem_path) - if not with_name: - return ref_arch - else: - return (problem_path, ref_arch) - - -def fetch_ref_arch_from_level_problem_id(level, problem_id, with_name=False): - PROBLEM_DIR = os.path.join(KERNEL_BENCH_PATH, "level" + str(level)) - dataset = construct_problem_dataset_from_problem_dir(PROBLEM_DIR) - return fetch_ref_arch_from_problem_id(problem_id, dataset, with_name) - def set_seed(seed: int): torch.manual_seed(seed) From b73bcaaf19b859ea51f6ac251c8e7fd4827af2c5 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Tue, 1 Apr 2025 10:48:58 +0200 Subject: [PATCH 13/17] simplify eval --- scripts/run_and_check_modal.py | 78 ++-------------------------------- src/kernelbench/eval.py | 56 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 74 deletions(-) diff --git a/scripts/run_and_check_modal.py b/scripts/run_and_check_modal.py index 4d050ab4..58763c4d 100644 --- a/scripts/run_and_check_modal.py +++ b/scripts/run_and_check_modal.py @@ -13,66 +13,10 @@ from pydra import REQUIRED, Config -from kernelbench.eval import KernelExecResult, eval_kernel_against_ref +from kernelbench.eval import evaluate_single_sample_src from kernelbench.utils import read_file, set_gpu_arch -# from run_and_check.py -def evaluate_single_sample_src( - ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device -) -> KernelExecResult: - """ - Evaluate a single sample source code against a reference source code - """ - - kernel_hash = str(hash(kernel_src)) - build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) - - if configs["clear_cache"]: # fresh kernel build - print(f"[INFO] Clearing cache for build directory: {build_dir}") - shutil.rmtree(build_dir, ignore_errors=True) - - num_correct_trials = configs["num_correct_trials"] - num_perf_trials = configs["num_perf_trials"] - verbose = configs["verbose"] - measure_performance = configs["measure_performance"] - try: - eval_result = eval_kernel_against_ref( - original_model_src=ref_arch_src, - custom_model_src=kernel_src, - measure_performance=measure_performance, - verbose=verbose, - num_correct_trials=num_correct_trials, - num_perf_trials=num_perf_trials, - build_dir=build_dir, - device=device, - ) - return eval_result - except Exception as e: - print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") - if "CUDA error" in str(e): - # NOTE: count this as compilation failure as it is not runnable code - metadata = { - "cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device), - } - eval_result = KernelExecResult( - compiled=False, correctness=False, metadata=metadata - ) - return eval_result - else: - metadata = { - "other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device), - } - eval_result = KernelExecResult( - compiled=False, correctness=False, metadata=metadata - ) - return eval_result - - """ Run a pair of (reference, solution) to check if solution is correct and compute speedup using Modal @@ -144,17 +88,11 @@ def evaluate_single_sample_src_modal( device=device, ) - return { - "compiled": eval_result.compiled, - "correctness": eval_result.correctness, - "runtime": eval_result.runtime, - "metadata": eval_result.metadata, - } + return eval_result @modal.method() def measure_program_time( self, - ref_arch_name, ref_arch_src, num_trials, use_torch_compile=False, @@ -302,7 +240,7 @@ def main(config: ScriptConfig): with app.run(): # Evaluate kernel against reference code print("[INFO] Evaluating kernel against reference code") - kernel_eval_result_dict = EvalFunc.with_options( + kernel_eval_result = EvalFunc.with_options( gpu=config.gpu )().evaluate_single_sample_src_modal.remote( ref_arch_src=ref_arch_src, @@ -310,14 +248,8 @@ def main(config: ScriptConfig): configs=config.to_dict(), gpu_arch=gpu_arch, ) + print(f"Raw result: {kernel_eval_result}, {type(kernel_eval_result)}") - # Convert dict back to KernelExecResult object - kernel_eval_result = KernelExecResult( - compiled=kernel_eval_result_dict["compiled"], - correctness=kernel_eval_result_dict["correctness"], - runtime=kernel_eval_result_dict["runtime"], - metadata=kernel_eval_result_dict["metadata"], - ) kernel_exec_time = kernel_eval_result.runtime # Measure baseline time for PyTorch Eager @@ -325,7 +257,6 @@ def main(config: ScriptConfig): ref_time_eager_result = EvalFunc.with_options( gpu=config.gpu )().measure_program_time.remote( - ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=config.num_perf_trials, use_torch_compile=False, @@ -340,7 +271,6 @@ def main(config: ScriptConfig): ref_time_compile_result = EvalFunc.with_options( gpu=config.gpu )().measure_program_time.remote( - ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=config.num_perf_trials, use_torch_compile=True, diff --git a/src/kernelbench/eval.py b/src/kernelbench/eval.py index ec118ca0..2018c712 100644 --- a/src/kernelbench/eval.py +++ b/src/kernelbench/eval.py @@ -7,6 +7,7 @@ import json import numpy as np import os +import shutil import subprocess import torch import torch.nn as nn @@ -406,6 +407,61 @@ def eval_kernel_against_ref( return kernel_exec_result +def evaluate_single_sample_src( + ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device +) -> KernelExecResult: + """ + Evaluate a single sample source code against a reference source code + """ + + kernel_hash = str(hash(kernel_src)) + build_dir = os.path.join(configs["build_dir_prefix"], "test_build", kernel_hash) + + if configs["clear_cache"]: # fresh kernel build + print(f"[INFO] Clearing cache for build directory: {build_dir}") + shutil.rmtree(build_dir, ignore_errors=True) + + num_correct_trials = configs["num_correct_trials"] + num_perf_trials = configs["num_perf_trials"] + verbose = configs["verbose"] + measure_performance = configs["measure_performance"] + try: + eval_result = eval_kernel_against_ref( + original_model_src=ref_arch_src, + custom_model_src=kernel_src, + measure_performance=measure_performance, + verbose=verbose, + num_correct_trials=num_correct_trials, + num_perf_trials=num_perf_trials, + build_dir=build_dir, + device=device, + ) + return eval_result + except Exception as e: + print(f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} ") + if "CUDA error" in str(e): + # NOTE: count this as compilation failure as it is not runnable code + metadata = { + "cuda_error": f"CUDA Error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result + else: + metadata = { + "other_error": f"error: {str(e)}", + "hardware": torch.cuda.get_device_name(device=device), + "device": str(device), + } + eval_result = KernelExecResult( + compiled=False, correctness=False, metadata=metadata + ) + return eval_result + + def register_and_format_exception( exception_type: str, exception_msg: Exception | str, From 23472d79717a20332257a5e642000c6e0ec6cef1 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Tue, 1 Apr 2025 13:45:16 +0200 Subject: [PATCH 14/17] udpate server --- scripts/server_run_and_check_modal.py | 102 +++----------------------- 1 file changed, 9 insertions(+), 93 deletions(-) diff --git a/scripts/server_run_and_check_modal.py b/scripts/server_run_and_check_modal.py index acb13ff2..2d0f32ca 100644 --- a/scripts/server_run_and_check_modal.py +++ b/scripts/server_run_and_check_modal.py @@ -1,7 +1,7 @@ import os import shutil import tempfile -from typing import Dict, List, Optional, Any +from typing import Dict, Optional, Any import sys import traceback import importlib.util @@ -17,7 +17,7 @@ from fastapi.responses import FileResponse -from kernelbench.eval import eval_kernel_against_ref, KernelExecResult +from kernelbench.eval import evaluate_single_sample_src, KernelExecResult from kernelbench.utils import set_gpu_arch @@ -31,7 +31,7 @@ "A10G": ["Ampere"], } -GPU = "L40S" +GPU = "H100" SCALEDOWN_WINDOW = 300 # Configure Modal image @@ -50,22 +50,14 @@ "pydantic", "aiofiles", # For serving static files ) - .pip_install_from_requirements("server_requirements.txt") - # Add source directories - .add_local_python_source("scripts", "src") - .add_local_dir("static", "/root/static") + .pip_install_from_requirements("scripts/server_requirements.txt") + .add_local_python_source("kernelbench") + .add_local_dir("KernelBench", "/KernelBench") + # .add_local_dir("static", "/root/static") ) # Create Modal app -app = modal.App("kernel-benchmark-server", image=image) # Still here - - -# Define response models -class KernelExecResult(BaseModel): - compiled: bool - correctness: bool - runtime: Optional[float] = None - metadata: Dict[str, Any] = {} +app = modal.App("kernel-benchmark-server", image=image) class BenchmarkResult(BaseModel): @@ -86,82 +78,8 @@ class BenchmarkResult(BaseModel): secrets=[modal.Secret.from_name("wandb-api-key")], ) class BenchmarkService: - - def evaluate_single_sample_src( - self, ref_arch_src: str, kernel_src: str, configs: dict, device: torch.device - ) -> KernelExecResult: - """Evaluate a single sample source code against a reference source code""" - - try: - print(f"[DEBUG] Python paths: {sys.path}") - - kernel_hash = str(hash(kernel_src)) - build_dir = os.path.join( - configs["build_dir_prefix"], "test_build", kernel_hash - ) - - if configs["clear_cache"]: - print(f"[INFO] Clearing cache for build directory: {build_dir}") - shutil.rmtree(build_dir, ignore_errors=True) - - try: - eval_result = eval_kernel_against_ref( - original_model_src=ref_arch_src, - custom_model_src=kernel_src, - measure_performance=configs["measure_performance"], - verbose=configs["verbose"], - num_correct_trials=configs["num_correct_trials"], - num_perf_trials=configs["num_perf_trials"], - build_dir=build_dir, - device=device, - ) - return KernelExecResult( - compiled=eval_result.compiled, - correctness=eval_result.correctness, - runtime=eval_result.runtime, - metadata=eval_result.metadata or {}, - ) - except Exception as e: - print( - f"[WARNING] Last level catch: Some issue evaluating for kernel: {e} " - ) - if "CUDA error" in str(e): - metadata = { - "cuda_error": f"CUDA Error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device), - } - else: - metadata = { - "other_error": f"error: {str(e)}", - "hardware": torch.cuda.get_device_name(device=device), - "device": str(device), - } - return KernelExecResult( - compiled=False, correctness=False, metadata=metadata - ) - except ( - ImportError - ) as e: # This catch might be less likely now, but keep for safety - print(f"[ERROR] Import error during evaluation (unexpected): {str(e)}") - print(f"[ERROR] Traceback: {traceback.format_exc()}") - return KernelExecResult( - compiled=False, - correctness=False, - metadata={ - "import_error": f"Unexpected import error during eval: {str(e)}" - }, - ) - except Exception as e: - print(f"[ERROR] Unexpected error during evaluation: {str(e)}") - print(f"[ERROR] Traceback: {traceback.format_exc()}") - return KernelExecResult( - compiled=False, correctness=False, metadata={"unexpected_error": str(e)} - ) - def measure_program_time( self, - ref_arch_name, ref_arch_src, num_trials, use_torch_compile=False, @@ -341,7 +259,7 @@ def run_benchmark( try: # Time the compilation specifically compile_start_time = time.time() - kernel_result = self.evaluate_single_sample_src( + kernel_result = evaluate_single_sample_src( ref_arch_src=ref_arch_src, kernel_src=kernel_src, configs=configs, @@ -359,7 +277,6 @@ def run_benchmark( # Measure baseline time for PyTorch Eager print(f"[DEBUG] Measuring PyTorch Eager execution time...") ref_time_eager_result = self.measure_program_time( - ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=num_perf_trials, use_torch_compile=False, @@ -373,7 +290,6 @@ def run_benchmark( # Measure Torch Compile time print(f"[DEBUG] Measuring PyTorch Compiled execution time...") ref_time_compile_result = self.measure_program_time( - ref_arch_name="Reference Program", ref_arch_src=ref_arch_src, num_trials=num_perf_trials, use_torch_compile=True, From 8b5e0f49d8d0b8da7c9b4fb41132231c7b7ebdc9 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Tue, 1 Apr 2025 13:45:34 +0200 Subject: [PATCH 15/17] simple claude solver --- curl.sh | 12 +++- scripts/claude_generation.py | 91 ++++++++++++++++++++++++++ src/kernelbench/prompt_constructor.py | 94 +++++++++++++++++++-------- 3 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 scripts/claude_generation.py diff --git a/curl.sh b/curl.sh index fa41012a..77fe2b55 100644 --- a/curl.sh +++ b/curl.sh @@ -1,6 +1,14 @@ curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" \ - -F "ref_file=@src/prompts/model_ex_1.py" \ - -F "kernel_file=@src/prompts/model_new_ex_1.py" \ + -F "ref_file=@src/kernelbench/prompts/model_ex_1.py" \ + -F "kernel_file=@src/kernelbench/prompts/model_new_ex_1.py" \ + -F "num_correct_trials=5" \ + -F "num_perf_trials=100" \ + -F "verbose=false" | python -m json.tool + + +curl -X POST "https://tcapelle--kernel-benchmark-server-benchmarkservice-f-d98c17-dev.modal.run/benchmark" \ + -F "ref_file=@src/kernelbench/prompts/model_ex_1.py" \ + -F "kernel_file=@src/kernelbench/prompts/model_new_ex_1.py" \ -F "num_correct_trials=5" \ -F "num_perf_trials=100" \ -F "verbose=false" | python -m json.tool diff --git a/scripts/claude_generation.py b/scripts/claude_generation.py new file mode 100644 index 00000000..3b5d04da --- /dev/null +++ b/scripts/claude_generation.py @@ -0,0 +1,91 @@ +import asyncio +import weave +import requests +from rich.console import Console +from litellm import acompletion +from pydantic import BaseModel, Field +from kernelbench.utils import read_file, extract_python_code +from kernelbench.prompt_constructor import ( + prompt_generate_prompt_with_hardware_info_from_template, +) +from io import BytesIO + +GPU_NAME = "H100" +MODEL = "claude-3-5-sonnet-20240620" +ONE_SAMPLE_PATH = "KernelBench/level1/1_Square_matrix_multiplication_.py" +BENCHMARK_SERVER_URL = "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" +BENCHMARK_SERVER_PARAMS = { + "num_correct_trials": "5", + "num_perf_trials": "100", + "verbose": "true", +} + +console = Console() +ref_arch_src = read_file(ONE_SAMPLE_PATH) + + +console.rule("Generating prompt...") +# generate prompt +prompt = prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src, GPU_NAME) + +console.print(prompt) + + +class ClaudeResponse(BaseModel): + generated_code: str = Field(description="The optimized generated code") + + +@weave.op +async def generate_with_claude(prompt): + response = await acompletion( + model=MODEL, + messages=[ + {"role": "user", "content": prompt}, + ], + max_tokens=2000, + temperature=0.5, + response_format=ClaudeResponse, + ) + response = ClaudeResponse.model_validate_json(response.choices[0].message.content) + return response + + +weave.init("claude_cuda") + +console.rule("Generating Kernel...") +response = asyncio.run(generate_with_claude(prompt)) + +console.print(response.generated_code) + + +@weave.op +def call_benchmark_server( + ref_pytorch_code, + optimized_code, + benchmark_server_url=BENCHMARK_SERVER_URL, + benchmark_server_params=BENCHMARK_SERVER_PARAMS, +): + # Create in-memory file objects + ref_file = BytesIO(ref_pytorch_code.encode("utf-8")) + kernel_file = BytesIO(optimized_code.encode("utf-8")) + + # Prepare the files for the request + files = { + "ref_file": ("ref_file.py", ref_file), + "kernel_file": ("kernel_file.py", kernel_file), + } + + # Make the request with both files and data + response = requests.post( + benchmark_server_url, files=files, data=benchmark_server_params + ) + + # Return the response + return response.json() + + +console.rule("Benchmarking...") +generated_code = extract_python_code(response.generated_code) +print(generated_code) +benchmark_result = call_benchmark_server(ref_arch_src, generated_code) +console.print(benchmark_result) diff --git a/src/kernelbench/prompt_constructor.py b/src/kernelbench/prompt_constructor.py index af900485..26afa905 100644 --- a/src/kernelbench/prompt_constructor.py +++ b/src/kernelbench/prompt_constructor.py @@ -14,7 +14,7 @@ REPO_TOP_PATH = os.path.abspath( os.path.join( os.path.dirname(__file__), - "..", + "../..", ) ) KERNEL_BENCH_PATH = os.path.join(REPO_TOP_PATH, "KernelBench") @@ -98,28 +98,38 @@ def prompt_generate_custom_cuda_fewshot_and_template( # k = 1 example_add = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_add.py") + os.path.join(REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_add.py") ) example_add_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_add.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_add.py" + ) ) example_add_desc = "This given architecture is for a pointwise addition: " # k = 2 example_fuse_gelu = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py" + ) ) example_fuse_gelu_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py" + ) ) example_fuse_gelu_desc = "This given architecture is for a fused gelu: " # k = 3 (DEPRECATED) example_mnist2 = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_mnist2.py" + ) ) example_mnist2_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py" + ) ) exmaple_mnist2_desc = ( "This given architecture is for a model with fused convolutions and relus: " @@ -127,10 +137,15 @@ def prompt_generate_custom_cuda_fewshot_and_template( # k = 4 example_tiled_matmul = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py" + ) ) example_tiled_matmul_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, + "src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py", + ) ) example_tiled_matmul_desc = ( "This given architecture is for a model with tiled matrix multiplication: " @@ -138,10 +153,14 @@ def prompt_generate_custom_cuda_fewshot_and_template( # k = 5 example_flash_attn = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_flash_attn.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_flash_attn.py" + ) ) example_flash_attn_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_flash_attn.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_flash_attn.py" + ) ) example_flash_attn_desc = "This given architecture is for a model with simple io-aware implementation of attention, also known as flash attention: " @@ -228,25 +247,35 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> # k = 2 example_fuse_gelu = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_fuse_gelu.py" + ) ) example_fuse_gelu_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_fuse_gelu.py" + ) ) example_fuse_gelu_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_fuse_gelu.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_fuse_gelu.py" + ) ) example_fuse_gelu_desc = "This given architecture is for a fused gelu: " # k = 3 example_mnist2 = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_mnist2.py" + ) ) example_mnist2_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_mnist2.py") + os.path.join(REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_mnist2.py") ) example_mnist2_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_mnist2.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_new_ex_mnist2.py" + ) ) exmaple_mnist2_desc = ( "This given architecture is for a model with fused convolutions and relus: " @@ -254,13 +283,20 @@ def prompt_generate_ex_with_CoT_template(ref_arch_src: str, cot_example: str) -> # k = 4 example_tiled_matmul = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/few_shot/model_ex_tiled_matmul.py" + ) ) example_tiled_matmul_cot = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/cot/model_cot_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, "src/kernelbench/prompts/cot/model_cot_tiled_matmul.py" + ) ) example_tiled_matmul_new = read_file( - os.path.join(REPO_TOP_PATH, "src/prompts/few_shot/model_new_ex_tiled_matmul.py") + os.path.join( + REPO_TOP_PATH, + "src/kernelbench/prompts/few_shot/model_new_ex_tiled_matmul.py", + ) ) example_tiled_matmul_desc = ( "This given architecture is for a model with tiled matrix multiplication: " @@ -327,10 +363,10 @@ def prompt_generate_custom_cuda_from_file_one_example(ref_arch_src, example_ind= # These are strictly defined for now example_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_ex_{example_ind}.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_{example_ind}.py" ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_{example_ind}.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_{example_ind}.py" ) if not os.path.exists(example_arch_path): @@ -357,9 +393,11 @@ def prompt_generate_custom_cuda_from_prompt_template(ref_arch_src: str) -> str: # These are strictly defined for now # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) - example_arch_path = os.path.join(REPO_TOP_PATH, f"src/prompts/model_ex_add.py") + example_arch_path = os.path.join( + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_add.py" + ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_add.py" ) if not os.path.exists(example_arch_path): @@ -389,13 +427,15 @@ def prompt_generate_prompt_with_hardware_info_from_template( # These are strictly defined for now # path to prompt template, show an example of Model (torch specifications) and ModelNew (torch + custom CUDA kernels) - example_arch_path = os.path.join(REPO_TOP_PATH, f"src/prompts/model_ex_add.py") + example_arch_path = os.path.join( + REPO_TOP_PATH, f"src/kernelbench/prompts/model_ex_add.py" + ) example_new_arch_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/model_new_ex_add.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/model_new_ex_add.py" ) gpu_spec_file_path = os.path.join( - REPO_TOP_PATH, f"src/prompts/hardware/gpu_specs.py" + REPO_TOP_PATH, f"src/kernelbench/prompts/hardware/gpu_specs.py" ) example_arch = read_file(example_arch_path) From fbf6f1fb79008e94b0f59da4b40cdf6cd1904c76 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Wed, 2 Apr 2025 17:51:11 +0200 Subject: [PATCH 16/17] rename --- scripts/claude_generation.py | 91 ----------------- scripts/llm_generation.py | 186 +++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 91 deletions(-) delete mode 100644 scripts/claude_generation.py create mode 100644 scripts/llm_generation.py diff --git a/scripts/claude_generation.py b/scripts/claude_generation.py deleted file mode 100644 index 3b5d04da..00000000 --- a/scripts/claude_generation.py +++ /dev/null @@ -1,91 +0,0 @@ -import asyncio -import weave -import requests -from rich.console import Console -from litellm import acompletion -from pydantic import BaseModel, Field -from kernelbench.utils import read_file, extract_python_code -from kernelbench.prompt_constructor import ( - prompt_generate_prompt_with_hardware_info_from_template, -) -from io import BytesIO - -GPU_NAME = "H100" -MODEL = "claude-3-5-sonnet-20240620" -ONE_SAMPLE_PATH = "KernelBench/level1/1_Square_matrix_multiplication_.py" -BENCHMARK_SERVER_URL = "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" -BENCHMARK_SERVER_PARAMS = { - "num_correct_trials": "5", - "num_perf_trials": "100", - "verbose": "true", -} - -console = Console() -ref_arch_src = read_file(ONE_SAMPLE_PATH) - - -console.rule("Generating prompt...") -# generate prompt -prompt = prompt_generate_prompt_with_hardware_info_from_template(ref_arch_src, GPU_NAME) - -console.print(prompt) - - -class ClaudeResponse(BaseModel): - generated_code: str = Field(description="The optimized generated code") - - -@weave.op -async def generate_with_claude(prompt): - response = await acompletion( - model=MODEL, - messages=[ - {"role": "user", "content": prompt}, - ], - max_tokens=2000, - temperature=0.5, - response_format=ClaudeResponse, - ) - response = ClaudeResponse.model_validate_json(response.choices[0].message.content) - return response - - -weave.init("claude_cuda") - -console.rule("Generating Kernel...") -response = asyncio.run(generate_with_claude(prompt)) - -console.print(response.generated_code) - - -@weave.op -def call_benchmark_server( - ref_pytorch_code, - optimized_code, - benchmark_server_url=BENCHMARK_SERVER_URL, - benchmark_server_params=BENCHMARK_SERVER_PARAMS, -): - # Create in-memory file objects - ref_file = BytesIO(ref_pytorch_code.encode("utf-8")) - kernel_file = BytesIO(optimized_code.encode("utf-8")) - - # Prepare the files for the request - files = { - "ref_file": ("ref_file.py", ref_file), - "kernel_file": ("kernel_file.py", kernel_file), - } - - # Make the request with both files and data - response = requests.post( - benchmark_server_url, files=files, data=benchmark_server_params - ) - - # Return the response - return response.json() - - -console.rule("Benchmarking...") -generated_code = extract_python_code(response.generated_code) -print(generated_code) -benchmark_result = call_benchmark_server(ref_arch_src, generated_code) -console.print(benchmark_result) diff --git a/scripts/llm_generation.py b/scripts/llm_generation.py new file mode 100644 index 00000000..5e592820 --- /dev/null +++ b/scripts/llm_generation.py @@ -0,0 +1,186 @@ +import asyncio +import weave +from weave.trace.context import call_context +import requests +from pathlib import Path +from dataclasses import dataclass +from rich.console import Console +import litellm +from litellm import acompletion +from pydantic import BaseModel, Field +import simple_parsing as sp +from kernelbench.utils import read_file, extract_python_code +from kernelbench.prompt_constructor import ( + prompt_generate_prompt_with_hardware_info_from_template, +) +from io import BytesIO + + +litellm.drop_params = True # drop params that are not used by the model + +BENCHMARK_SERVER_PARAMS: dict = { + "num_correct_trials": "5", + "num_perf_trials": "100", + "verbose": "true", +} + + +@dataclass +class ScriptArgs: + gpu_name: str = "H100" + # MODEL = "claude-3-5-sonnet-20240620" + model: str = "gpt-4o" + dataset_folder: str = "KernelBench/level1" + benchmark_server_url: str = ( + "https://tcapelle--kernel-benchmark-server-benchmarkservice-fastapi-app.modal.run/benchmark" + ) + debug: bool = False + N: int = 5 + + +args = sp.parse(ScriptArgs) + +console = Console() + +console.rule("Loading Dataset...") +ds = [ + {"ref_fname": str(sf), "ref_code": read_file(str(sf)), "gpu_name": args.gpu_name} + for sf in Path(args.dataset_folder).glob("*.py") +] + + +ds = ds[: args.N] + +console.print(f"Loaded {len(ds)} kernels") + + +class LLMResponse(BaseModel): + generated_code: str = Field(description="The optimized generated code") + + +class LLMCuda(weave.Model): + model: str = args.model + temperature: float = 0.5 + max_tokens: int = 2000 + + @weave.op + def prepare_prompt(self, filename: str, gpu_name: str): + ref_arch_src = read_file(filename) + prompt = prompt_generate_prompt_with_hardware_info_from_template( + ref_arch_src, gpu_name + ) + return prompt + + @weave.op + async def generate_with_llm(self, prompt) -> LLMResponse: + response = await acompletion( + model=self.model, + messages=[ + { + "role": "system", + "content": "You are a helpful assistant that generates optimized CUDA code for a given PyTorch function. You Reply in JSON format,", + }, + {"role": "user", "content": prompt}, + ], + max_tokens=self.max_tokens, + temperature=self.temperature, + response_format=LLMResponse, + ) + response = LLMResponse.model_validate_json(response.choices[0].message.content) + return response + + @weave.op + async def predict(self, ref_fname: str, gpu_name: str) -> LLMResponse: + prompt = self.prepare_prompt(ref_fname, gpu_name) + response = await self.generate_with_llm(prompt) + return response + + +weave.init("claude_cuda") + +claude = LLMCuda() + + +@weave.op +def call_benchmark_server( + ref_pytorch_code, + optimized_code, + benchmark_server_url=args.benchmark_server_url, + benchmark_server_params=BENCHMARK_SERVER_PARAMS, +): + # Create in-memory file objects + ref_file = BytesIO(ref_pytorch_code.encode("utf-8")) + kernel_file = BytesIO(optimized_code.encode("utf-8")) + + # Prepare the files for the request + files = { + "ref_file": ("ref_file.py", ref_file), + "kernel_file": ("kernel_file.py", kernel_file), + } + + # Make the request with both files and data + response = requests.post( + benchmark_server_url, files=files, data=benchmark_server_params + ) + + # Add debugging info + if args.debug: + console.print(f"Status code: {response.status_code}") + console.print( + f"Response content: {response.content[:500]}" + ) # Showing first 500 chars + + # Check for successful response before parsing JSON + if response.status_code != 200: + return { + "error": f"Server error: {response.status_code}", + "content": str(response.content), + } + + # Try to parse JSON with better error handling + try: + return response.json() + except requests.exceptions.JSONDecodeError: + return {"error": "Invalid JSON response", "content": str(response.content)} + + +@weave.op +def score_kernel(output: LLMResponse, ref_code: str) -> dict: + extracted_code = extract_python_code(output.generated_code) + benchmark_result = call_benchmark_server(ref_code, extracted_code) + error = benchmark_result.get("error", None) + if error is not None: + return { + "compiled": False, + "correctness": False, + "speedup_vs_compile": 0, + "speedup_vs_eager": 0, + "error": benchmark_result.get("content", str(error)), + } + + # Handle missing keys safely with .get() and provide defaults + kernel_result = benchmark_result.get("kernel_result", {}) + return { + "compiled": kernel_result.get("compiled", False), + "correctness": kernel_result.get("correctness", False), + "speedup_vs_compile": benchmark_result.get("speedup_vs_compile", 0), + "speedup_vs_eager": benchmark_result.get("speedup_vs_eager", 0), + "error": benchmark_result.get("error", None), + } + + +if args.debug: + console.rule("Running one sample...") + one_sample = ds[0] + console.print(f"One sample: {one_sample}") + response = asyncio.run( + claude.predict(one_sample["ref_fname"], one_sample["gpu_name"]) + ) + console.print(f"Response: {response}") + score = score_kernel(response, one_sample["ref_code"]) + console.print(f"Score: {score}") + +else: + console.rule("Running Evaluation...") + evaluation = weave.Evaluation(dataset=ds, scorers=[score_kernel]) + asyncio.run(evaluation.evaluate(claude)) From 3d845a386c0fb5ff1f34ccf8ba77466179a81396 Mon Sep 17 00:00:00 2001 From: Thomas Capelle Date: Sat, 5 Apr 2025 22:31:20 +0200 Subject: [PATCH 17/17] measure program time in here --- scripts/run_and_check.py | 130 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/scripts/run_and_check.py b/scripts/run_and_check.py index ab643371..3ac35bb6 100644 --- a/scripts/run_and_check.py +++ b/scripts/run_and_check.py @@ -1,14 +1,17 @@ import os import shutil +import importlib.util +import sys +import tempfile import torch +import numpy as np import pydra from pydra import REQUIRED, Config from datasets import load_dataset from kernelbench.eval import eval_kernel_against_ref, KernelExecResult from kernelbench.utils import read_file, set_gpu_arch -from scripts.generate_baseline_time import measure_program_time """ Run a pair of KernelBench format (problem, solution) to check if solution is correct and compute speedup @@ -126,6 +129,129 @@ def evaluate_single_sample_src( return eval_result +def measure_program_time( + ref_arch_name: str, # Added for consistency, although not used in this version + ref_arch_src: str, + num_trials: int, + device: torch.device, + use_torch_compile: bool = False, + torch_compile_backend: str | None = None, + torch_compile_options: str | None = None, +) -> dict: + """Measure the execution time of a reference program""" + + # Create temporary module + temp_dir = tempfile.mkdtemp() + ref_module_path = os.path.join(temp_dir, "ref_module.py") + + with open(ref_module_path, "w") as f: + f.write(ref_arch_src) + + # Load reference module + spec = importlib.util.spec_from_file_location("ref_module", ref_module_path) + ref_module = importlib.util.module_from_spec(spec) + sys.modules["ref_module"] = ref_module + spec.loader.exec_module(ref_module) + + # Create model instance + if hasattr(ref_module, "get_init_inputs"): + init_inputs = ref_module.get_init_inputs() + init_inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in init_inputs + ] + ref_model = ref_module.Model(*init_inputs).to(device) + else: + ref_model = ref_module.Model().to(device) + + # Apply torch.compile if needed + if use_torch_compile: + if torch_compile_backend is not None: + if torch_compile_options is not None and torch_compile_options != "default": + compile_options = ( + {"mode": torch_compile_options} + if torch_compile_options in ["max-autotune", "reduce-overhead"] + else {} + ) + ref_model = torch.compile( + ref_model, + backend=torch_compile_backend, + options=compile_options, + ) + else: + ref_model = torch.compile(ref_model, backend=torch_compile_backend) + else: + ref_model = torch.compile(ref_model) + + # Generate inputs + if hasattr(ref_module, "get_inputs"): + inputs = ref_module.get_inputs() + inputs = [ + ( + x + if (isinstance(x, torch.Tensor) and x.device == device) + else (x.to(device) if isinstance(x, torch.Tensor) else x) + ) + for x in inputs + ] + elif hasattr(ref_module, "INPUT_SHAPE"): + input_shape = ref_module.INPUT_SHAPE + if isinstance(input_shape, tuple): + inputs = (torch.randn(input_shape, device=device),) + elif isinstance(input_shape, list): + inputs = tuple(torch.randn(shape, device=device) for shape in input_shape) + else: + raise ValueError(f"Invalid INPUT_SHAPE: {input_shape}") + else: + # Infer inputs from model + if hasattr(ref_model, "forward"): + argcount = ref_model.forward.__code__.co_argcount + inputs = tuple( + torch.randn(1, 128, device=device) for _ in range(argcount - 1) + ) + else: + raise ValueError("Could not determine appropriate inputs for the model") + + # Warmup + for _ in range(10): + ref_model(*inputs) + + # Timing + torch.cuda.synchronize(device=device) + times = [] + for _ in range(num_trials): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + + start.record() + ref_model(*inputs) + end.record() + + torch.cuda.synchronize(device=device) + times.append(start.elapsed_time(end)) + + # Clean up + try: + os.remove(ref_module_path) + os.rmdir(temp_dir) + except OSError: + shutil.rmtree(temp_dir, ignore_errors=True) + + # Calculate statistics + times = np.array(times) + return { + "mean": float(np.mean(times)), + "std": float(np.std(times)), + "min": float(np.min(times)), + "max": float(np.max(times)), + "median": float(np.median(times)), + } + + @pydra.main(base=ScriptConfig) def main(config: ScriptConfig): @@ -171,7 +297,7 @@ def main(config: ScriptConfig): kernel_src = read_file(config.kernel_src_path) # Start Evaluation - device = torch.device("cuda:0") # default device + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") set_gpu_arch(config.gpu_arch) print("[INFO] Evaluating kernel against reference code")