diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3102a121..c548a51a 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -54,7 +54,7 @@ tito/ │ └── notebooks.py # Notebooks command └── tools/ # CLI tools ├── __init__.py - └── py_to_notebook.py # Conversion tool + └── tito # Main CLI script ``` ## 🎯 Design Patterns Applied diff --git a/bin/py_to_notebook.py b/bin/py_to_notebook.py deleted file mode 100755 index 61357509..00000000 --- a/bin/py_to_notebook.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 -""" -Convert Python files with cell markers to Jupyter notebooks. - -Usage: - python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py - python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py --output custom_name.ipynb -""" - -import argparse -import json -import re -import sys -from pathlib import Path - -def convert_py_to_notebook(py_file: Path, output_file: Path = None): - """Convert Python file with cell markers to notebook.""" - - if not py_file.exists(): - print(f"❌ File not found: {py_file}") - return False - - # Read the Python file - with open(py_file, 'r') as f: - content = f.read() - - # Split into cells based on # %% markers - cells = re.split(r'^# %%.*$', content, flags=re.MULTILINE) - cells = [cell.strip() for cell in cells if cell.strip()] - - # Create notebook structure - notebook = { - 'cells': [], - 'metadata': { - 'kernelspec': { - 'display_name': 'Python 3', - 'language': 'python', - 'name': 'python3' - }, - 'language_info': { - 'name': 'python', - 'version': '3.8.0' - } - }, - 'nbformat': 4, - 'nbformat_minor': 4 - } - - for i, cell_content in enumerate(cells): - if not cell_content: - continue - - # Check if this is a markdown cell - if cell_content.startswith('# ') and '\n' in cell_content: - lines = cell_content.split('\n') - if lines[0].startswith('# ') and not any(line.strip() and not line.startswith('#') for line in lines[:5]): - # This looks like a markdown cell - cell = { - 'cell_type': 'markdown', - 'metadata': {}, - 'source': [] - } - - for line in lines: - if line.startswith('# '): - cell['source'].append(line[2:] + '\n') - elif line.startswith('#'): - cell['source'].append(line[1:] + '\n') - elif line.strip() == '': - cell['source'].append('\n') - - notebook['cells'].append(cell) - continue - - # Code cell - cell = { - 'cell_type': 'code', - 'execution_count': None, - 'metadata': {}, - 'outputs': [], - 'source': [] - } - - for line in cell_content.split('\n'): - cell['source'].append(line + '\n') - - # Remove trailing newline from last line - if cell['source'] and cell['source'][-1].endswith('\n'): - cell['source'][-1] = cell['source'][-1][:-1] - - notebook['cells'].append(cell) - - # Determine output file - if output_file is None: - output_file = py_file.with_suffix('.ipynb') - - # Write notebook - output_file.parent.mkdir(parents=True, exist_ok=True) - with open(output_file, 'w') as f: - json.dump(notebook, f, indent=2) - - print(f"✅ Converted {py_file} → {output_file}") - return True - -def main(): - parser = argparse.ArgumentParser(description="Convert Python files to Jupyter notebooks") - parser.add_argument('input_file', type=Path, help='Input Python file') - parser.add_argument('--output', '-o', type=Path, help='Output notebook file') - parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output') - - args = parser.parse_args() - - success = convert_py_to_notebook(args.input_file, args.output) - - if not success: - sys.exit(1) - - if args.verbose: - print("🎉 Conversion complete!") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/modules/setup/setup_dev.ipynb b/modules/setup/setup_dev.ipynb index 284bd58d..e8d4c96e 100644 --- a/modules/setup/setup_dev.ipynb +++ b/modules/setup/setup_dev.ipynb @@ -1,684 +1,677 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "jupyter:\n", - " jupytext:\n", - " text_representation:\n", - " extension: .py\n", - " format_name: percent\n", - " format_version: '1.3'\n", - " jupytext_version: 1.17.1\n", - "---\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "# Module 0: Setup - Tiny\ud83d\udd25Torch Development Workflow\n", - "\n", - "Welcome to TinyTorch! This module teaches you the development workflow you'll use throughout the course.\n", - "\n", - "## Learning Goals\n", - "- Understand the nbdev notebook-to-Python workflow\n", - "- Write your first TinyTorch code\n", - "- Run tests and use the CLI tools\n", - "- Get comfortable with the development rhythm\n", - "\n", - "## The TinyTorch Development Cycle\n", - "\n", - "1. **Write code** in this notebook using `#| export` \n", - "2. **Export code** with `python bin/tito.py sync --module setup`\n", - "3. **Run tests** with `python bin/tito.py test --module setup`\n", - "4. **Check progress** with `python bin/tito.py info`\n", - "\n", - "Let's get started!\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| default_exp core.utils\n", - "\n", - "# Setup imports and environment\n", - "import sys\n", - "import platform\n", - "from datetime import datetime\n", - "import os\n", - "from pathlib import Path\n", - "\n", - "print(\"\ud83d\udd25 TinyTorch Development Environment\")\n", - "print(f\"Python {sys.version}\")\n", - "print(f\"Platform: {platform.system()} {platform.release()}\")\n", - "print(f\"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## Step 1: Understanding the Module \u2192 Package Structure\n", - "\n", - "**\ud83c\udf93 Teaching vs. \ud83d\udd27 Building**: This course has two sides:\n", - "- **Teaching side**: You work in `modules/setup/setup_dev.ipynb` (learning-focused)\n", - "- **Building side**: Your code exports to `tinytorch/core/utils.py` (production package)\n", - "\n", - "**Key Concept**: The `#| default_exp core.utils` directive at the top tells nbdev to export all `#| export` cells to `tinytorch/core/utils.py`.\n", - "\n", - "This separation allows us to:\n", - "- Organize learning by **concepts** (modules) \n", - "- Organize code by **function** (package structure)\n", - "- Build a real ML framework while learning systematically\n", - "\n", - "Let's write a simple \"Hello World\" function with the `#| export` directive:\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def hello_tinytorch():\n", - " \"\"\"\n", - " A simple hello world function for TinyTorch.\n", - " \n", - " TODO: Implement this function to display TinyTorch ASCII art and welcome message.\n", - " Load the flame art from tinytorch_flame.txt file with graceful fallback.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - "\n", - "def add_numbers(a, b):\n", - " \"\"\"\n", - " Add two numbers together.\n", - " \n", - " TODO: Implement addition of two numbers.\n", - " This is the foundation of all mathematical operations in ML.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "#| export\n", - "def hello_tinytorch():\n", - " \"\"\"Display the TinyTorch ASCII art and welcome message.\"\"\"\n", - " try:\n", - " # Get the directory containing this file\n", - " current_dir = Path(__file__).parent\n", - " art_file = current_dir / \"tinytorch_flame.txt\"\n", - " \n", - " if art_file.exists():\n", - " with open(art_file, 'r') as f:\n", - " ascii_art = f.read()\n", - " print(ascii_art)\n", - " print(\"Tiny\ud83d\udd25Torch\")\n", - " print(\"Build ML Systems from Scratch!\")\n", - " else:\n", - " print(\"\ud83d\udd25 TinyTorch \ud83d\udd25\")\n", - " print(\"Build ML Systems from Scratch!\")\n", - " except NameError:\n", - " # Handle case when running in notebook where __file__ is not defined\n", - " try:\n", - " art_file = Path(os.getcwd()) / \"tinytorch_flame.txt\"\n", - " if art_file.exists():\n", - " with open(art_file, 'r') as f:\n", - " ascii_art = f.read()\n", - " print(ascii_art)\n", - " print(\"Tiny\ud83d\udd25Torch\")\n", - " print(\"Build ML Systems from Scratch!\")\n", - " else:\n", - " print(\"\ud83d\udd25 TinyTorch \ud83d\udd25\")\n", - " print(\"Build ML Systems from Scratch!\")\n", - " except:\n", - " print(\"\ud83d\udd25 TinyTorch \ud83d\udd25\")\n", - " print(\"Build ML Systems from Scratch!\")\n", - "\n", - "def add_numbers(a, b):\n", - " \"\"\"Add two numbers together.\"\"\"\n", - " return a + b" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "### \ud83e\uddea Test Your Implementation\n", - "\n", - "Once you implement the functions above, run this cell to test them:\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test the functions in the notebook (will fail until implemented)\n", - "try:\n", - " print(\"Testing hello_tinytorch():\")\n", - " hello_tinytorch()\n", - " print()\n", - " print(\"Testing add_numbers():\")\n", - " print(f\"2 + 3 = {add_numbers(2, 3)}\")\n", - "except NotImplementedError as e:\n", - " print(f\"\u26a0\ufe0f {e}\")\n", - " print(\"Implement the functions above first!\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## Step 2: A Simple Class\n", - "\n", - "Let's create a simple class that will help us understand system information. This is still basic, but shows how to structure classes in TinyTorch.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "class SystemInfo:\n", - " \"\"\"\n", - " Simple system information class.\n", - " \n", - " TODO: Implement this class to collect and display system information.\n", - " \"\"\"\n", - " \n", - " def __init__(self):\n", - " \"\"\"\n", - " Initialize system information collection.\n", - " \n", - " TODO: Collect Python version, platform, and machine information.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def __str__(self):\n", - " \"\"\"\n", - " Return human-readable system information.\n", - " \n", - " TODO: Format system info as a readable string.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def is_compatible(self):\n", - " \"\"\"\n", - " Check if system meets minimum requirements.\n", - " \n", - " TODO: Check if Python version is >= 3.8\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "#| export\n", - "class SystemInfo:\n", - " \"\"\"Simple system information class.\"\"\"\n", - " \n", - " def __init__(self):\n", - " self.python_version = sys.version_info\n", - " self.platform = platform.system()\n", - " self.machine = platform.machine()\n", - " \n", - " def __str__(self):\n", - " return f\"Python {self.python_version.major}.{self.python_version.minor} on {self.platform} ({self.machine})\"\n", - " \n", - " def is_compatible(self):\n", - " \"\"\"Check if system meets minimum requirements.\"\"\"\n", - " return self.python_version >= (3, 8)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "### \ud83e\uddea Test Your SystemInfo Class\n", - "\n", - "Once you implement the SystemInfo class above, run this cell to test it:\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test the SystemInfo class (will fail until implemented)\n", - "try:\n", - " print(\"Testing SystemInfo class:\")\n", - " info = SystemInfo()\n", - " print(f\"System: {info}\")\n", - " print(f\"Compatible: {info.is_compatible()}\")\n", - "except NotImplementedError as e:\n", - " print(f\"\u26a0\ufe0f {e}\")\n", - " print(\"Implement the SystemInfo class above first!\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## Step 3: Developer Personalization\n", - "\n", - "Let's make TinyTorch yours! Create a developer profile that will identify you throughout your ML systems journey.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "class DeveloperProfile:\n", - " \"\"\"\n", - " Developer profile for personalizing TinyTorch experience.\n", - " \n", - " TODO: Implement this class to store and display developer information.\n", - " Default to course instructor but allow students to personalize.\n", - " \"\"\"\n", - " \n", - " @staticmethod\n", - " def _load_default_flame():\n", - " \"\"\"\n", - " Load the default TinyTorch flame ASCII art from file.\n", - " \n", - " TODO: Implement file loading for tinytorch_flame.txt with fallback.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def __init__(self, name=\"Vijay Janapa Reddi\", affiliation=\"Harvard University\", \n", - " email=\"vj@eecs.harvard.edu\", github_username=\"profvjreddi\", ascii_art=None):\n", - " \"\"\"\n", - " Initialize developer profile.\n", - " \n", - " TODO: Store developer information with sensible defaults.\n", - " Students should be able to customize this with their own info and ASCII art.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def __str__(self):\n", - " \"\"\"\n", - " Return formatted developer information.\n", - " \n", - " TODO: Format developer info as a professional signature with optional ASCII art.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def get_signature(self):\n", - " \"\"\"\n", - " Get a short signature for code headers.\n", - " \n", - " TODO: Return a concise signature like \"Built by Name (@github)\"\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")\n", - " \n", - " def get_ascii_art(self):\n", - " \"\"\"\n", - " Get ASCII art for the profile.\n", - " \n", - " TODO: Return custom ASCII art or default flame loaded from file.\n", - " \"\"\"\n", - " raise NotImplementedError(\"Student implementation required\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "#| export\n", - "class DeveloperProfile:\n", - " \"\"\"Developer profile for personalizing TinyTorch experience.\"\"\"\n", - " \n", - " @staticmethod\n", - " def _load_default_flame():\n", - " \"\"\"Load the default TinyTorch flame ASCII art from file.\"\"\"\n", - " try:\n", - " # Try to load from the same directory as this module\n", - " try:\n", - " # Try to get the directory of the current file\n", - " current_dir = os.path.dirname(__file__)\n", - " except NameError:\n", - " # If __file__ is not defined (e.g., in notebook), use current directory\n", - " current_dir = os.getcwd()\n", - " \n", - " flame_path = os.path.join(current_dir, 'tinytorch_flame.txt')\n", - " \n", - " with open(flame_path, 'r', encoding='utf-8') as f:\n", - " flame_art = f.read()\n", - " \n", - " # Add the Tiny\ud83d\udd25Torch text below the flame\n", - " return f\"\"\"{flame_art}\n", - " \n", - " Tiny\ud83d\udd25Torch\n", - " Build ML Systems from Scratch!\n", - " \"\"\"\n", - " except (FileNotFoundError, IOError):\n", - " # Fallback to simple flame if file not found\n", - " return \"\"\"\n", - " \ud83d\udd25 TinyTorch Developer \ud83d\udd25\n", - " . . . . . .\n", - " . . . . . .\n", - " . . . . . . .\n", - " . . . . . . . .\n", - " . . . . . . . . .\n", - " . . . . . . . . . .\n", - " . . . . . . . . . . .\n", - " . . . . . . . . . . . .\n", - " . . . . . . . . . . . . .\n", - ". . . . . . . . . . . . . .\n", - " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ \\\\ / / / / / /\n", - " \\\\ \\\\ / / / / / /\n", - " \\\\ / / / / / /\n", - " \\\\/ / / / / /\n", - " \\\\/ / / / /\n", - " \\\\/ / / /\n", - " \\\\/ / /\n", - " \\\\/ /\n", - " \\\\/\n", - " \n", - " Tiny\ud83d\udd25Torch\n", - " Build ML Systems from Scratch!\n", - " \"\"\"\n", - " \n", - " def __init__(self, name=\"Vijay Janapa Reddi\", affiliation=\"Harvard University\", \n", - " email=\"vj@eecs.harvard.edu\", github_username=\"profvjreddi\", ascii_art=None):\n", - " self.name = name\n", - " self.affiliation = affiliation\n", - " self.email = email\n", - " self.github_username = github_username\n", - " self.ascii_art = ascii_art or self._load_default_flame()\n", - " \n", - " def __str__(self):\n", - " return f\"\ud83d\udc68\u200d\ud83d\udcbb {self.name} | {self.affiliation} | @{self.github_username}\"\n", - " \n", - " def get_signature(self):\n", - " \"\"\"Get a short signature for code headers.\"\"\"\n", - " return f\"Built by {self.name} (@{self.github_username})\"\n", - " \n", - " def get_ascii_art(self):\n", - " \"\"\"Get ASCII art for the profile.\"\"\"\n", - " return self.ascii_art\n", - " \n", - " def get_full_profile(self):\n", - " \"\"\"Get complete profile with ASCII art.\"\"\"\n", - " return f\"\"\"{self.ascii_art}\n", - " \n", - "\ud83d\udc68\u200d\ud83d\udcbb Developer: {self.name}\n", - "\ud83c\udfdb\ufe0f Affiliation: {self.affiliation}\n", - "\ud83d\udce7 Email: {self.email}\n", - "\ud83d\udc19 GitHub: @{self.github_username}\n", - "\ud83d\udd25 Ready to build ML systems from scratch!\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "### \ud83e\uddea Test Your Developer Profile\n", - "\n", - "Customize your developer profile! Replace the default information with your own:\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test the DeveloperProfile class\n", - "try:\n", - " print(\"Testing DeveloperProfile (with defaults):\")\n", - " # Default profile (instructor)\n", - " default_profile = DeveloperProfile()\n", - " print(f\"Profile: {default_profile}\")\n", - " print(f\"Signature: {default_profile.get_signature()}\")\n", - " print()\n", - " \n", - " print(\"\ud83c\udfa8 ASCII Art Preview:\")\n", - " print(default_profile.get_ascii_art())\n", - " print()\n", - " \n", - " print(\"\ud83d\udd25 Full Profile Display:\")\n", - " print(default_profile.get_full_profile())\n", - " print()\n", - " \n", - " # TODO: Students should customize this with their own information!\n", - " print(\"\ud83c\udfaf YOUR TURN: Create your own profile!\")\n", - " print(\"Uncomment and modify the lines below:\")\n", - " print(\"# my_profile = DeveloperProfile(\")\n", - " print(\"# name='Your Name',\")\n", - " print(\"# affiliation='Your University/Company',\")\n", - " print(\"# email='your.email@example.com',\")\n", - " print(\"# github_username='yourgithub',\")\n", - " print(\"# ascii_art='''\")\n", - " print(\"# Your Custom ASCII Art Here!\")\n", - " print(\"# Maybe your initials, a logo, or something fun!\")\n", - " print(\"# '''\")\n", - " print(\"# )\")\n", - " print(\"# print(f'My Profile: {my_profile}')\")\n", - " print(\"# print(f'My Signature: {my_profile.get_signature()}')\")\n", - " print(\"# print(my_profile.get_full_profile())\")\n", - " \n", - "except NotImplementedError as e:\n", - " print(f\"\u26a0\ufe0f {e}\")\n", - " print(\"Implement the DeveloperProfile class above first!\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "### \ud83c\udfa8 Personalization Challenge\n", - "\n", - "**For Students**: Make TinyTorch truly yours by:\n", - "\n", - "1. **Update your profile** in the cell above with your real information\n", - "2. **Create custom ASCII art** - your initials, a simple logo, or something that represents you\n", - "3. **Customize the flame file** - edit `tinytorch_flame.txt` to create your own default art\n", - "4. **Add your signature** to code you write throughout the course\n", - "5. **Show off your full profile** with the `get_full_profile()` method\n", - "\n", - "This isn't just about customization - it's about taking ownership of your learning journey in ML systems!\n", - "\n", - "**ASCII Art Customization Options:**\n", - "\n", - "**Option 1: Custom ASCII Art Parameter**\n", - "```python\n", - "my_profile = DeveloperProfile(\n", - " name=\"Your Name\",\n", - " ascii_art='''\n", - " Your Custom ASCII Art Here!\n", - " Maybe your initials, a logo, or something fun!\n", - " '''\n", - ")\n", - "```\n", - "\n", - "**Option 2: Edit the Default Flame File**\n", - "- Edit `tinytorch_flame.txt` in this directory\n", - "- Replace with your own ASCII art design\n", - "- All students using defaults will see your custom art!\n", - "\n", - "**ASCII Art Ideas:**\n", - "- Your initials in block letters\n", - "- A simple logo or symbol that represents you\n", - "- Your university mascot in ASCII\n", - "- A coding-themed design\n", - "- Something that motivates you!\n", - "\n", - "**Pro Tip**: The `tinytorch_flame.txt` file contains the beautiful default flame art. You can:\n", - "- Edit it directly for a personalized default\n", - "- Create your own `.txt` file and modify the code to load it\n", - "- Use online ASCII art generators for inspiration\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## Step 4: Try the Export Process\n", - "\n", - "Now let's export our code! In your terminal, run:\n", - "\n", - "```bash\n", - "python bin/tito.py sync --module setup\n", - "```\n", - "\n", - "This will export the code marked with `#| export` to `tinytorch/core/utils.py`.\n", - "\n", - "**What happens during export:**\n", - "1. nbdev scans this notebook for `#| export` cells\n", - "2. Extracts the Python code \n", - "3. Writes it to `tinytorch/core/utils.py` (because of `#| default_exp core.utils`)\n", - "4. Handles imports and dependencies automatically\n", - "\n", - "**\ud83d\udd0d Verification**: After export, check `tinytorch/core/utils.py` - you'll see your functions there with auto-generated headers pointing back to this notebook!\n", - "\n", - "**Note**: The export process will use the instructor solutions (from `#|hide` cells) so the package will have working implementations even if you haven't completed the exercises yet.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## Step 5: Run Tests\n", - "\n", - "After exporting, run the tests:\n", - "\n", - "```bash\n", - "python bin/tito.py test --module setup\n", - "```\n", - "\n", - "This will run all tests for the setup module and verify your implementation works correctly.\n", - "\n", - "## Step 6: Check Your Progress\n", - "\n", - "See your overall progress:\n", - "\n", - "```bash\n", - "python bin/tito.py info\n", - "```\n", - "\n", - "This shows which modules are complete and which are pending.\n", - "\"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "## \ud83c\udf89 Congratulations!\n", - "\n", - "You've learned the TinyTorch development workflow:\n", - "\n", - "1. \u2705 Write code in notebooks with `#| export`\n", - "2. \u2705 Export with `tito sync --module setup` \n", - "3. \u2705 Test with `tito test --module setup`\n", - "4. \u2705 Check progress with `tito info`\n", - "\n", - "**This is the rhythm you'll use for every module in TinyTorch.**\n", - "\n", - "### Next Steps\n", - "\n", - "Ready for the real work? Head to **Module 1: Tensor** where you'll build the core data structures that power everything else in TinyTorch.\n", - "\n", - "**Development Tips:**\n", - "- Always test your code in the notebook first\n", - "- Export frequently to catch issues early \n", - "- Read error messages carefully - they're designed to help\n", - "- When stuck, check if your code exports cleanly first\n", - "\n", - "Happy building! \ud83d\udd25\n", - "\"\"\"" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.8.0" - } + "cells": [ + { + "cell_type": "markdown", + "id": "aedd14ca", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "# Module 0: Setup - Tiny🔥Torch Development Workflow\n", + "\n", + "Welcome to TinyTorch! This module teaches you the development workflow you'll use throughout the course.\n", + "\n", + "## Learning Goals\n", + "- Understand the nbdev notebook-to-Python workflow\n", + "- Write your first TinyTorch code\n", + "- Run tests and use the CLI tools\n", + "- Get comfortable with the development rhythm\n", + "\n", + "## The TinyTorch Development Cycle\n", + "\n", + "1. **Write code** in this notebook using `#| export` \n", + "2. **Export code** with `python bin/tito.py sync --module setup`\n", + "3. **Run tests** with `python bin/tito.py test --module setup`\n", + "4. **Check progress** with `python bin/tito.py info`\n", + "\n", + "Let's get started!" + ] }, - "nbformat": 4, - "nbformat_minor": 4 -} \ No newline at end of file + { + "cell_type": "code", + "execution_count": null, + "id": "f4f0d964", + "metadata": {}, + "outputs": [], + "source": [ + "#| default_exp core.utils\n", + "\n", + "# Setup imports and environment\n", + "import sys\n", + "import platform\n", + "from datetime import datetime\n", + "import os\n", + "from pathlib import Path\n", + "\n", + "print(\"🔥 TinyTorch Development Environment\")\n", + "print(f\"Python {sys.version}\")\n", + "print(f\"Platform: {platform.system()} {platform.release()}\")\n", + "print(f\"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\")" + ] + }, + { + "cell_type": "markdown", + "id": "17abd963", + "metadata": { + "cell_marker": "\"\"\"", + "lines_to_next_cell": 1 + }, + "source": [ + "## Step 1: Understanding the Module → Package Structure\n", + "\n", + "**🎓 Teaching vs. 🔧 Building**: This course has two sides:\n", + "- **Teaching side**: You work in `modules/setup/setup_dev.ipynb` (learning-focused)\n", + "- **Building side**: Your code exports to `tinytorch/core/utils.py` (production package)\n", + "\n", + "**Key Concept**: The `#| default_exp core.utils` directive at the top tells nbdev to export all `#| export` cells to `tinytorch/core/utils.py`.\n", + "\n", + "This separation allows us to:\n", + "- Organize learning by **concepts** (modules) \n", + "- Organize code by **function** (package structure)\n", + "- Build a real ML framework while learning systematically\n", + "\n", + "Let's write a simple \"Hello World\" function with the `#| export` directive:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "447a2e89", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| export\n", + "def hello_tinytorch():\n", + " \"\"\"\n", + " A simple hello world function for TinyTorch.\n", + " \n", + " TODO: Implement this function to display TinyTorch ASCII art and welcome message.\n", + " Load the flame art from tinytorch_flame.txt file with graceful fallback.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + "\n", + "def add_numbers(a, b):\n", + " \"\"\"\n", + " Add two numbers together.\n", + " \n", + " TODO: Implement addition of two numbers.\n", + " This is the foundation of all mathematical operations in ML.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1201766", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| hide\n", + "#| export\n", + "def hello_tinytorch():\n", + " \"\"\"Display the TinyTorch ASCII art and welcome message.\"\"\"\n", + " try:\n", + " # Get the directory containing this file\n", + " current_dir = Path(__file__).parent\n", + " art_file = current_dir / \"tinytorch_flame.txt\"\n", + " \n", + " if art_file.exists():\n", + " with open(art_file, 'r') as f:\n", + " ascii_art = f.read()\n", + " print(ascii_art)\n", + " print(\"Tiny🔥Torch\")\n", + " print(\"Build ML Systems from Scratch!\")\n", + " else:\n", + " print(\"🔥 TinyTorch 🔥\")\n", + " print(\"Build ML Systems from Scratch!\")\n", + " except NameError:\n", + " # Handle case when running in notebook where __file__ is not defined\n", + " try:\n", + " art_file = Path(os.getcwd()) / \"tinytorch_flame.txt\"\n", + " if art_file.exists():\n", + " with open(art_file, 'r') as f:\n", + " ascii_art = f.read()\n", + " print(ascii_art)\n", + " print(\"Tiny🔥Torch\")\n", + " print(\"Build ML Systems from Scratch!\")\n", + " else:\n", + " print(\"🔥 TinyTorch 🔥\")\n", + " print(\"Build ML Systems from Scratch!\")\n", + " except:\n", + " print(\"🔥 TinyTorch 🔥\")\n", + " print(\"Build ML Systems from Scratch!\")\n", + "\n", + "def add_numbers(a, b):\n", + " \"\"\"Add two numbers together.\"\"\"\n", + " return a + b" + ] + }, + { + "cell_type": "markdown", + "id": "9ecf0bcb", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "### 🧪 Test Your Implementation\n", + "\n", + "Once you implement the functions above, run this cell to test them:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9907a7a9", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the functions in the notebook (will fail until implemented)\n", + "try:\n", + " print(\"Testing hello_tinytorch():\")\n", + " hello_tinytorch()\n", + " print()\n", + " print(\"Testing add_numbers():\")\n", + " print(f\"2 + 3 = {add_numbers(2, 3)}\")\n", + "except NotImplementedError as e:\n", + " print(f\"⚠️ {e}\")\n", + " print(\"Implement the functions above first!\")" + ] + }, + { + "cell_type": "markdown", + "id": "97702bd5", + "metadata": { + "cell_marker": "\"\"\"", + "lines_to_next_cell": 1 + }, + "source": [ + "## Step 2: A Simple Class\n", + "\n", + "Let's create a simple class that will help us understand system information. This is still basic, but shows how to structure classes in TinyTorch." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eedfc9c5", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| export\n", + "class SystemInfo:\n", + " \"\"\"\n", + " Simple system information class.\n", + " \n", + " TODO: Implement this class to collect and display system information.\n", + " \"\"\"\n", + " \n", + " def __init__(self):\n", + " \"\"\"\n", + " Initialize system information collection.\n", + " \n", + " TODO: Collect Python version, platform, and machine information.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def __str__(self):\n", + " \"\"\"\n", + " Return human-readable system information.\n", + " \n", + " TODO: Format system info as a readable string.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def is_compatible(self):\n", + " \"\"\"\n", + " Check if system meets minimum requirements.\n", + " \n", + " TODO: Check if Python version is >= 3.8\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8e11e12", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| hide\n", + "#| export\n", + "class SystemInfo:\n", + " \"\"\"Simple system information class.\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.python_version = sys.version_info\n", + " self.platform = platform.system()\n", + " self.machine = platform.machine()\n", + " \n", + " def __str__(self):\n", + " return f\"Python {self.python_version.major}.{self.python_version.minor} on {self.platform} ({self.machine})\"\n", + " \n", + " def is_compatible(self):\n", + " \"\"\"Check if system meets minimum requirements.\"\"\"\n", + " return self.python_version >= (3, 8)" + ] + }, + { + "cell_type": "markdown", + "id": "9d15a078", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "### 🧪 Test Your SystemInfo Class\n", + "\n", + "Once you implement the SystemInfo class above, run this cell to test it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb3ae5be", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the SystemInfo class (will fail until implemented)\n", + "try:\n", + " print(\"Testing SystemInfo class:\")\n", + " info = SystemInfo()\n", + " print(f\"System: {info}\")\n", + " print(f\"Compatible: {info.is_compatible()}\")\n", + "except NotImplementedError as e:\n", + " print(f\"⚠️ {e}\")\n", + " print(\"Implement the SystemInfo class above first!\")" + ] + }, + { + "cell_type": "markdown", + "id": "c5242239", + "metadata": { + "cell_marker": "\"\"\"", + "lines_to_next_cell": 1 + }, + "source": [ + "## Step 3: Developer Personalization\n", + "\n", + "Let's make TinyTorch yours! Create a developer profile that will identify you throughout your ML systems journey." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b78dc40", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| export\n", + "class DeveloperProfile:\n", + " \"\"\"\n", + " Developer profile for personalizing TinyTorch experience.\n", + " \n", + " TODO: Implement this class to store and display developer information.\n", + " Default to course instructor but allow students to personalize.\n", + " \"\"\"\n", + " \n", + " @staticmethod\n", + " def _load_default_flame():\n", + " \"\"\"\n", + " Load the default TinyTorch flame ASCII art from file.\n", + " \n", + " TODO: Implement file loading for tinytorch_flame.txt with fallback.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def __init__(self, name=\"Vijay Janapa Reddi\", affiliation=\"Harvard University\", \n", + " email=\"vj@eecs.harvard.edu\", github_username=\"profvjreddi\", ascii_art=None):\n", + " \"\"\"\n", + " Initialize developer profile.\n", + " \n", + " TODO: Store developer information with sensible defaults.\n", + " Students should be able to customize this with their own info and ASCII art.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def __str__(self):\n", + " \"\"\"\n", + " Return formatted developer information.\n", + " \n", + " TODO: Format developer info as a professional signature with optional ASCII art.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def get_signature(self):\n", + " \"\"\"\n", + " Get a short signature for code headers.\n", + " \n", + " TODO: Return a concise signature like \"Built by Name (@github)\"\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")\n", + " \n", + " def get_ascii_art(self):\n", + " \"\"\"\n", + " Get ASCII art for the profile.\n", + " \n", + " TODO: Return custom ASCII art or default flame loaded from file.\n", + " \"\"\"\n", + " raise NotImplementedError(\"Student implementation required\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70f54331", + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "#| hide\n", + "#| export\n", + "class DeveloperProfile:\n", + " \"\"\"Developer profile for personalizing TinyTorch experience.\"\"\"\n", + " \n", + " @staticmethod\n", + " def _load_default_flame():\n", + " \"\"\"Load the default TinyTorch flame ASCII art from file.\"\"\"\n", + " try:\n", + " # Try to load from the same directory as this module\n", + " try:\n", + " # Try to get the directory of the current file\n", + " current_dir = os.path.dirname(__file__)\n", + " except NameError:\n", + " # If __file__ is not defined (e.g., in notebook), use current directory\n", + " current_dir = os.getcwd()\n", + " \n", + " flame_path = os.path.join(current_dir, 'tinytorch_flame.txt')\n", + " \n", + " with open(flame_path, 'r', encoding='utf-8') as f:\n", + " flame_art = f.read()\n", + " \n", + " # Add the Tiny🔥Torch text below the flame\n", + " return f\"\"\"{flame_art}\n", + " \n", + " Tiny🔥Torch\n", + " Build ML Systems from Scratch!\n", + " \"\"\"\n", + " except (FileNotFoundError, IOError):\n", + " # Fallback to simple flame if file not found\n", + " return \"\"\"\n", + " 🔥 TinyTorch Developer 🔥\n", + " . . . . . .\n", + " . . . . . .\n", + " . . . . . . .\n", + " . . . . . . . .\n", + " . . . . . . . . .\n", + " . . . . . . . . . .\n", + " . . . . . . . . . . .\n", + " . . . . . . . . . . . .\n", + " . . . . . . . . . . . . .\n", + ". . . . . . . . . . . . . .\n", + " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ \\\\ / / / / / /\n", + " \\\\ \\\\ / / / / / /\n", + " \\\\ / / / / / /\n", + " \\\\/ / / / / /\n", + " \\\\/ / / / /\n", + " \\\\/ / / /\n", + " \\\\/ / /\n", + " \\\\/ /\n", + " \\\\/\n", + " \n", + " Tiny🔥Torch\n", + " Build ML Systems from Scratch!\n", + " \"\"\"\n", + " \n", + " def __init__(self, name=\"Vijay Janapa Reddi\", affiliation=\"Harvard University\", \n", + " email=\"vj@eecs.harvard.edu\", github_username=\"profvjreddi\", ascii_art=None):\n", + " self.name = name\n", + " self.affiliation = affiliation\n", + " self.email = email\n", + " self.github_username = github_username\n", + " self.ascii_art = ascii_art or self._load_default_flame()\n", + " \n", + " def __str__(self):\n", + " return f\"👨‍💻 {self.name} | {self.affiliation} | @{self.github_username}\"\n", + " \n", + " def get_signature(self):\n", + " \"\"\"Get a short signature for code headers.\"\"\"\n", + " return f\"Built by {self.name} (@{self.github_username})\"\n", + " \n", + " def get_ascii_art(self):\n", + " \"\"\"Get ASCII art for the profile.\"\"\"\n", + " return self.ascii_art\n", + " \n", + " def get_full_profile(self):\n", + " \"\"\"Get complete profile with ASCII art.\"\"\"\n", + " return f\"\"\"{self.ascii_art}\n", + " \n", + "👨‍💻 Developer: {self.name}\n", + "🏛️ Affiliation: {self.affiliation}\n", + "📧 Email: {self.email}\n", + "🐙 GitHub: @{self.github_username}\n", + "🔥 Ready to build ML systems from scratch!\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "id": "eb7bd077", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "### 🧪 Test Your Developer Profile\n", + "\n", + "Customize your developer profile! Replace the default information with your own:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19ddd178", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the DeveloperProfile class\n", + "try:\n", + " print(\"Testing DeveloperProfile (with defaults):\")\n", + " # Default profile (instructor)\n", + " default_profile = DeveloperProfile()\n", + " print(f\"Profile: {default_profile}\")\n", + " print(f\"Signature: {default_profile.get_signature()}\")\n", + " print()\n", + " \n", + " print(\"🎨 ASCII Art Preview:\")\n", + " print(default_profile.get_ascii_art())\n", + " print()\n", + " \n", + " print(\"🔥 Full Profile Display:\")\n", + " print(default_profile.get_full_profile())\n", + " print()\n", + " \n", + " # TODO: Students should customize this with their own information!\n", + " print(\"🎯 YOUR TURN: Create your own profile!\")\n", + " print(\"Uncomment and modify the lines below:\")\n", + " print(\"# my_profile = DeveloperProfile(\")\n", + " print(\"# name='Your Name',\")\n", + " print(\"# affiliation='Your University/Company',\")\n", + " print(\"# email='your.email@example.com',\")\n", + " print(\"# github_username='yourgithub',\")\n", + " print(\"# ascii_art='''\")\n", + " print(\"# Your Custom ASCII Art Here!\")\n", + " print(\"# Maybe your initials, a logo, or something fun!\")\n", + " print(\"# '''\")\n", + " print(\"# )\")\n", + " print(\"# print(f'My Profile: {my_profile}')\")\n", + " print(\"# print(f'My Signature: {my_profile.get_signature()}')\")\n", + " print(\"# print(my_profile.get_full_profile())\")\n", + " \n", + "except NotImplementedError as e:\n", + " print(f\"⚠️ {e}\")\n", + " print(\"Implement the DeveloperProfile class above first!\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c3e1666", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "### 🎨 Personalization Challenge\n", + "\n", + "**For Students**: Make TinyTorch truly yours by:\n", + "\n", + "1. **Update your profile** in the cell above with your real information\n", + "2. **Create custom ASCII art** - your initials, a simple logo, or something that represents you\n", + "3. **Customize the flame file** - edit `tinytorch_flame.txt` to create your own default art\n", + "4. **Add your signature** to code you write throughout the course\n", + "5. **Show off your full profile** with the `get_full_profile()` method\n", + "\n", + "This isn't just about customization - it's about taking ownership of your learning journey in ML systems!\n", + "\n", + "**ASCII Art Customization Options:**\n", + "\n", + "**Option 1: Custom ASCII Art Parameter**\n", + "```python\n", + "my_profile = DeveloperProfile(\n", + " name=\"Your Name\",\n", + " ascii_art='''\n", + " Your Custom ASCII Art Here!\n", + " Maybe your initials, a logo, or something fun!\n", + " '''\n", + ")\n", + "```\n", + "\n", + "**Option 2: Edit the Default Flame File**\n", + "- Edit `tinytorch_flame.txt` in this directory\n", + "- Replace with your own ASCII art design\n", + "- All students using defaults will see your custom art!\n", + "\n", + "**ASCII Art Ideas:**\n", + "- Your initials in block letters\n", + "- A simple logo or symbol that represents you\n", + "- Your university mascot in ASCII\n", + "- A coding-themed design\n", + "- Something that motivates you!\n", + "\n", + "**Pro Tip**: The `tinytorch_flame.txt` file contains the beautiful default flame art. You can:\n", + "- Edit it directly for a personalized default\n", + "- Create your own `.txt` file and modify the code to load it\n", + "- Use online ASCII art generators for inspiration" + ] + }, + { + "cell_type": "markdown", + "id": "e994a6a3", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "## Step 4: Try the Export Process\n", + "\n", + "Now let's export our code! In your terminal, run:\n", + "\n", + "```bash\n", + "python bin/tito.py sync --module setup\n", + "```\n", + "\n", + "This will export the code marked with `#| export` to `tinytorch/core/utils.py`.\n", + "\n", + "**What happens during export:**\n", + "1. nbdev scans this notebook for `#| export` cells\n", + "2. Extracts the Python code \n", + "3. Writes it to `tinytorch/core/utils.py` (because of `#| default_exp core.utils`)\n", + "4. Handles imports and dependencies automatically\n", + "\n", + "**🔍 Verification**: After export, check `tinytorch/core/utils.py` - you'll see your functions there with auto-generated headers pointing back to this notebook!\n", + "\n", + "**Note**: The export process will use the instructor solutions (from `#|hide` cells) so the package will have working implementations even if you haven't completed the exercises yet." + ] + }, + { + "cell_type": "markdown", + "id": "79e6a76f", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "## Step 5: Run Tests\n", + "\n", + "After exporting, run the tests:\n", + "\n", + "```bash\n", + "python bin/tito.py test --module setup\n", + "```\n", + "\n", + "This will run all tests for the setup module and verify your implementation works correctly.\n", + "\n", + "## Step 6: Check Your Progress\n", + "\n", + "See your overall progress:\n", + "\n", + "```bash\n", + "python bin/tito.py info\n", + "```\n", + "\n", + "This shows which modules are complete and which are pending." + ] + }, + { + "cell_type": "markdown", + "id": "868b2ae5", + "metadata": { + "cell_marker": "\"\"\"" + }, + "source": [ + "## 🎉 Congratulations!\n", + "\n", + "You've learned the TinyTorch development workflow:\n", + "\n", + "1. ✅ Write code in notebooks with `#| export`\n", + "2. ✅ Export with `tito sync --module setup` \n", + "3. ✅ Test with `tito test --module setup`\n", + "4. ✅ Check progress with `tito info`\n", + "\n", + "**This is the rhythm you'll use for every module in TinyTorch.**\n", + "\n", + "### Next Steps\n", + "\n", + "Ready for the real work? Head to **Module 1: Tensor** where you'll build the core data structures that power everything else in TinyTorch.\n", + "\n", + "**Development Tips:**\n", + "- Always test your code in the notebook first\n", + "- Export frequently to catch issues early \n", + "- Read error messages carefully - they're designed to help\n", + "- When stuck, check if your code exports cleanly first\n", + "\n", + "Happy building! 🔥" + ] + } + ], + "metadata": { + "jupytext": { + "main_language": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tito/commands/notebooks.py b/tito/commands/notebooks.py index 8556cb19..491df097 100644 --- a/tito/commands/notebooks.py +++ b/tito/commands/notebooks.py @@ -1,5 +1,5 @@ """ -Notebooks command for building Jupyter notebooks from Python files. +Notebooks command for building Jupyter notebooks from Python files using Jupytext. """ import subprocess @@ -15,7 +15,7 @@ from .base import BaseCommand from ..core.exceptions import ExecutionError, ModuleNotFoundError class NotebooksCommand(BaseCommand): - """Command to build Jupyter notebooks from Python files.""" + """Command to build Jupyter notebooks from Python files using Jupytext.""" @property def name(self) -> str: @@ -62,33 +62,31 @@ class NotebooksCommand(BaseCommand): return dev_files def _convert_file(self, dev_file: Path) -> Tuple[bool, str]: - """Convert a single Python file to notebook.""" + """Convert a single Python file to notebook using Jupytext.""" try: - py_to_notebook_tool = self.config.bin_dir / "py_to_notebook.py" + # Use Jupytext to convert Python file to notebook result = subprocess.run([ - sys.executable, str(py_to_notebook_tool), str(dev_file) - ], capture_output=True, text=True, timeout=30) + "jupytext", "--to", "notebook", str(dev_file) + ], capture_output=True, text=True, timeout=30, cwd=dev_file.parent) if result.returncode == 0: - # Extract success message from the tool output - output_lines = result.stdout.strip().split('\n') - success_msg = output_lines[-1] if output_lines else f"{dev_file.name} → {dev_file.with_suffix('.ipynb').name}" - # Clean up the message - clean_msg = success_msg.replace('✅ ', '').replace('Converted ', '') - return True, clean_msg + notebook_file = dev_file.with_suffix('.ipynb') + return True, f"{dev_file.name} → {notebook_file.name}" else: error_msg = result.stderr.strip() if result.stderr.strip() else "Conversion failed" return False, error_msg except subprocess.TimeoutExpired: return False, "Conversion timed out" + except FileNotFoundError: + return False, "Jupytext not found. Install with: pip install jupytext" except Exception as e: return False, f"Error: {str(e)}" def run(self, args: Namespace) -> int: """Execute the notebooks command.""" self.console.print(Panel( - "📓 Building Notebooks from Python Files", + "📓 Building Notebooks from Python Files (using Jupytext)", title="Notebook Generation", border_style="bright_cyan" )) @@ -149,8 +147,8 @@ class NotebooksCommand(BaseCommand): summary_text.append("\n💡 Next steps:\n", style="bold yellow") summary_text.append(" • Open notebooks with: jupyter lab\n", style="white") summary_text.append(" • Work interactively in the notebooks\n", style="white") - summary_text.append(" • Export code with: tito sync\n", style="white") - summary_text.append(" • Run tests with: tito test\n", style="white") + summary_text.append(" • Export code with: tito package sync\n", style="white") + summary_text.append(" • Run tests with: tito module test\n", style="white") border_style = "green" if error_count == 0 else "yellow" self.console.print(Panel( diff --git a/tito/tools/__init__.py b/tito/tools/__init__.py index 091c9b3f..6bae9a54 100644 --- a/tito/tools/__init__.py +++ b/tito/tools/__init__.py @@ -4,6 +4,6 @@ CLI Tools package. Contains utility tools used by the CLI commands. """ -from .py_to_notebook import convert_py_to_notebook +# No tools currently - py_to_notebook removed in favor of Jupytext -__all__ = ['convert_py_to_notebook'] \ No newline at end of file +__all__ = [] \ No newline at end of file diff --git a/tito/tools/py_to_notebook.py b/tito/tools/py_to_notebook.py deleted file mode 100755 index 61357509..00000000 --- a/tito/tools/py_to_notebook.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 -""" -Convert Python files with cell markers to Jupyter notebooks. - -Usage: - python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py - python3 bin/py_to_notebook.py modules/tensor/tensor_dev.py --output custom_name.ipynb -""" - -import argparse -import json -import re -import sys -from pathlib import Path - -def convert_py_to_notebook(py_file: Path, output_file: Path = None): - """Convert Python file with cell markers to notebook.""" - - if not py_file.exists(): - print(f"❌ File not found: {py_file}") - return False - - # Read the Python file - with open(py_file, 'r') as f: - content = f.read() - - # Split into cells based on # %% markers - cells = re.split(r'^# %%.*$', content, flags=re.MULTILINE) - cells = [cell.strip() for cell in cells if cell.strip()] - - # Create notebook structure - notebook = { - 'cells': [], - 'metadata': { - 'kernelspec': { - 'display_name': 'Python 3', - 'language': 'python', - 'name': 'python3' - }, - 'language_info': { - 'name': 'python', - 'version': '3.8.0' - } - }, - 'nbformat': 4, - 'nbformat_minor': 4 - } - - for i, cell_content in enumerate(cells): - if not cell_content: - continue - - # Check if this is a markdown cell - if cell_content.startswith('# ') and '\n' in cell_content: - lines = cell_content.split('\n') - if lines[0].startswith('# ') and not any(line.strip() and not line.startswith('#') for line in lines[:5]): - # This looks like a markdown cell - cell = { - 'cell_type': 'markdown', - 'metadata': {}, - 'source': [] - } - - for line in lines: - if line.startswith('# '): - cell['source'].append(line[2:] + '\n') - elif line.startswith('#'): - cell['source'].append(line[1:] + '\n') - elif line.strip() == '': - cell['source'].append('\n') - - notebook['cells'].append(cell) - continue - - # Code cell - cell = { - 'cell_type': 'code', - 'execution_count': None, - 'metadata': {}, - 'outputs': [], - 'source': [] - } - - for line in cell_content.split('\n'): - cell['source'].append(line + '\n') - - # Remove trailing newline from last line - if cell['source'] and cell['source'][-1].endswith('\n'): - cell['source'][-1] = cell['source'][-1][:-1] - - notebook['cells'].append(cell) - - # Determine output file - if output_file is None: - output_file = py_file.with_suffix('.ipynb') - - # Write notebook - output_file.parent.mkdir(parents=True, exist_ok=True) - with open(output_file, 'w') as f: - json.dump(notebook, f, indent=2) - - print(f"✅ Converted {py_file} → {output_file}") - return True - -def main(): - parser = argparse.ArgumentParser(description="Convert Python files to Jupyter notebooks") - parser.add_argument('input_file', type=Path, help='Input Python file') - parser.add_argument('--output', '-o', type=Path, help='Output notebook file') - parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output') - - args = parser.parse_args() - - success = convert_py_to_notebook(args.input_file, args.output) - - if not success: - sys.exit(1) - - if args.verbose: - print("🎉 Conversion complete!") - -if __name__ == "__main__": - main() \ No newline at end of file