diff --git a/assignments/source/00_setup/setup_dev.py b/assignments/source/00_setup/setup_dev.py index bbff237e..5c6e2829 100644 --- a/assignments/source/00_setup/setup_dev.py +++ b/assignments/source/00_setup/setup_dev.py @@ -113,7 +113,8 @@ hello_tinytorch() # Should print welcome message without crashing ``` """ -# %% nbgrader={"grade": false, "grade_id": "hello_function", "locked": false, "schema_version": 3, "solution": true, "task": false} +# %% +# nbgrader: grade, solution #| export def hello_tinytorch(): """ @@ -124,14 +125,7 @@ def hello_tinytorch(): 2. If the file doesn't exist, display a simple text banner 3. Print "TinyTorch" and "Build ML Systems from Scratch!" 4. Handle any exceptions gracefully - - Hints: - - Use Path('filename').exists() to check if file exists - - Use try/except to handle file reading errors - - Use .strip() to remove extra whitespace - - Always print the tagline regardless of file status """ - ### BEGIN SOLUTION try: # Try to read the ASCII art file flame_file = Path('tinytorch_flame.txt') @@ -145,7 +139,36 @@ def hello_tinytorch(): # Always print the tagline print("Build ML Systems from Scratch!") - ### END SOLUTION + +# %% +# nbgrader: tests +# Test hello_tinytorch function +def test_hello_tinytorch(): + """Test that hello_tinytorch runs without crashing.""" + import io + import sys + + # Capture output + old_stdout = sys.stdout + sys.stdout = captured_output = io.StringIO() + + try: + hello_tinytorch() + output = captured_output.getvalue() + + # Should have some output + assert len(output.strip()) > 0, "Function should produce output" + + # Should contain the tagline + assert "Build ML Systems from Scratch!" in output, "Should contain tagline" + + # Should contain either ASCII art or simple banner + assert "TinyTorch" in output or len(output.split('\n')) > 2, "Should contain banner" + + finally: + sys.stdout = old_stdout + +test_hello_tinytorch() # Run the test # %% [markdown] """ @@ -161,7 +184,7 @@ def hello_tinytorch(): ### πŸ’‘ Approach & Hints This problem demonstrates **multiple solution blocks** within one function: -- Each step has its own `### BEGIN SOLUTION` / `### END SOLUTION` block +- Each step has its own solution block - Complete each step before moving to the next - Use descriptive variable names as suggested in comments - Follow the math carefully: `(a+2) + (b+2) = a+b+4`, then `Γ—10` @@ -197,544 +220,516 @@ print(f"Result: {result}") # Should print: Result: 110 **Note**: This demonstrates how NBGrader can guide you through complex functions step-by-step! """ -# %% nbgrader={"grade": false, "grade_id": "multi_step_function", "locked": false, "schema_version": 3, "solution": true, "task": false} +# %% +# nbgrader: grade, solution #| export def complex_calculation(a, b): """ Perform a multi-step calculation with guided implementation. + This function demonstrates multiple solution blocks: + 1. Add 2 to both input variables + 2. Sum the modified variables + 3. Multiply by 10 + Args: - a: First number - b: Second number + a (int): First number + b (int): Second number Returns: - Result of multi-step calculation - - This function demonstrates multiple solution blocks within one function. - Complete each step in order using the suggested variable names. + int: Result of (a+2) + (b+2), then multiplied by 10 """ - # Step 1: Add 2 to each input variable - # Create variables: a_plus_2 and b_plus_2 - # Hint: Simple addition - a_plus_2 = a + ? - ### BEGIN SOLUTION + # Step 1: Add 2 to each variable a_plus_2 = a + 2 b_plus_2 = b + 2 - ### END SOLUTION - # Step 2: Sum everything - # Create variable: everything_summed - # Hint: Add the two variables from Step 1 - ### BEGIN SOLUTION + # Step 2: Sum the modified variables everything_summed = a_plus_2 + b_plus_2 - ### END SOLUTION - # Step 3: Multiply your previous result by 10 - # Create variable: everything_summed_times_10 - # Hint: You can use * operator (np.multiply is overkill and will make people hate you πŸ˜„) - ### BEGIN SOLUTION + # Step 3: Multiply by 10 everything_summed_times_10 = everything_summed * 10 - ### END SOLUTION return everything_summed_times_10 +# %% +# nbgrader: tests +# Test complex_calculation function +assert complex_calculation(3, 4) == 110, "complex_calculation(3, 4) should equal 110" +assert complex_calculation(1, 2) == 70, "complex_calculation(1, 2) should equal 70" +assert complex_calculation(0, 0) == 40, "complex_calculation(0, 0) should equal 40" +assert complex_calculation(-1, 1) == 40, "complex_calculation(-1, 1) should equal 40" +print("βœ… complex_calculation tests passed!") + # %% [markdown] """ ## Problem 3: Basic Math Function (5 points) βž• -**Goal**: Create a simple addition function to verify our basic workflow. +**Goal**: Create a simple function that adds two numbers. ### πŸ“ Requirements -- Accept two parameters (a and b) +- Take two parameters: `a` and `b` - Return their sum -- Handle both integers and floats -- Keep it simple - this is a workflow verification! +- Handle any numeric types (int, float) ### πŸ’‘ Approach & Hints -- This is intentionally simple - focus on getting the workflow right -- Use the `+` operator (works for int, float, and even some other types) -- No error checking needed - assume valid inputs -- One line of code inside the function is enough! +1. **Simple addition** - just use the `+` operator +2. **No type conversion needed** - Python handles int + float automatically +3. **One line implementation** - this is straightforward! ### 🎯 Expected Behavior ```python -add_numbers(3, 4) # Returns: 7 -add_numbers(2.5, 1.5) # Returns: 4.0 -add_numbers(-1, 1) # Returns: 0 -add_numbers(0, 0) # Returns: 0 -``` - -### πŸ§ͺ Quick Test -```python -result = add_numbers(2.5, 1.5) -print(f"2.5 + 1.5 = {result}") # Should print: 2.5 + 1.5 = 4.0 +add_numbers(2, 3) # Returns: 5 +add_numbers(1.5, 2.5) # Returns: 4.0 +add_numbers(-1, 1) # Returns: 0 ``` ### 🚨 Common Pitfalls -- ❌ Overthinking it - this is meant to be simple! -- ❌ Adding unnecessary error checking -- ❌ Using complex math when simple + works fine +- ❌ Overthinking it - this is really simple! +- ❌ Forgetting to return the result +- ❌ Trying to do type conversion (not needed) + +### πŸ§ͺ Quick Test +```python +result = add_numbers(5, 7) +print(f"5 + 7 = {result}") # Should print: 5 + 7 = 12 +``` """ -# %% nbgrader={"grade": false, "grade_id": "add_function", "locked": false, "schema_version": 3, "solution": true, "task": false} +# %% +# nbgrader: grade, solution #| export def add_numbers(a, b): """ Add two numbers together. Args: - a: First number (int or float) - b: Second number (int or float) + a (int or float): First number + b (int or float): Second number Returns: - Sum of a and b - - Hint: This is intentionally simple - just use the + operator! + int or float: Sum of a and b """ - ### BEGIN SOLUTION return a + b - ### END SOLUTION + +# %% +# nbgrader: tests +# Test add_numbers function +assert add_numbers(2, 3) == 5, "add_numbers(2, 3) should equal 5" +assert add_numbers(1.5, 2.5) == 4.0, "add_numbers(1.5, 2.5) should equal 4.0" +assert add_numbers(-1, 1) == 0, "add_numbers(-1, 1) should equal 0" +assert add_numbers(0, 0) == 0, "add_numbers(0, 0) should equal 0" +print("βœ… add_numbers tests passed!") # %% [markdown] """ -## Problem 4: System Information Class (20 points) πŸ–₯️ +## Problem 4: System Info Class (20 points) πŸ–₯️ -**Goal**: Create a class that collects and displays system information for debugging and compatibility. +**Goal**: Create a class that gathers and displays system information. ### πŸ“ Requirements -- **`__init__`**: Collect Python version, platform, and machine architecture -- **`__str__`**: Return formatted system info as "Python X.Y.Z on Platform (Architecture)" -- **`is_compatible()`**: Check if Python version >= 3.8 -- Store information as instance variables (self.*) +- Create a class called `SystemInfo` +- `__init__()`: Store system information (Python version, platform, timestamp) +- `__str__()`: Return a formatted string with all system info +- `is_compatible()`: Return True if Python version >= 3.8 ### πŸ’‘ Approach & Hints - -#### For `__init__`: -- Use `sys.version_info` to get Python version as tuple -- Convert to string: `f"{major}.{minor}.{micro}"` -- Use `platform.system()` for OS name (Darwin, Windows, Linux) -- Use `platform.machine()` for architecture (arm64, x86_64, etc.) - -#### For `__str__`: -- Return exact format: `"Python {version} on {platform} ({machine})"` -- Use f-strings for clean formatting -- Example: `"Python 3.9.7 on Darwin (arm64)"` - -#### For `is_compatible()`: -- Compare `sys.version_info[:2]` with `(3, 8)` -- Use `>=` operator on tuples -- Return boolean (True/False) +1. **Use sys.version** - gets Python version string +2. **Use platform.system()** - gets OS name (Windows, Darwin, Linux) +3. **Use datetime.now()** - gets current timestamp +4. **Parse version string** - extract major.minor version for compatibility check +5. **String formatting** - use f-strings for clean output ### 🎯 Expected Behavior ```python info = SystemInfo() -print(info) # "Python 3.9.7 on Darwin (arm64)" -print(info.is_compatible()) # True (if Python >= 3.8) +print(info) +# Output: +# TinyTorch System Info +# Python Version: 3.9.7 +# Platform: Darwin +# Timestamp: 2024-01-15 10:30:45.123456 -# Access individual properties -print(info.python_version) # "3.9.7" -print(info.platform) # "Darwin" -print(info.machine) # "arm64" +print(info.is_compatible()) # True (if Python >= 3.8) ``` +### 🚨 Common Pitfalls +- ❌ Not storing data in __init__ (computing it in __str__ instead) +- ❌ Version parsing errors (handling edge cases in version string) +- ❌ Incorrect string formatting (missing newlines or proper spacing) +- ❌ Not using instance variables (self.variable_name) + ### πŸ§ͺ Quick Test ```python info = SystemInfo() -print(f"System: {info}") -print(f"Compatible: {info.is_compatible()}") +print(f"Compatible: {info.is_compatible()}") # Should print: Compatible: True +print(info) # Should print formatted system info ``` - -### 🚨 Common Pitfalls -- ❌ Not storing as instance variables (using local variables instead) -- ❌ Wrong string formatting in `__str__` -- ❌ Using `sys.version` (string) instead of `sys.version_info` (tuple) -- ❌ Hardcoding version check instead of using `sys.version_info` """ -# %% nbgrader={"grade": false, "grade_id": "systeminfo_class", "locked": false, "schema_version": 3, "solution": true, "task": false} -#| export +# %% +# nbgrader: grade, solution +#| export class SystemInfo: """ - A class for collecting and displaying system information. + A class for gathering and displaying system information. - This class helps with debugging and compatibility checking. + This class collects Python version, platform, and timestamp information + when instantiated and provides methods to display and check compatibility. """ def __init__(self): - """ - Initialize the SystemInfo object. - Collect Python version, platform, and machine information. - - Hints: - - Use sys.version_info to get version tuple - - Convert version to string: f"{major}.{minor}.{micro}" - - Use platform.system() and platform.machine() - - Store as self.attribute_name - """ - ### BEGIN SOLUTION - # Get Python version info - version_info = sys.version_info - self.python_version = f"{version_info.major}.{version_info.minor}.{version_info.micro}" - - # Get platform information + """Initialize system info by collecting current system data.""" + self.python_version = sys.version.split()[0] # Get clean version string self.platform = platform.system() - - # Get machine architecture - self.machine = platform.machine() - ### END SOLUTION + self.timestamp = datetime.now() def __str__(self): - """ - Return a formatted string representation of system information. - Format: "Python X.Y.Z on Platform (Architecture)" - - Hints: - - Use f-string formatting - - Exact format: "Python {version} on {platform} ({machine})" - - Example: "Python 3.9.7 on Darwin (arm64)" - """ - ### BEGIN SOLUTION - return f"Python {self.python_version} on {self.platform} ({self.machine})" - ### END SOLUTION + """Return formatted system information string.""" + return f"""TinyTorch System Info +Python Version: {self.python_version} +Platform: {self.platform} +Timestamp: {self.timestamp}""" def is_compatible(self): - """ - Check if the Python version is compatible (>= 3.8). - Returns True if compatible, False otherwise. - - Hints: - - Use sys.version_info[:2] to get (major, minor) tuple - - Compare with (3, 8) using >= operator - - Tuple comparison works element by element - """ - ### BEGIN SOLUTION - return sys.version_info[:2] >= (3, 8) - ### END SOLUTION + """Check if Python version is compatible (>= 3.8).""" + try: + version_parts = self.python_version.split('.') + major = int(version_parts[0]) + minor = int(version_parts[1]) + return major > 3 or (major == 3 and minor >= 8) + except (ValueError, IndexError): + return False + +# %% +# nbgrader: tests +# Test SystemInfo class +info = SystemInfo() + +# Test that attributes exist +assert hasattr(info, 'python_version'), "SystemInfo should have python_version attribute" +assert hasattr(info, 'platform'), "SystemInfo should have platform attribute" +assert hasattr(info, 'timestamp'), "SystemInfo should have timestamp attribute" + +# Test string representation +info_str = str(info) +assert "TinyTorch System Info" in info_str, "String should contain title" +assert "Python Version:" in info_str, "String should contain Python version" +assert "Platform:" in info_str, "String should contain platform" +assert "Timestamp:" in info_str, "String should contain timestamp" + +# Test compatibility check +compatibility = info.is_compatible() +assert isinstance(compatibility, bool), "is_compatible should return boolean" + +print("βœ… SystemInfo tests passed!") # %% [markdown] """ ## Problem 5: Developer Profile Class (30 points) πŸ‘¨β€πŸ’» -**Goal**: Create a class to manage developer profiles with multiple methods and data handling. +**Goal**: Create an advanced class representing a developer profile with multiple methods. ### πŸ“ Requirements -- **`__init__`**: Store developer information (name, email, affiliation, specialization) -- **`__str__`**: Return basic representation as "Name (email)" -- **`get_signature()`**: Return formatted signature with name, affiliation, and specialization -- **`get_profile_info()`**: Return all info as a dictionary +- Create a class called `DeveloperProfile` +- `__init__()`: Accept name, email, affiliation, specialization (with defaults) +- `__str__()`: Return a formatted profile card +- `get_signature()`: Return a signature line with name and specialization +- `get_profile_info()`: Return a dictionary with all profile information ### πŸ’‘ Approach & Hints - -#### For `__init__`: -- Use default parameters as shown in the function signature -- Store all parameters as instance variables: `self.name = name`, etc. -- Default values make the class easy to use: `DeveloperProfile()` works! - -#### For `__str__`: -- Simple format: `"Name (email)"` -- Use f-string: `f"{self.name} ({self.email})"` -- Example: `"Alice (alice@example.com)"` - -#### For `get_signature()`: -- Multi-line string with `\\n` separators -- Format: `"Name\\nAffiliation\\nSpecialization: specialization"` -- Example: - ``` - Alice - University - Specialization: Neural Networks - ``` - -#### For `get_profile_info()`: -- Return dictionary with all four attributes -- Keys: 'name', 'email', 'affiliation', 'specialization' -- Values: the corresponding instance variable values +1. **Default parameters** - use defaults in __init__ method signature +2. **Instance variables** - store all parameters as self.variable_name +3. **String formatting** - create a nice "business card" format +4. **Dictionary creation** - return all instance variables as key-value pairs +5. **Method chaining** - each method should work independently ### 🎯 Expected Behavior ```python -# Using defaults -profile = DeveloperProfile() -print(profile) # "Student (student@example.com)" - -# Custom values -dev = DeveloperProfile("Alice", "alice@example.com", "University", "Neural Networks") -print(dev) # "Alice (alice@example.com)" +dev = DeveloperProfile("Alice", "alice@example.com", "MIT", "Deep Learning") +print(dev) +# Output: +# ═══════════════════════════════════════ +# πŸš€ DEVELOPER PROFILE +# ═══════════════════════════════════════ +# Name: Alice +# Email: alice@example.com +# Affiliation: MIT +# Specialization: Deep Learning +# ═══════════════════════════════════════ print(dev.get_signature()) -# Alice -# University -# Specialization: Neural Networks +# Output: Alice - Deep Learning Specialist -print(dev.get_profile_info()) -# {'name': 'Alice', 'email': 'alice@example.com', 'affiliation': 'University', 'specialization': 'Neural Networks'} -``` - -### πŸ§ͺ Quick Test -```python -profile = DeveloperProfile("Test", "test@example.com") -print(f"Profile: {profile}") -print(f"Signature:\\n{profile.get_signature()}") -print(f"Info: {profile.get_profile_info()}") +profile_dict = dev.get_profile_info() +print(profile_dict['name']) # Output: Alice ``` ### 🚨 Common Pitfalls -- ❌ Not using `self.` when storing or accessing instance variables -- ❌ Wrong dictionary keys in `get_profile_info()` -- ❌ Incorrect string formatting in `get_signature()` -- ❌ Forgetting to return values from methods -- ❌ Not using the default parameters properly +- ❌ Not using default parameters correctly +- ❌ Missing the decorative formatting (borders, emoji) +- ❌ Incorrect dictionary key names +- ❌ Not storing all parameters as instance variables +- ❌ String formatting issues (missing newlines, spacing) + +### πŸ§ͺ Quick Test +```python +dev = DeveloperProfile() # Should work with defaults +print(dev.get_signature()) # Should print default signature +``` """ -# %% nbgrader={"grade": false, "grade_id": "developer_profile_class", "locked": false, "schema_version": 3, "solution": true, "task": false} +# %% +# nbgrader: grade, solution #| export class DeveloperProfile: """ - A class representing a developer profile. + A class representing a developer profile with personal and professional information. - This class manages developer information and provides - multiple ways to display and access the data. + This class stores developer information and provides methods to display + and access the profile data in various formats. """ - def __init__(self, name="Student", email="student@example.com", affiliation="TinyTorch Community", specialization="ML Systems"): + def __init__(self, name="Student", email="student@example.com", + affiliation="TinyTorch Community", specialization="ML Systems"): """ Initialize a developer profile. Args: - name: Developer's name - email: Developer's email - affiliation: Developer's affiliation or organization - specialization: Developer's area of specialization - - Hints: - - Store each parameter as an instance variable - - Use self.attribute_name = parameter_name - - Default values are already provided + name (str): Developer's name + email (str): Developer's email address + affiliation (str): Developer's organization or school + specialization (str): Developer's area of expertise """ - ### BEGIN SOLUTION self.name = name self.email = email self.affiliation = affiliation self.specialization = specialization - ### END SOLUTION def __str__(self): - """ - Return a basic string representation of the developer. - Format: "Name (email)" - - Hints: - - Use f-string formatting - - Format: f"{self.name} ({self.email})" - - Example: "Alice (alice@example.com)" - """ - ### BEGIN SOLUTION - return f"{self.name} ({self.email})" - ### END SOLUTION + """Return a formatted developer profile card.""" + return f"""═══════════════════════════════════════ +πŸš€ DEVELOPER PROFILE +═══════════════════════════════════════ +Name: {self.name} +Email: {self.email} +Affiliation: {self.affiliation} +Specialization: {self.specialization} +═══════════════════════════════════════""" def get_signature(self): - """ - Return a formatted signature for the developer. - Should include name, affiliation, and specialization. - - Hints: - - Multi-line string with `\\n` separators - - Format: "Name\\nAffiliation\\nSpecialization: specialization" - - Use f-strings for clean formatting - """ - ### BEGIN SOLUTION - return f"{self.name}\n{self.affiliation}\nSpecialization: {self.specialization}" - ### END SOLUTION + """Return a signature line with name and specialization.""" + return f"{self.name} - {self.specialization} Specialist" def get_profile_info(self): - """ - Return comprehensive profile information as a dictionary. - - Hints: - - Return dict with keys: 'name', 'email', 'affiliation', 'specialization' - - Values should be the corresponding self.attribute values - - Use exact key names as shown above - """ - ### BEGIN SOLUTION + """Return profile information as a dictionary.""" return { 'name': self.name, 'email': self.email, 'affiliation': self.affiliation, 'specialization': self.specialization } - ### END SOLUTION + +# %% +# nbgrader: tests +# Test DeveloperProfile class +dev = DeveloperProfile("Alice", "alice@example.com", "MIT", "Deep Learning") + +# Test attributes +assert dev.name == "Alice", "Name should be stored correctly" +assert dev.email == "alice@example.com", "Email should be stored correctly" +assert dev.affiliation == "MIT", "Affiliation should be stored correctly" +assert dev.specialization == "Deep Learning", "Specialization should be stored correctly" + +# Test string representation +dev_str = str(dev) +assert "DEVELOPER PROFILE" in dev_str, "String should contain title" +assert "Alice" in dev_str, "String should contain name" +assert "alice@example.com" in dev_str, "String should contain email" +assert "MIT" in dev_str, "String should contain affiliation" +assert "Deep Learning" in dev_str, "String should contain specialization" + +# Test signature +signature = dev.get_signature() +assert "Alice - Deep Learning Specialist" == signature, "Signature should be formatted correctly" + +# Test profile info dictionary +profile_info = dev.get_profile_info() +assert isinstance(profile_info, dict), "get_profile_info should return dict" +assert profile_info['name'] == "Alice", "Profile info should contain correct name" +assert profile_info['email'] == "alice@example.com", "Profile info should contain correct email" +assert profile_info['affiliation'] == "MIT", "Profile info should contain correct affiliation" +assert profile_info['specialization'] == "Deep Learning", "Profile info should contain correct specialization" + +# Test default initialization +default_dev = DeveloperProfile() +assert default_dev.name == "Student", "Default name should be 'Student'" +assert default_dev.email == "student@example.com", "Default email should be correct" + +print("βœ… DeveloperProfile tests passed!") # %% [markdown] """ -## Testing Your Implementation πŸ§ͺ +## Problem 6: Integration Test (25 points) πŸ”§ -Once you've implemented all the functions above, run the cells below to test your work! - -### πŸ”„ TinyTorch Workflow Reminder -1. **Implement** the functions above βœ… -2. **Export** to package: `tito module export 00_setup` -3. **Test** your work: `pytest tests/ -v` -4. **Use** your code: `from tinytorch.core.utils import hello_tinytorch` - -### 🚨 Before You Continue -Make sure you can run this without errors: -```python -# Quick test all functions -hello_tinytorch() -print(complex_calculation(3, 4)) # Should be 110 -print(add_numbers(2.5, 1.5)) # Should be 4.0 -print(SystemInfo()) # Should show your system info -print(DeveloperProfile()) # Should show default profile -``` -""" - -# %% [markdown] -""" -## Problem 6: Integration Test (25 points) πŸ”— - -**Goal**: Test that all your components work together correctly and demonstrate the complete workflow. +**Goal**: Create a comprehensive test function that verifies all previous functions work together. ### πŸ“ Requirements -- Test all functions and classes work correctly -- Test the multi-step function with multiple solution blocks -- Verify system compatibility -- Display a complete developer profile -- Show successful framework initialization -- Return `True` if all tests pass +- Create a function called `test_integration()` +- Test all previously implemented functions +- Use proper assertions with descriptive error messages +- Handle any exceptions gracefully +- Return a success message if all tests pass ### πŸ’‘ Approach & Hints -- Use the functions you just implemented -- Include print statements for clear output -- Test with specific values to verify correctness -- Use assertions to check expected results -- Catch and handle any exceptions gracefully +1. **Test each function systematically** - call each function with known inputs +2. **Use assert statements** - verify expected outputs +3. **Descriptive error messages** - help debug what went wrong +4. **Exception handling** - catch and report any unexpected errors +5. **Comprehensive coverage** - test both normal and edge cases -### 🎯 Expected Output -Your integration test should produce output like: -``` -πŸ§ͺ Testing hello_tinytorch()... -[ASCII art or TinyTorch banner] -Build ML Systems from Scratch! -βœ… Welcome function works! - -πŸ§ͺ Testing complex_calculation() with multiple solution blocks... -βœ… Multi-step calculation: complex_calculation(3, 4) = 110 -βœ… Multiple solution blocks working correctly! - -... [more tests] ... - -πŸŽ‰ All components working together! -βœ… Ready for module export and package building! -``` - -### πŸ§ͺ Quick Test Structure +### 🎯 Expected Behavior ```python -def test_integration(): - print("πŸ§ͺ Testing...") - - # Test each function - # Use assertions to verify correctness - # Print success messages - - print("πŸŽ‰ All tests passed!") - return True +test_integration() +# Output: +# βœ… Testing hello_tinytorch... passed +# βœ… Testing complex_calculation... passed +# βœ… Testing add_numbers... passed +# βœ… Testing SystemInfo... passed +# βœ… Testing DeveloperProfile... passed +# πŸŽ‰ All integration tests passed! TinyTorch setup is complete. ``` ### 🚨 Common Pitfalls -- ❌ Not testing all functions -- ❌ Not checking return values with assertions -- ❌ Not handling potential exceptions -- ❌ Forgetting to return True at the end +- ❌ Not testing all functions thoroughly +- ❌ Missing error handling for unexpected exceptions +- ❌ Unclear error messages (hard to debug failures) +- ❌ Not returning a success indicator +- ❌ Testing only happy path (not edge cases) + +### πŸ§ͺ Quick Test +```python +result = test_integration() +print(result) # Should print success message +``` """ -# %% nbgrader={"grade": true, "grade_id": "integration_test", "locked": false, "points": 25, "schema_version": 3, "solution": true, "task": false} +# %% +# nbgrader: grade, solution +#| export def test_integration(): """ - Integration test to verify all components work together. - This function tests the complete TinyTorch setup workflow. + Comprehensive integration test for all TinyTorch setup functions. + + This function tests all implemented functions to ensure they work + correctly together and individually. Returns: - bool: True if all tests pass + str: Success message if all tests pass - Hints: - - Test each function you implemented - - Use assertions to verify expected results - - Include informative print statements - - Handle any exceptions gracefully - - Return True if everything works + Raises: + AssertionError: If any test fails + Exception: If any unexpected error occurs """ - ### BEGIN SOLUTION - # Test 1: Welcome function - print("πŸ§ͺ Testing hello_tinytorch()...") - hello_tinytorch() - print("βœ… Welcome function works!\n") - - # Test 2: Multi-step calculation (demonstrates multiple solution blocks) - print("πŸ§ͺ Testing complex_calculation() with multiple solution blocks...") - result = complex_calculation(3, 4) - expected = 110 # (3+2) + (4+2) = 11, 11*10 = 110 - assert result == expected, f"Expected {expected}, got {result}" - print(f"βœ… Multi-step calculation: complex_calculation(3, 4) = {result}") - print("βœ… Multiple solution blocks working correctly!\n") - - # Test 3: Simple math function - print("πŸ§ͺ Testing add_numbers()...") - result = add_numbers(2.5, 1.5) - assert result == 4.0, f"Expected 4.0, got {result}" - print(f"βœ… Math function: add_numbers(2.5, 1.5) = {result}\n") - - # Test 4: System information - print("πŸ§ͺ Testing SystemInfo class...") - sys_info = SystemInfo() - print(f"βœ… System info: {sys_info}") - print(f"βœ… Python compatible: {sys_info.is_compatible()}\n") - - # Test 5: Developer profile - print("πŸ§ͺ Testing DeveloperProfile class...") - dev = DeveloperProfile("TinyTorch Student", "student@tinytorch.edu", "TinyTorch University", "ML Systems") - print(f"βœ… Developer: {dev}") - print(f"βœ… Profile info: {dev.get_profile_info()}\n") - - # Test 6: Complete workflow - print("πŸŽ‰ All components working together!") - print("βœ… Ready for module export and package building!") - return True - ### END SOLUTION + try: + # Test hello_tinytorch + print("βœ… Testing hello_tinytorch... ", end="") + import io + import sys + old_stdout = sys.stdout + sys.stdout = io.StringIO() + hello_tinytorch() + output = sys.stdout.getvalue() + sys.stdout = old_stdout + assert len(output.strip()) > 0, "hello_tinytorch should produce output" + assert "Build ML Systems from Scratch!" in output, "hello_tinytorch should contain tagline" + print("passed") + + # Test complex_calculation + print("βœ… Testing complex_calculation... ", end="") + result = complex_calculation(3, 4) + assert result == 110, f"complex_calculation(3, 4) should return 110, got {result}" + result = complex_calculation(0, 0) + assert result == 40, f"complex_calculation(0, 0) should return 40, got {result}" + print("passed") + + # Test add_numbers + print("βœ… Testing add_numbers... ", end="") + result = add_numbers(5, 7) + assert result == 12, f"add_numbers(5, 7) should return 12, got {result}" + result = add_numbers(1.5, 2.5) + assert result == 4.0, f"add_numbers(1.5, 2.5) should return 4.0, got {result}" + print("passed") + + # Test SystemInfo + print("βœ… Testing SystemInfo... ", end="") + info = SystemInfo() + assert hasattr(info, 'python_version'), "SystemInfo should have python_version attribute" + assert hasattr(info, 'platform'), "SystemInfo should have platform attribute" + assert hasattr(info, 'timestamp'), "SystemInfo should have timestamp attribute" + info_str = str(info) + assert "TinyTorch System Info" in info_str, "SystemInfo string should contain title" + assert isinstance(info.is_compatible(), bool), "is_compatible should return boolean" + print("passed") + + # Test DeveloperProfile + print("βœ… Testing DeveloperProfile... ", end="") + dev = DeveloperProfile("Test User", "test@example.com", "Test Org", "Testing") + assert dev.name == "Test User", "DeveloperProfile should store name correctly" + assert dev.email == "test@example.com", "DeveloperProfile should store email correctly" + dev_str = str(dev) + assert "DEVELOPER PROFILE" in dev_str, "DeveloperProfile string should contain title" + signature = dev.get_signature() + assert "Test User - Testing Specialist" == signature, "Signature should be formatted correctly" + profile_info = dev.get_profile_info() + assert isinstance(profile_info, dict), "get_profile_info should return dict" + assert profile_info['name'] == "Test User", "Profile info should contain correct name" + print("passed") + + # All tests passed + success_msg = "πŸŽ‰ All integration tests passed! TinyTorch setup is complete." + print(success_msg) + return success_msg + + except Exception as e: + error_msg = f"❌ Integration test failed: {str(e)}" + print(error_msg) + raise + +# %% +# nbgrader: tests +# Test integration function +try: + result = test_integration() + assert "All integration tests passed" in result, "Integration test should return success message" + print("βœ… Integration test verification passed!") +except Exception as e: + print(f"❌ Integration test verification failed: {e}") + raise # %% [markdown] """ -## Next Steps πŸš€ +## πŸŽ‰ Assignment Complete! -Congratulations! You've completed your first TinyTorch assignment! +Congratulations! You've successfully completed the TinyTorch setup assignment. -### 🎯 What You've Accomplished -- βœ… Set up your TinyTorch development environment -- βœ… Implemented 5 core utility functions -- βœ… Learned the complete development workflow -- βœ… Practiced error handling and defensive programming -- βœ… Mastered NBGrader solution blocks and guided implementation -- βœ… Created your first real TinyTorch components! +### What You've Accomplished: +βœ… **File I/O & Error Handling** - Created robust file reading with graceful fallbacks +βœ… **Multi-Step Implementation** - Learned NBGrader's guided solution approach +βœ… **Basic Functions** - Implemented fundamental mathematical operations +βœ… **Object-Oriented Programming** - Built classes with multiple methods +βœ… **System Integration** - Created comprehensive testing workflows +βœ… **Real-World Skills** - Practiced debugging, testing, and validation -### πŸ”„ Complete the Workflow -1. **Export** your code to the TinyTorch package: - ```bash - tito module export 00_setup - ``` +### Next Steps: +1. **Export your code**: `tito module export 00_setup` +2. **Run the tests**: `pytest tests/ -v` +3. **Use your functions**: `from tinytorch.core.utils import hello_tinytorch` -2. **Run tests** to verify everything works: - ```bash - pytest tests/ -v - ``` +### Key Takeaways: +- **Error handling is crucial** - Always plan for things to go wrong +- **Testing saves time** - Comprehensive tests catch bugs early +- **Documentation matters** - Clear docstrings help future you +- **Incremental development** - Build and test one piece at a time -3. **Try using** your functions: - ```python - from tinytorch.core.utils import hello_tinytorch, add_numbers - hello_tinytorch() - print(add_numbers(5, 3)) - ``` - -4. **Move on** to the next assignment: `01_tensor` - -### πŸŽ‰ You're Ready! -You've just created the foundation utilities for TinyTorch. These functions will be used throughout the framework. Great job! - -Welcome to the world of building ML systems from scratch! πŸ”₯ +**Welcome to the TinyTorch journey! πŸš€** """ \ No newline at end of file diff --git a/assignments/source/00_setup/tests/test_setup.py b/assignments/source/00_setup/tests/test_setup.py deleted file mode 100644 index 9da0107a..00000000 --- a/assignments/source/00_setup/tests/test_setup.py +++ /dev/null @@ -1,239 +0,0 @@ -""" -Test suite for the setup module. -This tests the student implementations to ensure they work correctly. -""" - -import pytest -import sys -from pathlib import Path - -# Import from the main package -from tinytorch.core.utils import hello_tinytorch, add_numbers, SystemInfo, DeveloperProfile, complex_calculation - - -class TestSetupFunctions: - """Test setup module functions.""" - - def test_hello_tinytorch_executes(self): - """Test that hello_tinytorch runs without error.""" - # Should not raise any exceptions - hello_tinytorch() - - def test_hello_tinytorch_prints_content(self, capsys): - """Test that hello_tinytorch prints the expected content.""" - hello_tinytorch() - captured = capsys.readouterr() - - # Should print either ASCII art or simple banner, plus the tagline - # Check for the tagline which should always be present - assert "Build ML Systems from Scratch!" in captured.out - - # Should print either "TinyTorch" (simple banner) or ASCII art - # If ASCII art file exists, it will show art; otherwise simple banner - assert ("TinyTorch" in captured.out or - "TinyπŸ”₯Torch" in captured.out or - len(captured.out.splitlines()) > 5) # ASCII art has many lines - - def test_complex_calculation_basic(self): - """Test multi-step calculation with multiple solution blocks.""" - # Test the example from the assignment: complex_calculation(3, 4) - # Step 1: a_plus_2 = 3+2 = 5, b_plus_2 = 4+2 = 6 - # Step 2: everything_summed = 5+6 = 11 - # Step 3: everything_summed_times_10 = 11*10 = 110 - result = complex_calculation(3, 4) - expected = 110 - assert result == expected, f"Expected {expected}, got {result}" - - def test_complex_calculation_different_inputs(self): - """Test multi-step calculation with different inputs.""" - # Test with different numbers - result = complex_calculation(1, 2) - # Step 1: 1+2=3, 2+2=4 - # Step 2: 3+4=7 - # Step 3: 7*10=70 - expected = 70 - assert result == expected, f"Expected {expected}, got {result}" - - def test_complex_calculation_negative(self): - """Test multi-step calculation with negative numbers.""" - result = complex_calculation(-1, -2) - # Step 1: -1+2=1, -2+2=0 - # Step 2: 1+0=1 - # Step 3: 1*10=10 - expected = 10 - assert result == expected, f"Expected {expected}, got {result}" - - def test_add_numbers_basic(self): - """Test basic addition functionality.""" - assert add_numbers(2, 3) == 5 - assert add_numbers(10, 15) == 25 - assert add_numbers(0, 0) == 0 - - def test_add_numbers_negative(self): - """Test addition with negative numbers.""" - assert add_numbers(-5, 3) == -2 - assert add_numbers(-10, -15) == -25 - assert add_numbers(10, -5) == 5 - - def test_add_numbers_floats(self): - """Test addition with floating point numbers.""" - assert abs(add_numbers(2.5, 3.7) - 6.2) < 1e-9 - assert abs(add_numbers(1.1, 2.2) - 3.3) < 1e-9 - - -class TestSystemInfo: - """Test SystemInfo class.""" - - def test_system_info_creation(self): - """Test SystemInfo class instantiation.""" - info = SystemInfo() - assert hasattr(info, 'python_version') - assert hasattr(info, 'platform') - assert hasattr(info, 'machine') - - def test_system_info_properties(self): - """Test SystemInfo properties.""" - info = SystemInfo() - - # Check python_version is a string - assert isinstance(info.python_version, str) - assert len(info.python_version) > 0 - - # Check platform is a string - assert isinstance(info.platform, str) - assert len(info.platform) > 0 - - # Check machine is a string - assert isinstance(info.machine, str) - assert len(info.machine) > 0 - - def test_system_info_str(self): - """Test SystemInfo string representation.""" - info = SystemInfo() - str_repr = str(info) - - assert isinstance(str_repr, str) - assert "Python" in str_repr - assert info.python_version in str_repr - assert info.platform in str_repr - assert info.machine in str_repr - - def test_is_compatible(self): - """Test SystemInfo compatibility check.""" - info = SystemInfo() - compatible = info.is_compatible() - - # Should return a boolean - assert isinstance(compatible, bool) - - # Since we're running this test, Python should be >= 3.8 - assert compatible is True - - -class TestDeveloperProfile: - """Test DeveloperProfile class.""" - - def test_developer_profile_creation_defaults(self): - """Test DeveloperProfile with default values.""" - profile = DeveloperProfile() - - # Check default values - assert profile.name == "Student" - assert profile.email == "student@example.com" - assert profile.affiliation == "TinyTorch Community" - assert profile.specialization == "ML Systems" - - def test_developer_profile_creation_custom(self): - """Test DeveloperProfile with custom values.""" - profile = DeveloperProfile( - name="Test Student", - email="test@example.com", - affiliation="Test University", - specialization="Deep Learning" - ) - - assert profile.name == "Test Student" - assert profile.email == "test@example.com" - assert profile.affiliation == "Test University" - assert profile.specialization == "Deep Learning" - - def test_developer_profile_str(self): - """Test DeveloperProfile string representation.""" - profile = DeveloperProfile("Alice", "alice@example.com") - str_repr = str(profile) - - assert isinstance(str_repr, str) - assert "Alice" in str_repr - assert "alice@example.com" in str_repr - - def test_developer_profile_signature(self): - """Test DeveloperProfile signature method.""" - profile = DeveloperProfile("Bob", "bob@example.com", "Test University", "Neural Networks") - signature = profile.get_signature() - - assert isinstance(signature, str) - assert "Bob" in signature - assert "Test University" in signature - assert "Neural Networks" in signature - - def test_developer_profile_info(self): - """Test DeveloperProfile get_profile_info method.""" - profile = DeveloperProfile("Charlie", "charlie@example.com", "AI Lab", "Computer Vision") - info = profile.get_profile_info() - - assert isinstance(info, dict) - assert info['name'] == "Charlie" - assert info['email'] == "charlie@example.com" - assert info['affiliation'] == "AI Lab" - assert info['specialization'] == "Computer Vision" - - -class TestFileOperations: - """Test file-related operations.""" - - def test_hello_tinytorch_handles_missing_file(self, monkeypatch, capsys): - """Test that hello_tinytorch handles missing ASCII art file gracefully.""" - # Mock Path.exists to return False - def mock_exists(self): - return False - - monkeypatch.setattr(Path, "exists", mock_exists) - - # Should still work without the file - hello_tinytorch() - captured = capsys.readouterr() - - # Should still print the branding text - assert "TinyTorch" in captured.out - assert "Build ML Systems from Scratch!" in captured.out - - -class TestModuleIntegration: - """Test integration between different parts of the setup module.""" - - def test_all_functions_work_together(self): - """Test that all setup functions work without conflicts.""" - # Test functions - hello_tinytorch() # Should not raise - sum_result = add_numbers(5, 10) - calc_result = complex_calculation(1, 1) - - # Test classes - info = SystemInfo() - profile = DeveloperProfile() - - # All should work without errors - assert sum_result == 15 - assert calc_result == 60 # (1+2) + (1+2) = 6, 6*10 = 60 - assert str(info) # Should not be empty - assert str(profile) # Should not be empty - assert profile.get_signature() # Should not be empty - - def test_no_import_errors(self): - """Test that imports work correctly.""" - # If we got here, imports worked - assert callable(hello_tinytorch) - assert callable(add_numbers) - assert callable(complex_calculation) - assert callable(SystemInfo) - assert callable(DeveloperProfile) \ No newline at end of file