Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions challenges/colab_exports/README.md

Large diffs are not rendered by default.

519 changes: 519 additions & 0 deletions challenges/colab_exports/easy/19_reverse_array.ipynb

Large diffs are not rendered by default.

483 changes: 483 additions & 0 deletions challenges/colab_exports/easy/1_vector_add.ipynb

Large diffs are not rendered by default.

530 changes: 530 additions & 0 deletions challenges/colab_exports/easy/21_relu.ipynb

Large diffs are not rendered by default.

505 changes: 505 additions & 0 deletions challenges/colab_exports/easy/23_leaky_relu.ipynb

Large diffs are not rendered by default.

520 changes: 520 additions & 0 deletions challenges/colab_exports/easy/24_rainbow_table.ipynb

Large diffs are not rendered by default.

518 changes: 518 additions & 0 deletions challenges/colab_exports/easy/2_matrix_multiplication.ipynb

Large diffs are not rendered by default.

489 changes: 489 additions & 0 deletions challenges/colab_exports/easy/31_matrix_copy.ipynb

Large diffs are not rendered by default.

490 changes: 490 additions & 0 deletions challenges/colab_exports/easy/3_matrix_transpose.ipynb

Large diffs are not rendered by default.

485 changes: 485 additions & 0 deletions challenges/colab_exports/easy/41_simple_inference.ipynb

Large diffs are not rendered by default.

504 changes: 504 additions & 0 deletions challenges/colab_exports/easy/52_silu.ipynb

Large diffs are not rendered by default.

498 changes: 498 additions & 0 deletions challenges/colab_exports/easy/54_swiglu.ipynb

Large diffs are not rendered by default.

510 changes: 510 additions & 0 deletions challenges/colab_exports/easy/62_value_clipping.ipynb

Large diffs are not rendered by default.

542 changes: 542 additions & 0 deletions challenges/colab_exports/easy/63_interleave.ipynb

Large diffs are not rendered by default.

504 changes: 504 additions & 0 deletions challenges/colab_exports/easy/65_geglu.ipynb

Large diffs are not rendered by default.

597 changes: 597 additions & 0 deletions challenges/colab_exports/easy/66_rgb_to_grayscale.ipynb

Large diffs are not rendered by default.

472 changes: 472 additions & 0 deletions challenges/colab_exports/easy/68_sigmoid.ipynb

Large diffs are not rendered by default.

483 changes: 483 additions & 0 deletions challenges/colab_exports/easy/7_color_inversion.ipynb

Large diffs are not rendered by default.

561 changes: 561 additions & 0 deletions challenges/colab_exports/easy/8_matrix_addition.ipynb

Large diffs are not rendered by default.

520 changes: 520 additions & 0 deletions challenges/colab_exports/easy/9_1d_convolution.ipynb

Large diffs are not rendered by default.

495 changes: 495 additions & 0 deletions challenges/colab_exports/hard/12_multi_head_attention.ipynb

Large diffs are not rendered by default.

483 changes: 483 additions & 0 deletions challenges/colab_exports/hard/14_multi_agent_sim.ipynb

Large diffs are not rendered by default.

456 changes: 456 additions & 0 deletions challenges/colab_exports/hard/15_sorting.ipynb

Large diffs are not rendered by default.

682 changes: 682 additions & 0 deletions challenges/colab_exports/hard/20_kmeans_clustering.ipynb

Large diffs are not rendered by default.

514 changes: 514 additions & 0 deletions challenges/colab_exports/hard/36_radix_sort.ipynb

Large diffs are not rendered by default.

483 changes: 483 additions & 0 deletions challenges/colab_exports/hard/39_Fast_Fourier_transform.ipynb

Large diffs are not rendered by default.

627 changes: 627 additions & 0 deletions challenges/colab_exports/hard/46_bfs_shortest_path.ipynb

Large diffs are not rendered by default.

510 changes: 510 additions & 0 deletions challenges/colab_exports/hard/53_casual_attention.ipynb

Large diffs are not rendered by default.

530 changes: 530 additions & 0 deletions challenges/colab_exports/hard/56_linear_attention.ipynb

Large diffs are not rendered by default.

544 changes: 544 additions & 0 deletions challenges/colab_exports/hard/59_sliding_window_attn.ipynb

Large diffs are not rendered by default.

606 changes: 606 additions & 0 deletions challenges/colab_exports/hard/73_all_pairs_shortest_paths.ipynb

Large diffs are not rendered by default.

565 changes: 565 additions & 0 deletions challenges/colab_exports/hard/74_gpt2_block.ipynb

Large diffs are not rendered by default.

600 changes: 600 additions & 0 deletions challenges/colab_exports/hard/93_llama_transformer_block.ipynb

Large diffs are not rendered by default.

561 changes: 561 additions & 0 deletions challenges/colab_exports/medium/10_2d_convolution.ipynb

Large diffs are not rendered by default.

651 changes: 651 additions & 0 deletions challenges/colab_exports/medium/11_3d_convolution.ipynb

Large diffs are not rendered by default.

493 changes: 493 additions & 0 deletions challenges/colab_exports/medium/13_histogramming.ipynb

Large diffs are not rendered by default.

480 changes: 480 additions & 0 deletions challenges/colab_exports/medium/16_prefix_sum.ipynb

Large diffs are not rendered by default.

498 changes: 498 additions & 0 deletions challenges/colab_exports/medium/17_dot_product.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

541 changes: 541 additions & 0 deletions challenges/colab_exports/medium/22_gemm.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

497 changes: 497 additions & 0 deletions challenges/colab_exports/medium/27_mean_squared_error.ipynb

Large diffs are not rendered by default.

585 changes: 585 additions & 0 deletions challenges/colab_exports/medium/28_gaussian_blur.ipynb

Large diffs are not rendered by default.

493 changes: 493 additions & 0 deletions challenges/colab_exports/medium/29_top_k_selection.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

601 changes: 601 additions & 0 deletions challenges/colab_exports/medium/32_int8_quantized_matmul.ipynb

Large diffs are not rendered by default.

709 changes: 709 additions & 0 deletions challenges/colab_exports/medium/33_ordinary_least_squares.ipynb

Large diffs are not rendered by default.

621 changes: 621 additions & 0 deletions challenges/colab_exports/medium/34_logistic_regression.ipynb

Large diffs are not rendered by default.

507 changes: 507 additions & 0 deletions challenges/colab_exports/medium/35_monte_carlo_integration.ipynb

Large diffs are not rendered by default.

518 changes: 518 additions & 0 deletions challenges/colab_exports/medium/37_matrix_power.ipynb

Large diffs are not rendered by default.

599 changes: 599 additions & 0 deletions challenges/colab_exports/medium/38_nearest_neighbor.ipynb

Large diffs are not rendered by default.

601 changes: 601 additions & 0 deletions challenges/colab_exports/medium/40_batch_normalization.ipynb

Large diffs are not rendered by default.

774 changes: 774 additions & 0 deletions challenges/colab_exports/medium/42_2d_max_pooling.ipynb

Large diffs are not rendered by default.

490 changes: 490 additions & 0 deletions challenges/colab_exports/medium/43_count_array_element.ipynb

Large diffs are not rendered by default.

498 changes: 498 additions & 0 deletions challenges/colab_exports/medium/44_count_2d_array_element.ipynb

Large diffs are not rendered by default.

513 changes: 513 additions & 0 deletions challenges/colab_exports/medium/45_count_3d_array_element.ipynb

Large diffs are not rendered by default.

497 changes: 497 additions & 0 deletions challenges/colab_exports/medium/47_subarray_sum.ipynb

Large diffs are not rendered by default.

531 changes: 531 additions & 0 deletions challenges/colab_exports/medium/48_2d_subarray_sum.ipynb

Large diffs are not rendered by default.

560 changes: 560 additions & 0 deletions challenges/colab_exports/medium/49_3d_subarray_sum.ipynb

Large diffs are not rendered by default.

490 changes: 490 additions & 0 deletions challenges/colab_exports/medium/4_reduction.ipynb

Large diffs are not rendered by default.

551 changes: 551 additions & 0 deletions challenges/colab_exports/medium/50_rms_normalization.ipynb

Large diffs are not rendered by default.

517 changes: 517 additions & 0 deletions challenges/colab_exports/medium/51_max_subarray_sum.ipynb

Large diffs are not rendered by default.

574 changes: 574 additions & 0 deletions challenges/colab_exports/medium/55_attn_w_linear_bias.ipynb

Large diffs are not rendered by default.

499 changes: 499 additions & 0 deletions challenges/colab_exports/medium/57_fp16_batched_matmul.ipynb

Large diffs are not rendered by default.

504 changes: 504 additions & 0 deletions challenges/colab_exports/medium/58_fp16_dot_product.ipynb

Large diffs are not rendered by default.

506 changes: 506 additions & 0 deletions challenges/colab_exports/medium/5_softmax.ipynb

Large diffs are not rendered by default.

586 changes: 586 additions & 0 deletions challenges/colab_exports/medium/60_top_p_sampling.ipynb

Large diffs are not rendered by default.

560 changes: 560 additions & 0 deletions challenges/colab_exports/medium/61_rope_embedding.ipynb

Large diffs are not rendered by default.

526 changes: 526 additions & 0 deletions challenges/colab_exports/medium/64_weight_dequantization.ipynb

Large diffs are not rendered by default.

581 changes: 581 additions & 0 deletions challenges/colab_exports/medium/67_moe_topk_gating.ipynb

Large diffs are not rendered by default.

610 changes: 610 additions & 0 deletions challenges/colab_exports/medium/69_jacobi_stencil_2d.ipynb

Large diffs are not rendered by default.

524 changes: 524 additions & 0 deletions challenges/colab_exports/medium/6_softmax_attention.ipynb

Large diffs are not rendered by default.

552 changes: 552 additions & 0 deletions challenges/colab_exports/medium/70_segmented_prefix_sum.ipynb

Large diffs are not rendered by default.

535 changes: 535 additions & 0 deletions challenges/colab_exports/medium/71_parallel_merge.ipynb

Large diffs are not rendered by default.

488 changes: 488 additions & 0 deletions challenges/colab_exports/medium/72_stream_compaction.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

728 changes: 728 additions & 0 deletions challenges/colab_exports/medium/76_adder_transformer.ipynb

Large diffs are not rendered by default.

474 changes: 474 additions & 0 deletions challenges/colab_exports/medium/78_2d_fft.ipynb

Large diffs are not rendered by default.

560 changes: 560 additions & 0 deletions challenges/colab_exports/medium/80_grouped_query_attention.ipynb

Large diffs are not rendered by default.

538 changes: 538 additions & 0 deletions challenges/colab_exports/medium/81_int4_matmul.ipynb

Large diffs are not rendered by default.

503 changes: 503 additions & 0 deletions challenges/colab_exports/medium/82_linear_recurrence.ipynb

Large diffs are not rendered by default.

543 changes: 543 additions & 0 deletions challenges/colab_exports/medium/84_swiglu_mlp_block.ipynb

Large diffs are not rendered by default.

552 changes: 552 additions & 0 deletions challenges/colab_exports/medium/85_lora_linear.ipynb

Large diffs are not rendered by default.

Large diffs are not rendered by default.

568 changes: 568 additions & 0 deletions challenges/colab_exports/medium/90_causal_depthwise_conv1d.ipynb

Large diffs are not rendered by default.

527 changes: 527 additions & 0 deletions challenges/colab_exports/medium/92_decaying_causal_attention.ipynb

Large diffs are not rendered by default.

582 changes: 582 additions & 0 deletions challenges/colab_exports/medium/94_ssm_selective_scan.ipynb

Large diffs are not rendered by default.

536 changes: 536 additions & 0 deletions challenges/colab_exports/medium/96_int8_kv_cache_attention.ipynb

Large diffs are not rendered by default.

320 changes: 320 additions & 0 deletions scripts/migrate_to_colab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
import os
import glob
import json
import re

def generate_notebook(challenge_dir, output_dir):
parts = challenge_dir.strip('/').split('/')
level = parts[-2]
name = parts[-1]

# Check if files exist
challenge_py_path = os.path.join(challenge_dir, "challenge.py")
challenge_html_path = os.path.join(challenge_dir, "challenge.html")
starter_dir = os.path.join(challenge_dir, "starter")

if not os.path.exists(challenge_py_path) or not os.path.exists(challenge_html_path):
return

print(f"Migrating {level} {name}...")

cells = []

# 1. Config cell
cells.append({
"cell_type": "code",
"execution_count": None,
"metadata": {"id": "config_cell"},
"outputs": [],
"source": [
"# Change this to your preferred framework (e.g., 'cuda', 'pytorch', 'triton', 'jax', 'mojo')\n",
"EVAL_LANG = 'cuda'\n",
"\n",
"SAVE_GPU = True\n"
]
})

# 2. Markdown description
with open(challenge_html_path, 'r', encoding='utf-8') as f:
html_content = f.read()

# Replace LaTeX delimiters with standard markdown/mathjax delimiters
html_content = html_content.replace("\\(", "$").replace("\\)", "$")
html_content = html_content.replace("\\[", "$$").replace("\\]", "$$")

cells.append({
"cell_type": "markdown",
"metadata": {"id": "desc_cell"},
"source": [html_content]
})

# 3. Starter templates (hidden cells)
if os.path.exists(starter_dir):
for starter_file in sorted(os.listdir(starter_dir)):
if starter_file.startswith("starter."):
ext = starter_file[len("starter."):]

# Header mapping
header_map = {
"cu": "# CUDA",
"cute.py": "# CUTE",
"jax.py": "# JAX",
"mojo": "# MOJO",
"pytorch.py": "# Torch",
"triton.py": "# Triton"
}
header_text = header_map.get(ext, f"# {ext.upper()}")

cells.append({
"cell_type": "markdown",
"metadata": {},
"source": [header_text]
})

# Default output filenames
out_filename = "solution.cu" if ext == "cu" else f"solution.{ext}"
if out_filename.endswith(".py"):
out_filename = "solution.py"

with open(os.path.join(starter_dir, starter_file), 'r', encoding='utf-8') as f:
starter_content = f.read()

cells.append({
"cell_type": "code",
"execution_count": None,
"metadata": {
"id": f"starter_{ext.replace('.', '_')}",
"cellView": "form",
"collapsed": True
},
"outputs": [],
"source": [
f"%%writefile {out_filename}\n",
starter_content
]
})

# 4. Challenge Base & Challenge logic
base_py_path = os.path.join("challenges", "core", "challenge_base.py")
with open(base_py_path, 'r', encoding='utf-8') as f:
base_content = f.read()

with open(challenge_py_path, 'r', encoding='utf-8') as f:
challenge_content = f.read()

# Remove the import statement
challenge_content = re.sub(r"from core\.challenge_base import ChallengeBase\n?", "", challenge_content)

combined_challenge = (
"# --- Core Challenge Base ---\n" +
base_content + "\n\n" +
"# --- Challenge Logic ---\n" +
challenge_content + "\n\n" +
"ch = Challenge()\n"
)

cells.append({
"cell_type": "markdown",
"metadata": {},
"source": ["# Evaluate Setup"]
})

cells.append({
"cell_type": "code",
"execution_count": None,
"metadata": {"id": "challenge_logic"},
"outputs": [],
"source": [line + "\n" for line in combined_challenge.split("\n")]
})

# 5. Evaluator script
eval_script = """import os
import time
import ctypes
import torch

class Evaluate:
@staticmethod
def eval_cuda(ch):
# 1. Compile a fresh uniquely named library
so_filename = f'solution_func_{int(time.time())}.so'
os.system(f'nvcc -shared -Xcompiler -fPIC -O3 solution.cu -o {so_filename}')
lib = ctypes.CDLL(f'./{so_filename}')

# 2. Extract signature and set argtypes
signature = ch.get_solve_signature()
lib.solve.argtypes = [arg_info[0] for arg_info in signature.values()]

Evaluate._run_tests(ch, signature, lambda kwargs: lib.solve(*Evaluate._build_cuda_args(kwargs, signature)))

@staticmethod
def eval_python(ch):
import importlib.util
import sys

spec = importlib.util.spec_from_file_location("solution", "solution.py")
solution = importlib.util.module_from_spec(spec)
sys.modules["solution"] = solution
spec.loader.exec_module(solution)

signature = ch.get_solve_signature()
Evaluate._run_tests(ch, signature, lambda kwargs: Evaluate._run_python(solution, kwargs))

@staticmethod
def _run_python(solution, kwargs):
solution.solve(**kwargs)
if torch.cuda.is_available():
torch.cuda.synchronize()

@staticmethod
def eval_mojo(ch):
print("Mojo evaluation is currently executed via a separate runner or wrapper.")
print("Ensure you have the mojo compiler installed and use 'mojo build solution.mojo' + ctypes/ffi,")
print("or run an external python bridge. This is a stub.")

@staticmethod
def _build_cuda_args(kwargs, signature):
cuda_args = []
for k, (arg_type, dir_type) in signature.items():
val = kwargs[k]
if isinstance(val, torch.Tensor):
cuda_args.append(ctypes.cast(val.data_ptr(), arg_type))
else:
cuda_args.append(arg_type(val))
return cuda_args

@staticmethod
def _run_tests(ch, signature, run_fn):
print("=== Running Functional Tests ===")
functional_tests = ch.generate_functional_test()
all_passed = True

for i, test in enumerate(functional_tests):
ref_kwargs = {k: (v.clone() if isinstance(v, torch.Tensor) else v) for k, v in test.items()}
test_kwargs = {k: (v.clone() if isinstance(v, torch.Tensor) else v) for k, v in test.items()}

# Run Reference
ch.reference_impl(**ref_kwargs)

# Run implementation
run_fn(test_kwargs)
if torch.cuda.is_available():
torch.cuda.synchronize()

# Verify outputs
match = True
for k, (_, dir_type) in signature.items():
if dir_type == "out":
if not torch.allclose(ref_kwargs[k], test_kwargs[k], atol=ch.atol, rtol=ch.rtol):
match = False
print(f"❌ Test {i+1}/{len(functional_tests)} Failed on output '{k}'")
break

if match:
print(f"✅ Test {i+1}/{len(functional_tests)} Passed")
else:
all_passed = False
break

if all_passed:
print("\\n🎉 All functional tests passed!")
return True
else:
return False
"""
cells.append({
"cell_type": "code",
"execution_count": None,
"metadata": {"id": "evaluator", "cellView": "form", "collapsed": True},
"outputs": [],
"source": [line + "\n" for line in eval_script.split("\n")]
})

cells.append({
"cell_type": "markdown",
"metadata": {},
"source": ["# Evaluation code"]
})

# 6. Run and Disconnect runtime cell
cells.append({
"cell_type": "code",
"execution_count": None,
"metadata": {"id": "disconnect"},
"outputs": [],
"source": [
"# Run the evaluator based on configuration\n",
"if EVAL_LANG == 'cuda':\n",
" Evaluate.eval_cuda(ch)\n",
"elif EVAL_LANG in ['pytorch', 'triton', 'jax', 'cute']:\n",
" Evaluate.eval_python(ch)\n",
"elif EVAL_LANG == 'mojo':\n",
" Evaluate.eval_mojo(ch)\n",
"else:\n",
" print(f\"Unknown language {EVAL_LANG}\")\n",
"\n",
"# Disconnect runtime to save Colab resources\n",
"if SAVE_GPU:\n",
" from google.colab import runtime\n",
" runtime.unassign()\n"
]
})

notebook = {
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"accelerator": "GPU",
"colab": {
"gpuType": "T4",
"provenance": []
}
},
"cells": cells
}

out_dir_level = os.path.join(output_dir, level)
out_file = os.path.join(out_dir_level, f"{name}.ipynb")
os.makedirs(out_dir_level, exist_ok=True)
with open(out_file, 'w', encoding='utf-8') as f:
json.dump(notebook, f, indent=2)
print(f"Saved to {out_file}")

if __name__ == "__main__":
out_dir = "challenges/colab_exports"

exported_notebooks = []

challenges_glob = glob.glob("challenges/*/*")
for challenge_dir in challenges_glob:
if os.path.isdir(challenge_dir) and "colab_exports" not in challenge_dir:
generate_notebook(challenge_dir, out_dir)
parts = challenge_dir.strip('/').split('/')
level = parts[-2]
name = parts[-1]
if os.path.exists(os.path.join(out_dir, level, f"{name}.ipynb")):
exported_notebooks.append((level, name))

# Create README.md
readme_path = os.path.join(out_dir, "README.md")
with open(readme_path, "w", encoding="utf-8") as f:
f.write("# LeetGPU Colab Notebooks\n\n")
f.write("Click the badges below to open the challenges directly in Google Colab.\n\n")

# Group by level
grouped = {}
for level, name in exported_notebooks:
if level not in grouped:
grouped[level] = []
grouped[level].append(name)

# Define specific sort order for levels
level_order = {"easy": 1, "medium": 2, "hard": 3}
for level in sorted(grouped.keys(), key=lambda x: level_order.get(x, 99)):
f.write(f"## {level.capitalize()}\n\n")
for name in sorted(grouped[level]):
colab_link = f"https://colab.research.google.com/github/lekhit/leetgpu-challenges/blob/main/challenges/colab_exports/{level}/{name}.ipynb"
badge = f"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)]({colab_link})"
f.write(f"- {badge} **{name}**\n")
f.write("\n")
print(f"Generated {readme_path}")