diff --git a/modules/source/01_setup/setup_dev.py b/modules/source/01_setup/setup_dev.py index 0b951bc9..a1e4aedc 100644 --- a/modules/source/01_setup/setup_dev.py +++ b/modules/source/01_setup/setup_dev.py @@ -528,7 +528,7 @@ These test functions provide immediate feedback when developing your solutions: """ # %% -def test_personal_info(): +def test_unit_personal_info_basic(): """Test personal_info function implementation.""" print("๐Ÿ”ฌ Unit Test: Personal Information...") @@ -562,7 +562,7 @@ def test_personal_info(): print(f"โœ… TinyTorch configured for: {personal['developer']}") # %% -def test_system_info(): +def test_unit_system_info_basic(): """Test system_info function implementation.""" print("๐Ÿ”ฌ Unit Test: System Information...") @@ -631,22 +631,6 @@ You've successfully configured your TinyTorch installation and learned the found - **Code export**: Functions become part of production package - **Testing practices**: Comprehensive validation of functionality -### Connections to Real ML Systems - -This module connects to broader ML engineering practices: - -#### **Industry Parallels** -- **Docker containers**: System configuration and reproducibility -- **MLflow tracking**: Experiment context and system metadata -- **Model cards**: Documentation of system requirements and performance -- **CI/CD pipelines**: Automated testing and environment validation - -#### **Production Considerations** -- **Deployment matching**: Development environment should match production -- **Resource planning**: Understanding hardware constraints for scaling -- **Monitoring**: System metrics for performance optimization -- **Debugging**: System context for troubleshooting issues - ### Next Steps in Your ML Systems Journey #### **Immediate Actions** @@ -682,698 +666,3 @@ You've taken your first step in ML systems engineering! This module taught you: **Ready for the next challenge?** Let's build the foundation of ML systems with tensors! """ - -# %% [markdown] -""" -## Step 4: Environment Validation - -### The Concept: Dependency Management in ML Systems -**Environment validation** ensures your system has the necessary packages and versions for ML development. This is crucial because ML systems have complex dependency chains that can break in subtle ways. - -### Why Environment Validation Matters - -#### 1. **Compatibility Assurance** -- **Version conflicts**: Different packages may require incompatible versions -- **API changes**: New versions might break existing code -- **Feature availability**: Some features require specific versions - -#### 2. **Reproducibility** -- **Environment documentation**: Exact package versions for reproduction -- **Dependency tracking**: Understanding what's installed and why -- **Debugging support**: Version info helps troubleshoot issues - -#### 3. **Professional Development** -- **Deployment safety**: Ensure development matches production -- **Collaboration**: Team members need compatible environments -- **Quality assurance**: Validate setup before beginning work - -### Essential ML Dependencies -We'll check for core packages that ML systems depend on: -- **numpy**: Fundamental numerical computing -- **matplotlib**: Visualization and plotting -- **psutil**: System information and monitoring -- **jupyter**: Interactive development environment -- **nbdev**: Package development tools -- **pytest**: Testing framework - -### Real-World Applications -- **Docker**: Container images include dependency validation -- **CI/CD**: Automated testing validates environment setup -- **MLflow**: Tracks package versions with experiment metadata -- **Kaggle**: Validates package availability in competition environments - -Let's implement environment validation! -""" - -# %% nbgrader={"grade": false, "grade_id": "environment-validation", "locked": false, "schema_version": 3, "solution": true, "task": false} -#| export -import importlib -import pkg_resources -from typing import Dict, List, Optional - -def validate_environment() -> Dict[str, Any]: - """ - Validate ML development environment and check essential dependencies. - - This function checks that your system has the necessary packages for ML development. - It's like a pre-flight check before you start building ML systems. - - TODO: Implement environment validation. - - STEP-BY-STEP IMPLEMENTATION: - 1. Define list of essential ML packages to check - 2. For each package, try to import it and get version - 3. Track which packages are available vs missing - 4. Calculate environment health score - 5. Return comprehensive environment report - - ESSENTIAL PACKAGES TO CHECK: - - numpy: Numerical computing foundation - - matplotlib: Visualization and plotting - - psutil: System monitoring - - jupyter: Interactive development - - nbdev: Package development - - pytest: Testing framework - - IMPLEMENTATION HINTS: - - Use try/except to handle missing packages gracefully - - Use pkg_resources.get_distribution(package).version for versions - - Calculate health_score as (available_packages / total_packages) * 100 - - Round health_score to 1 decimal place - """ - ### BEGIN SOLUTION - essential_packages = [ - 'numpy', 'matplotlib', 'psutil', 'jupyter', 'nbdev', 'pytest' - ] - - available = {} - missing = [] - - for package in essential_packages: - try: - # Try to import the package - importlib.import_module(package) - # Get version information - version = pkg_resources.get_distribution(package).version - available[package] = version - except (ImportError, pkg_resources.DistributionNotFound): - missing.append(package) - - # Calculate health score - total_packages = len(essential_packages) - available_packages = len(available) - health_score = round((available_packages / total_packages) * 100, 1) - - return { - 'available_packages': available, - 'missing_packages': missing, - 'health_score': health_score, - 'total_checked': total_packages, - 'status': 'healthy' if health_score >= 80 else 'needs_attention' - } - ### END SOLUTION - -# %% [markdown] -""" -## Step 5: Performance Benchmarking - -### The Concept: Hardware Performance Profiling -**Performance benchmarking** measures your system's computational capabilities for ML workloads. This helps you understand your hardware limits and optimize your development workflow. - -### Why Performance Benchmarking Matters - -#### 1. **Resource Planning** -- **Training time estimation**: How long will model training take? -- **Memory allocation**: What's the maximum batch size you can handle? -- **Parallelization**: How many cores can you effectively use? - -#### 2. **Optimization Guidance** -- **Bottleneck identification**: Is your system CPU-bound or memory-bound? -- **Hardware upgrades**: What would improve performance most? -- **Algorithm selection**: Which algorithms suit your hardware? - -#### 3. **Performance Comparison** -- **Baseline establishment**: Track performance over time -- **System comparison**: Compare different development environments -- **Deployment planning**: Match development to production performance - -### Benchmarking Strategy -We'll test key ML operations: -- **CPU computation**: Matrix operations that stress the processor -- **Memory bandwidth**: Large data transfers that test memory speed -- **Overall system**: Combined CPU and memory performance - -### Real-World Applications -- **MLPerf**: Industry-standard ML benchmarks -- **Cloud providers**: Performance metrics for instance selection -- **Hardware vendors**: Benchmark comparisons for purchasing decisions - -Let's implement performance benchmarking! -""" - -# %% nbgrader={"grade": false, "grade_id": "performance-benchmark", "locked": false, "schema_version": 3, "solution": true, "task": false} -#| export -import time -import random - -def benchmark_performance() -> Dict[str, Any]: - """ - Benchmark system performance for ML workloads. - - This function measures computational performance to help you understand - your system's capabilities and optimize your ML development workflow. - - TODO: Implement performance benchmarking. - - STEP-BY-STEP IMPLEMENTATION: - 1. CPU Test: Time a computationally intensive operation - 2. Memory Test: Time a memory-intensive operation - 3. Calculate performance scores based on execution time - 4. Determine overall system performance rating - 5. Return comprehensive benchmark results - - BENCHMARK TESTS: - - CPU: Nested loop calculation (computational intensity) - - Memory: Large list operations (memory bandwidth) - - Combined: Overall system performance score - - IMPLEMENTATION HINTS: - - Use time.time() to measure execution time - - CPU test: nested loops with mathematical operations - - Memory test: large list creation and manipulation - - Lower execution time = better performance - - Calculate scores as inverse of time (e.g., 1/time * 1000) - """ - ### BEGIN SOLUTION - benchmarks = {} - - # CPU Performance Test - print("โšก Running CPU benchmark...") - start_time = time.time() - - # CPU-intensive calculation - result = 0 - for i in range(100000): - result += i * i + i / 2 - - cpu_time = time.time() - start_time - benchmarks['cpu_time'] = round(cpu_time, 3) - benchmarks['cpu_score'] = round(1000 / cpu_time, 1) - - # Memory Performance Test - print("๐Ÿง  Running memory benchmark...") - start_time = time.time() - - # Memory-intensive operations - large_list = list(range(1000000)) - large_list.reverse() - large_list.sort() - - memory_time = time.time() - start_time - benchmarks['memory_time'] = round(memory_time, 3) - benchmarks['memory_score'] = round(1000 / memory_time, 1) - - # Overall Performance Score - overall_score = round((benchmarks['cpu_score'] + benchmarks['memory_score']) / 2, 1) - benchmarks['overall_score'] = overall_score - - # Performance Rating - if overall_score >= 80: - rating = 'excellent' - elif overall_score >= 60: - rating = 'good' - elif overall_score >= 40: - rating = 'fair' - else: - rating = 'needs_optimization' - - benchmarks['performance_rating'] = rating - - return benchmarks - ### END SOLUTION - -# %% [markdown] -""" -## Step 6: Development Environment Setup - -### The Concept: Professional Development Configuration -**Development environment setup** configures essential tools and settings for professional ML development. This includes Git configuration, Jupyter settings, and other tools that make development more efficient. - -### Why Development Setup Matters - -#### 1. **Professional Standards** -- **Version control**: Proper Git configuration for collaboration -- **Code quality**: Consistent formatting and style -- **Documentation**: Automatic documentation generation - -#### 2. **Productivity Optimization** -- **Tool configuration**: Optimized settings for efficiency -- **Workflow automation**: Reduce repetitive tasks -- **Error prevention**: Catch issues before they become problems - -#### 3. **Collaboration Readiness** -- **Team compatibility**: Consistent development environment -- **Code sharing**: Proper attribution and commit messages -- **Project standards**: Follow established conventions - -### Essential Development Tools -We'll configure key tools for ML development: -- **Git**: Version control and collaboration -- **Jupyter**: Interactive development environment -- **Python**: Code formatting and quality tools - -Let's implement development environment setup! -""" - -# %% nbgrader={"grade": false, "grade_id": "development-setup", "locked": false, "schema_version": 3, "solution": true, "task": false} -#| export -import subprocess -import json -from pathlib import Path - -def setup_development_environment() -> Dict[str, Any]: - """ - Configure development environment for professional ML development. - - This function sets up essential tools and configurations to make your - development workflow more efficient and professional. - - TODO: Implement development environment setup. - - STEP-BY-STEP IMPLEMENTATION: - 1. Check if Git is installed and configured - 2. Verify Jupyter installation and configuration - 3. Check Python development tools - 4. Configure any missing tools - 5. Return setup status and recommendations - - DEVELOPMENT TOOLS TO CHECK: - - Git: Version control system - - Jupyter: Interactive development - - Python tools: Code quality and formatting - - IMPLEMENTATION HINTS: - - Use subprocess.run() to check tool availability - - Use try/except to handle missing tools gracefully - - Provide helpful recommendations for missing tools - - Focus on tools that improve ML development workflow - """ - ### BEGIN SOLUTION - setup_status = {} - recommendations = [] - - # Check Git installation and configuration - try: - git_version = subprocess.run(['git', '--version'], - capture_output=True, text=True, check=True) - setup_status['git_installed'] = True - setup_status['git_version'] = git_version.stdout.strip() - - # Check Git configuration - try: - git_name = subprocess.run(['git', 'config', 'user.name'], - capture_output=True, text=True, check=True) - git_email = subprocess.run(['git', 'config', 'user.email'], - capture_output=True, text=True, check=True) - setup_status['git_configured'] = True - setup_status['git_name'] = git_name.stdout.strip() - setup_status['git_email'] = git_email.stdout.strip() - except subprocess.CalledProcessError: - setup_status['git_configured'] = False - recommendations.append("Configure Git: git config --global user.name 'Your Name'") - recommendations.append("Configure Git: git config --global user.email 'your.email@domain.com'") - - except (subprocess.CalledProcessError, FileNotFoundError): - setup_status['git_installed'] = False - recommendations.append("Install Git: https://git-scm.com/downloads") - - # Check Jupyter installation - try: - jupyter_version = subprocess.run(['jupyter', '--version'], - capture_output=True, text=True, check=True) - setup_status['jupyter_installed'] = True - setup_status['jupyter_version'] = jupyter_version.stdout.strip() - except (subprocess.CalledProcessError, FileNotFoundError): - setup_status['jupyter_installed'] = False - recommendations.append("Install Jupyter: pip install jupyter") - - # Check Python tools - python_tools = ['pip', 'python'] - for tool in python_tools: - try: - tool_version = subprocess.run([tool, '--version'], - capture_output=True, text=True, check=True) - setup_status[f'{tool}_installed'] = True - setup_status[f'{tool}_version'] = tool_version.stdout.strip() - except (subprocess.CalledProcessError, FileNotFoundError): - setup_status[f'{tool}_installed'] = False - recommendations.append(f"Install {tool}: Check Python installation") - - # Calculate setup health - total_tools = 4 # git, jupyter, pip, python - installed_tools = sum([ - setup_status.get('git_installed', False), - setup_status.get('jupyter_installed', False), - setup_status.get('pip_installed', False), - setup_status.get('python_installed', False) - ]) - - setup_score = round((installed_tools / total_tools) * 100, 1) - - return { - 'setup_status': setup_status, - 'recommendations': recommendations, - 'setup_score': setup_score, - 'status': 'ready' if setup_score >= 75 else 'needs_configuration' - } - ### END SOLUTION - -# %% [markdown] -""" -## Step 7: Comprehensive System Report - -### The Concept: Integrated System Analysis -**Comprehensive system reporting** combines all your configuration and diagnostic information into a single, actionable report. This is like a "health check" for your ML development environment. - -### Why Comprehensive Reporting Matters - -#### 1. **Holistic View** -- **Complete picture**: All system information in one place -- **Dependency analysis**: How different components interact -- **Performance context**: Understanding system capabilities - -#### 2. **Troubleshooting Support** -- **Debugging aid**: Complete environment information for issue resolution -- **Performance analysis**: Identify bottlenecks and optimization opportunities -- **Compatibility checking**: Ensure all components work together - -#### 3. **Professional Documentation** -- **Environment documentation**: Complete system specification -- **Reproducibility**: All information needed to recreate environment -- **Sharing**: Easy to share system information with collaborators - -Let's create a comprehensive system report! -""" - -# %% nbgrader={"grade": false, "grade_id": "system-report", "locked": false, "schema_version": 3, "solution": true, "task": false} -#| export -from datetime import datetime - -def generate_system_report() -> Dict[str, Any]: - """ - Generate comprehensive system report for ML development. - - This function combines all configuration and diagnostic information - into a single, actionable report for your ML development environment. - - TODO: Implement comprehensive system reporting. - - STEP-BY-STEP IMPLEMENTATION: - 1. Gather personal information - 2. Collect system information - 3. Validate environment - 4. Run performance benchmarks - 5. Check development setup - 6. Generate overall health score - 7. Create comprehensive report with recommendations - - REPORT SECTIONS: - - Personal configuration - - System specifications - - Environment validation - - Performance benchmarks - - Development setup - - Overall health assessment - - Recommendations for improvement - - IMPLEMENTATION HINTS: - - Call all previously implemented functions - - Combine results into comprehensive report - - Calculate overall health score from all components - - Provide actionable recommendations - """ - ### BEGIN SOLUTION - print("๐Ÿ“Š Generating comprehensive system report...") - - # Gather all information - personal = personal_info() - system = system_info() - environment = validate_environment() - performance = benchmark_performance() - development = setup_development_environment() - - # Calculate overall health score (normalize performance score to 0-100 range) - normalized_performance = min(performance['overall_score'], 100) # Cap at 100 - - health_components = [ - environment['health_score'], - normalized_performance, - development['setup_score'] - ] - - overall_health = round(sum(health_components) / len(health_components), 1) - - # Generate status - if overall_health >= 85: - status = 'excellent' - elif overall_health >= 70: - status = 'good' - elif overall_health >= 50: - status = 'fair' - else: - status = 'needs_attention' - - # Compile recommendations - recommendations = [] - - if environment['health_score'] < 80: - recommendations.extend([f"Install missing package: {pkg}" for pkg in environment['missing_packages']]) - - if performance['overall_score'] < 50: - recommendations.append("Consider hardware upgrade for better ML performance") - - recommendations.extend(development['recommendations']) - - # Create comprehensive report - report = { - 'timestamp': datetime.now().isoformat(), - 'personal_info': personal, - 'system_info': system, - 'environment_validation': environment, - 'performance_benchmarks': performance, - 'development_setup': development, - 'overall_health': overall_health, - 'status': status, - 'recommendations': recommendations, - 'report_version': '1.0.0' - } - - return report - ### END SOLUTION - -# %% [markdown] -""" -### ๐Ÿงช Unit Test: Enhanced Setup Functions - -Test all the new enhanced setup functions: -""" - -# Old function removed - using shared test runner pattern - -# %% -def test_performance_benchmark(): - """Test performance benchmarking function.""" - print("๐Ÿ”ฌ Unit Test: Performance Benchmarking...") - - benchmark_report = benchmark_performance() - - # Test return type and structure - assert isinstance(benchmark_report, dict), "benchmark_performance should return a dictionary" - - # Test required keys - required_keys = ['cpu_time', 'cpu_score', 'memory_time', 'memory_score', 'overall_score', 'performance_rating'] - for key in required_keys: - assert key in benchmark_report, f"Report should have '{key}' key" - - # Test data types - assert isinstance(benchmark_report['cpu_time'], (int, float)), "cpu_time should be number" - assert isinstance(benchmark_report['cpu_score'], (int, float)), "cpu_score should be number" - assert isinstance(benchmark_report['memory_time'], (int, float)), "memory_time should be number" - assert isinstance(benchmark_report['memory_score'], (int, float)), "memory_score should be number" - assert isinstance(benchmark_report['overall_score'], (int, float)), "overall_score should be number" - assert isinstance(benchmark_report['performance_rating'], str), "performance_rating should be string" - - # Test reasonable values - assert benchmark_report['cpu_time'] > 0, "cpu_time should be positive" - assert benchmark_report['memory_time'] > 0, "memory_time should be positive" - assert benchmark_report['cpu_score'] > 0, "cpu_score should be positive" - assert benchmark_report['memory_score'] > 0, "memory_score should be positive" - assert benchmark_report['overall_score'] > 0, "overall_score should be positive" - - valid_ratings = ['excellent', 'good', 'fair', 'needs_optimization'] - assert benchmark_report['performance_rating'] in valid_ratings, "performance_rating should be valid" - - print("โœ… Performance benchmark tests passed!") - print(f"โœ… Performance rating: {benchmark_report['performance_rating']}") - -# %% -def test_development_setup(): - """Test development environment setup function.""" - print("๐Ÿ”ฌ Unit Test: Development Environment Setup...") - - setup_report = setup_development_environment() - - # Test return type and structure - assert isinstance(setup_report, dict), "setup_development_environment should return a dictionary" - - # Test required keys - required_keys = ['setup_status', 'recommendations', 'setup_score', 'status'] - for key in required_keys: - assert key in setup_report, f"Report should have '{key}' key" - - # Test data types - assert isinstance(setup_report['setup_status'], dict), "setup_status should be dict" - assert isinstance(setup_report['recommendations'], list), "recommendations should be list" - assert isinstance(setup_report['setup_score'], (int, float)), "setup_score should be number" - assert isinstance(setup_report['status'], str), "status should be string" - - # Test reasonable values - assert 0 <= setup_report['setup_score'] <= 100, "setup_score should be between 0 and 100" - assert setup_report['status'] in ['ready', 'needs_configuration'], "status should be valid" - - print("โœ… Development setup tests passed!") - print(f"โœ… Setup score: {setup_report['setup_score']}%") - -# %% -def test_system_report(): - """Test comprehensive system report function.""" - print("๐Ÿ”ฌ Unit Test: System Report Generation...") - - report = generate_system_report() - - # Test return type and structure - assert isinstance(report, dict), "generate_system_report should return a dictionary" - - # Test required keys - required_keys = ['timestamp', 'personal_info', 'system_info', 'environment_validation', - 'performance_benchmarks', 'development_setup', 'overall_health', - 'status', 'recommendations', 'report_version'] - for key in required_keys: - assert key in report, f"Report should have '{key}' key" - - # Test data types - assert isinstance(report['timestamp'], str), "timestamp should be string" - assert isinstance(report['personal_info'], dict), "personal_info should be dict" - assert isinstance(report['system_info'], dict), "system_info should be dict" - assert isinstance(report['environment_validation'], dict), "environment_validation should be dict" - assert isinstance(report['performance_benchmarks'], dict), "performance_benchmarks should be dict" - assert isinstance(report['development_setup'], dict), "development_setup should be dict" - assert isinstance(report['overall_health'], (int, float)), "overall_health should be number" - assert isinstance(report['status'], str), "status should be string" - assert isinstance(report['recommendations'], list), "recommendations should be list" - assert isinstance(report['report_version'], str), "report_version should be string" - - # Test reasonable values - assert 0 <= report['overall_health'] <= 100, "overall_health should be between 0 and 100" - valid_statuses = ['excellent', 'good', 'fair', 'needs_attention'] - assert report['status'] in valid_statuses, "status should be valid" - - print("โœ… System report tests passed!") - print(f"โœ… Overall system health: {report['overall_health']}%") - - - -# %% -def test_personal_info(): - """Test personal information function comprehensively.""" - personal = personal_info() - assert isinstance(personal, dict), "personal_info should return a dictionary" - assert 'developer' in personal, "Dictionary should have 'developer' key" - assert '@' in personal['email'], "Email should contain @ symbol" - print("โœ… Personal information function works") - -def test_system_info(): - """Test system information function comprehensively.""" - system = system_info() - assert isinstance(system, dict), "system_info should return a dictionary" - assert 'python_version' in system, "Dictionary should have 'python_version' key" - assert system['memory_gb'] > 0, "Memory should be positive" - print("โœ… System information function works") - -def test_environment_validation(): - """Test environment validation function comprehensively.""" - env = validate_environment() - assert isinstance(env, dict), "validate_environment should return a dictionary" - assert 'health_score' in env, "Dictionary should have 'health_score' key" - print("โœ… Environment validation function works") - -def test_performance_benchmark(): - """Test performance benchmarking function comprehensively.""" - perf = benchmark_performance() - assert isinstance(perf, dict), "benchmark_performance should return a dictionary" - assert 'cpu_score' in perf, "Dictionary should have 'cpu_score' key" - print("โœ… Performance benchmarking function works") - -def test_development_setup(): - """Test development setup function comprehensively.""" - dev = setup_development_environment() - assert isinstance(dev, dict), "setup_development_environment should return a dictionary" - assert 'setup_score' in dev, "Dictionary should have 'setup_score' key" - print("โœ… Development setup function works") - -def test_system_report(): - """Test system report comprehensive function.""" - report = generate_system_report() - assert isinstance(report, dict), "generate_system_report should return a dictionary" - assert 'overall_health' in report, "Dictionary should have 'overall_health' key" - print("โœ… System report function works") - -# %% [markdown] -""" -## ๐Ÿงช Module Testing - -Time to test your implementation! This section uses TinyTorch's standardized testing framework to ensure your implementation works correctly. - -**This testing section is locked** - it provides consistent feedback across all modules and cannot be modified. -""" - -# %% nbgrader={"grade": false, "grade_id": "standardized-testing", "locked": true, "schema_version": 3, "solution": false, "task": false} -# ============================================================================= -# STANDARDIZED MODULE TESTING - DO NOT MODIFY -# This cell is locked to ensure consistent testing across all TinyTorch modules -# ============================================================================= - -if __name__ == "__main__": - from tito.tools.testing import run_module_tests_auto - - # Automatically discover and run all tests in this module - success = run_module_tests_auto("Setup") - -# %% [markdown] -""" -## ๐ŸŽฏ Module Summary: Development Environment Setup Complete! - -Congratulations! You've successfully set up your TinyTorch development environment: - -### What You've Accomplished -โœ… **Personal Configuration**: Developer information and preferences -โœ… **System Analysis**: Hardware and software environment validation -โœ… **Environment Validation**: Python packages and dependencies -โœ… **Performance Benchmarking**: CPU and memory performance testing -โœ… **Development Setup**: IDE configuration and tooling -โœ… **Comprehensive Reporting**: System health and recommendations - -### Key Concepts You've Learned -- **Environment Management**: How to validate and configure development environments -- **Performance Analysis**: Benchmarking system capabilities for ML workloads -- **System Diagnostics**: Comprehensive health checking and reporting -- **Development Best Practices**: Professional setup for ML development - -### Next Steps -1. **Export your code**: `tito package nbdev --export 00_setup` -2. **Test your implementation**: `tito test 00_setup` -3. **Use your environment**: Start building with confidence in a validated setup -4. **Move to Module 1**: Begin implementing the core tensor system! - -**Ready for the ML journey?** Your development environment is now optimized for building neural networks from scratch! -""" \ No newline at end of file diff --git a/modules/source/02_tensor/tensor_dev.py b/modules/source/02_tensor/tensor_dev.py index d67e83a4..36281964 100644 --- a/modules/source/02_tensor/tensor_dev.py +++ b/modules/source/02_tensor/tensor_dev.py @@ -613,6 +613,35 @@ class Tensor: return Tensor(result) ### END SOLUTION + def mean(self) -> 'Tensor': + """Computes the mean of the tensor's elements.""" + return Tensor(np.mean(self.data)) + + # --- Matmul --- + def matmul(self, other: 'Tensor') -> 'Tensor': + """ + Perform matrix multiplication between two tensors. + + TODO: Implement matrix multiplication. + + APPROACH: + 1. Use np.matmul() to perform matrix multiplication + 2. Return a new Tensor with the result + 3. Handle broadcasting automatically + + EXAMPLE: + Tensor([[1, 2], [3, 4]]) @ Tensor([[5, 6], [7, 8]]) โ†’ Tensor([[19, 22], [43, 50]]) + + HINTS: + - Use np.matmul(self._data, other._data) + - Return Tensor(result) + - This is matrix multiplication, not element-wise multiplication + """ + ### BEGIN SOLUTION + result = np.matmul(self._data, other._data) + return Tensor(result) + ### END SOLUTION + # %% [markdown] """ ### ๐Ÿงช Unit Test: Tensor Creation @@ -755,798 +784,6 @@ print(" Returns new Tensor objects") # %% [markdown] """ -### ๐Ÿงช Comprehensive Test: Tensor Creation - -Let's thoroughly test your tensor creation to make sure it handles all the cases you'll encounter in ML. -This tests the foundation of everything else we'll build. -""" - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-creation-comprehensive", "locked": true, "points": 15, "schema_version": 3, "solution": false, "task": false} -def test_tensor_creation(): - """Comprehensive test of tensor creation with all data types and shapes.""" - print("๐Ÿ”ฌ Testing comprehensive tensor creation...") - - tests_passed = 0 - total_tests = 8 - - # Test 1: Scalar creation (0D tensor) - try: - scalar_int = Tensor(42) - scalar_float = Tensor(3.14) - scalar_zero = Tensor(0) - - assert hasattr(scalar_int, '_data'), "Tensor should have _data attribute" - assert scalar_int._data.shape == (), f"Scalar should have shape (), got {scalar_int._data.shape}" - assert scalar_float._data.shape == (), f"Float scalar should have shape (), got {scalar_float._data.shape}" - assert scalar_zero._data.shape == (), f"Zero scalar should have shape (), got {scalar_zero._data.shape}" - - print("โœ… Scalar creation: integers, floats, and zero") - tests_passed += 1 - except Exception as e: - print(f"โŒ Scalar creation failed: {e}") - - # Test 2: Vector creation (1D tensor) - try: - vector_int = Tensor([1, 2, 3, 4, 5]) - vector_float = Tensor([1.0, 2.5, 3.7]) - vector_single = Tensor([42]) - vector_empty = Tensor([]) - - assert vector_int._data.shape == (5,), f"Int vector should have shape (5,), got {vector_int._data.shape}" - assert vector_float._data.shape == (3,), f"Float vector should have shape (3,), got {vector_float._data.shape}" - assert vector_single._data.shape == (1,), f"Single element vector should have shape (1,), got {vector_single._data.shape}" - assert vector_empty._data.shape == (0,), f"Empty vector should have shape (0,), got {vector_empty._data.shape}" - - print("โœ… Vector creation: integers, floats, single element, and empty") - tests_passed += 1 - except Exception as e: - print(f"โŒ Vector creation failed: {e}") - - # Test 3: Matrix creation (2D tensor) - try: - matrix_2x2 = Tensor([[1, 2], [3, 4]]) - matrix_3x2 = Tensor([[1, 2], [3, 4], [5, 6]]) - matrix_1x3 = Tensor([[1, 2, 3]]) - - assert matrix_2x2._data.shape == (2, 2), f"2x2 matrix should have shape (2, 2), got {matrix_2x2._data.shape}" - assert matrix_3x2._data.shape == (3, 2), f"3x2 matrix should have shape (3, 2), got {matrix_3x2._data.shape}" - assert matrix_1x3._data.shape == (1, 3), f"1x3 matrix should have shape (1, 3), got {matrix_1x3._data.shape}" - - print("โœ… Matrix creation: 2x2, 3x2, and 1x3 matrices") - tests_passed += 1 - except Exception as e: - print(f"โŒ Matrix creation failed: {e}") - - # Test 4: Data type handling - try: - int_tensor = Tensor([1, 2, 3]) - float_tensor = Tensor([1.0, 2.0, 3.0]) - mixed_tensor = Tensor([1, 2.5, 3]) # Should convert to float - - # Check that data types are reasonable - assert int_tensor._data.dtype in [np.int32, np.int64], f"Int tensor has unexpected dtype: {int_tensor._data.dtype}" - assert float_tensor._data.dtype in [np.float32, np.float64], f"Float tensor has unexpected dtype: {float_tensor._data.dtype}" - assert mixed_tensor._data.dtype in [np.float32, np.float64], f"Mixed tensor should be float, got: {mixed_tensor._data.dtype}" - - print("โœ… Data type handling: integers, floats, and mixed types") - tests_passed += 1 - except Exception as e: - print(f"โŒ Data type handling failed: {e}") - - # Test 5: NumPy array input - try: - np_array = np.array([1, 2, 3, 4]) - tensor_from_np = Tensor(np_array) - - assert tensor_from_np._data.shape == (4,), f"Tensor from NumPy should have shape (4,), got {tensor_from_np._data.shape}" - assert np.array_equal(tensor_from_np._data, np_array), "Tensor from NumPy should preserve data" - - print("โœ… NumPy array input: conversion works correctly") - tests_passed += 1 - except Exception as e: - print(f"โŒ NumPy array input failed: {e}") - - # Test 6: Large tensor creation - try: - large_tensor = Tensor(list(range(1000))) - assert large_tensor._data.shape == (1000,), f"Large tensor should have shape (1000,), got {large_tensor._data.shape}" - assert large_tensor._data[0] == 0, "Large tensor should start with 0" - assert large_tensor._data[-1] == 999, "Large tensor should end with 999" - - print("โœ… Large tensor creation: 1000 elements") - tests_passed += 1 - except Exception as e: - print(f"โŒ Large tensor creation failed: {e}") - - # Test 7: Negative numbers - try: - negative_tensor = Tensor([-1, -2, -3]) - mixed_signs = Tensor([-1, 0, 1]) - - assert negative_tensor._data.shape == (3,), f"Negative tensor should have shape (3,), got {negative_tensor._data.shape}" - assert np.array_equal(negative_tensor._data, np.array([-1, -2, -3])), "Negative numbers should be preserved" - assert np.array_equal(mixed_signs._data, np.array([-1, 0, 1])), "Mixed signs should be preserved" - - print("โœ… Negative numbers: handled correctly") - tests_passed += 1 - except Exception as e: - print(f"โŒ Negative numbers failed: {e}") - - # Test 8: Edge cases - try: - # Very large numbers - big_tensor = Tensor([1e6, 1e-6]) - assert big_tensor._data.shape == (2,), "Big numbers tensor should have correct shape" - - # Zero tensor - zero_tensor = Tensor([0, 0, 0]) - assert np.all(zero_tensor._data == 0), "Zero tensor should contain all zeros" - - print("โœ… Edge cases: large numbers and zeros") - tests_passed += 1 - except Exception as e: - print(f"โŒ Edge cases failed: {e}") - - # Results summary - print(f"\n๐Ÿ“Š Tensor Creation Results: {tests_passed}/{total_tests} tests passed") - - if tests_passed == total_tests: - print("๐ŸŽ‰ All tensor creation tests passed! Your Tensor class can handle:") - print(" โ€ข Scalars, vectors, and matrices") - print(" โ€ข Different data types (int, float)") - print(" โ€ข NumPy arrays") - print(" โ€ข Large tensors and edge cases") - print("๐Ÿ“ˆ Progress: Tensor Creation โœ“") - return True - else: - print("โš ๏ธ Some tensor creation tests failed. Common issues:") - print(" โ€ข Check your __init__ method implementation") - print(" โ€ข Make sure you're storing data in self._data") - print(" โ€ข Verify NumPy array conversion works correctly") - print(" โ€ข Test with different input types (int, float, list, np.array)") - return False - -# Run the comprehensive test -success = test_tensor_creation() - -# %% [markdown] -""" -### ๐Ÿงช Comprehensive Test: Tensor Properties - -Now let's test all the properties your tensor should have. These properties are essential for ML operations. -""" - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-properties-comprehensive", "locked": true, "points": 15, "schema_version": 3, "solution": false, "task": false} -def test_tensor_properties(): - """Comprehensive test of tensor properties (shape, size, dtype, data access).""" - print("๐Ÿ”ฌ Testing comprehensive tensor properties...") - - tests_passed = 0 - total_tests = 6 - - # Test 1: Shape property - try: - scalar = Tensor(5.0) - vector = Tensor([1, 2, 3]) - matrix = Tensor([[1, 2], [3, 4]]) - tensor_3d = Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) - - assert scalar.shape == (), f"Scalar shape should be (), got {scalar.shape}" - assert vector.shape == (3,), f"Vector shape should be (3,), got {vector.shape}" - assert matrix.shape == (2, 2), f"Matrix shape should be (2, 2), got {matrix.shape}" - assert tensor_3d.shape == (2, 2, 2), f"3D tensor shape should be (2, 2, 2), got {tensor_3d.shape}" - - print("โœ… Shape property: scalar, vector, matrix, and 3D tensor") - tests_passed += 1 - except Exception as e: - print(f"โŒ Shape property failed: {e}") - - # Test 2: Size property - try: - scalar = Tensor(5.0) - vector = Tensor([1, 2, 3]) - matrix = Tensor([[1, 2], [3, 4]]) - empty = Tensor([]) - - assert scalar.size == 1, f"Scalar size should be 1, got {scalar.size}" - assert vector.size == 3, f"Vector size should be 3, got {vector.size}" - assert matrix.size == 4, f"Matrix size should be 4, got {matrix.size}" - assert empty.size == 0, f"Empty tensor size should be 0, got {empty.size}" - - print("โœ… Size property: scalar, vector, matrix, and empty tensor") - tests_passed += 1 - except Exception as e: - print(f"โŒ Size property failed: {e}") - - # Test 3: Data type property - try: - int_tensor = Tensor([1, 2, 3]) - float_tensor = Tensor([1.0, 2.0, 3.0]) - - # Check that dtype is accessible and reasonable - assert hasattr(int_tensor, 'dtype'), "Tensor should have dtype property" - assert hasattr(float_tensor, 'dtype'), "Tensor should have dtype property" - - # Data types should be NumPy dtypes - assert isinstance(int_tensor.dtype, np.dtype), f"dtype should be np.dtype, got {type(int_tensor.dtype)}" - assert isinstance(float_tensor.dtype, np.dtype), f"dtype should be np.dtype, got {type(float_tensor.dtype)}" - - print(f"โœ… Data type property: int tensor is {int_tensor.dtype}, float tensor is {float_tensor.dtype}") - tests_passed += 1 - except Exception as e: - print(f"โŒ Data type property failed: {e}") - - # Test 4: Data access property - try: - scalar = Tensor(5.0) - vector = Tensor([1, 2, 3]) - matrix = Tensor([[1, 2], [3, 4]]) - - # Test data access - assert hasattr(scalar, 'data'), "Tensor should have data property" - assert hasattr(vector, 'data'), "Tensor should have data property" - assert hasattr(matrix, 'data'), "Tensor should have data property" - - # Test data content - assert scalar.data.item() == 5.0, f"Scalar data should be 5.0, got {scalar.data.item()}" - assert np.array_equal(vector.data, np.array([1, 2, 3])), "Vector data mismatch" - assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), "Matrix data mismatch" - - print("โœ… Data access: scalar, vector, and matrix data retrieval") - tests_passed += 1 - except Exception as e: - print(f"โŒ Data access failed: {e}") - - # Test 5: String representation - try: - scalar = Tensor(5.0) - vector = Tensor([1, 2, 3]) - - # Test that __repr__ works - scalar_str = str(scalar) - vector_str = str(vector) - - assert isinstance(scalar_str, str), "Tensor string representation should be a string" - assert isinstance(vector_str, str), "Tensor string representation should be a string" - assert len(scalar_str) > 0, "Tensor string representation should not be empty" - assert len(vector_str) > 0, "Tensor string representation should not be empty" - - print(f"โœ… String representation: scalar={scalar_str[:50]}{'...' if len(scalar_str) > 50 else ''}") - tests_passed += 1 - except Exception as e: - print(f"โŒ String representation failed: {e}") - - # Test 6: Property consistency - try: - test_cases = [ - Tensor(42), - Tensor([1, 2, 3, 4, 5]), - Tensor([[1, 2, 3], [4, 5, 6]]), - Tensor([]) - ] - - for i, tensor in enumerate(test_cases): - # Size should equal product of shape - expected_size = np.prod(tensor.shape) if tensor.shape else 1 - assert tensor.size == expected_size, f"Test case {i}: size {tensor.size} doesn't match shape {tensor.shape}" - - # Data shape should match tensor shape - assert tensor.data.shape == tensor.shape, f"Test case {i}: data shape {tensor.data.shape} doesn't match tensor shape {tensor.shape}" - - print("โœ… Property consistency: size matches shape, data shape matches tensor shape") - tests_passed += 1 - except Exception as e: - print(f"โŒ Property consistency failed: {e}") - - # Results summary - print(f"\n๐Ÿ“Š Tensor Properties Results: {tests_passed}/{total_tests} tests passed") - - if tests_passed == total_tests: - print("๐ŸŽ‰ All tensor property tests passed! Your tensor has:") - print(" โ€ข Correct shape property for all dimensions") - print(" โ€ข Accurate size calculation") - print(" โ€ข Proper data type handling") - print(" โ€ข Working data access") - print(" โ€ข Good string representation") - print("๐Ÿ“ˆ Progress: Tensor Creation โœ“, Properties โœ“") - return True - else: - print("โš ๏ธ Some property tests failed. Common issues:") - print(" โ€ข Check your @property decorators") - print(" โ€ข Verify shape returns self._data.shape") - print(" โ€ข Make sure size returns self._data.size") - print(" โ€ข Ensure dtype returns self._data.dtype") - print(" โ€ข Test your __repr__ method") - return False - -# Run the comprehensive test -success = test_tensor_properties() and success - -# %% [markdown] -""" -### ๐Ÿงช Comprehensive Test: Tensor Arithmetic - -Let's test all arithmetic operations. These are the foundation of neural network computations! -""" - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-arithmetic-comprehensive", "locked": true, "points": 20, "schema_version": 3, "solution": false, "task": false} -def test_tensor_arithmetic(): - """Comprehensive test of tensor arithmetic operations.""" - print("๐Ÿ”ฌ Testing comprehensive tensor arithmetic...") - - tests_passed = 0 - total_tests = 8 - - # Test 1: Basic addition method - try: - a = Tensor([1, 2, 3]) - b = Tensor([4, 5, 6]) - c = a.add(b) - - expected = np.array([5, 7, 9]) - assert np.array_equal(c.data, expected), f"Addition method failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "Addition should return a Tensor" - - print(f"โœ… Addition method: {a.data} + {b.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ Addition method failed: {e}") - - # Test 2: Basic multiplication method - try: - a = Tensor([1, 2, 3]) - b = Tensor([4, 5, 6]) - c = a.multiply(b) - - expected = np.array([4, 10, 18]) - assert np.array_equal(c.data, expected), f"Multiplication method failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "Multiplication should return a Tensor" - - print(f"โœ… Multiplication method: {a.data} * {b.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ Multiplication method failed: {e}") - - # Test 3: Addition operator (+) - try: - a = Tensor([1, 2, 3]) - b = Tensor([4, 5, 6]) - c = a + b - - expected = np.array([5, 7, 9]) - assert np.array_equal(c.data, expected), f"+ operator failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "+ operator should return a Tensor" - - print(f"โœ… + operator: {a.data} + {b.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ + operator failed: {e}") - - # Test 4: Multiplication operator (*) - try: - a = Tensor([1, 2, 3]) - b = Tensor([4, 5, 6]) - c = a * b - - expected = np.array([4, 10, 18]) - assert np.array_equal(c.data, expected), f"* operator failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "* operator should return a Tensor" - - print(f"โœ… * operator: {a.data} * {b.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ * operator failed: {e}") - - # Test 5: Subtraction operator (-) - try: - a = Tensor([1, 2, 3]) - b = Tensor([4, 5, 6]) - c = b - a - - expected = np.array([3, 3, 3]) - assert np.array_equal(c.data, expected), f"- operator failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "- operator should return a Tensor" - - print(f"โœ… - operator: {b.data} - {a.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ - operator failed: {e}") - - # Test 6: Division operator (/) - try: - a = Tensor([1, 2, 4]) - b = Tensor([2, 4, 8]) - c = b / a - - expected = np.array([2.0, 2.0, 2.0]) - assert np.allclose(c.data, expected), f"/ operator failed: expected {expected}, got {c.data}" - assert isinstance(c, Tensor), "/ operator should return a Tensor" - - print(f"โœ… / operator: {b.data} / {a.data} = {c.data}") - tests_passed += 1 - except Exception as e: - print(f"โŒ / operator failed: {e}") - - # Test 7: Scalar operations - try: - a = Tensor([1, 2, 3]) - - # Addition with scalar - b = a + 10 - expected_add = np.array([11, 12, 13]) - assert np.array_equal(b.data, expected_add), f"Scalar addition failed: expected {expected_add}, got {b.data}" - - # Multiplication with scalar - c = a * 2 - expected_mul = np.array([2, 4, 6]) - assert np.array_equal(c.data, expected_mul), f"Scalar multiplication failed: expected {expected_mul}, got {c.data}" - - # Subtraction with scalar - d = a - 1 - expected_sub = np.array([0, 1, 2]) - assert np.array_equal(d.data, expected_sub), f"Scalar subtraction failed: expected {expected_sub}, got {d.data}" - - # Division with scalar - e = a / 2 - expected_div = np.array([0.5, 1.0, 1.5]) - assert np.allclose(e.data, expected_div), f"Scalar division failed: expected {expected_div}, got {e.data}" - - print(f"โœ… Scalar operations: +10, *2, -1, /2 all work correctly") - tests_passed += 1 - except Exception as e: - print(f"โŒ Scalar operations failed: {e}") - - # Test 8: Matrix operations - try: - matrix_a = Tensor([[1, 2], [3, 4]]) - matrix_b = Tensor([[5, 6], [7, 8]]) - - # Matrix addition - c = matrix_a + matrix_b - expected = np.array([[6, 8], [10, 12]]) - assert np.array_equal(c.data, expected), f"Matrix addition failed: expected {expected}, got {c.data}" - assert c.shape == (2, 2), f"Matrix addition should preserve shape, got {c.shape}" - - # Matrix multiplication (element-wise) - d = matrix_a * matrix_b - expected_mul = np.array([[5, 12], [21, 32]]) - assert np.array_equal(d.data, expected_mul), f"Matrix multiplication failed: expected {expected_mul}, got {d.data}" - - print(f"โœ… Matrix operations: 2x2 matrix addition and multiplication") - tests_passed += 1 - except Exception as e: - print(f"โŒ Matrix operations failed: {e}") - - # Results summary - print(f"\n๐Ÿ“Š Tensor Arithmetic Results: {tests_passed}/{total_tests} tests passed") - - if tests_passed == total_tests: - print("๐ŸŽ‰ All tensor arithmetic tests passed! Your tensor supports:") - print(" โ€ข Basic methods: add(), multiply()") - print(" โ€ข Python operators: +, -, *, /") - print(" โ€ข Scalar operations: tensor + number") - print(" โ€ข Matrix operations: element-wise operations") - print("๐Ÿ“ˆ Progress: Tensor Creation โœ“, Properties โœ“, Arithmetic โœ“") - return True - else: - print("โš ๏ธ Some arithmetic tests failed. Common issues:") - print(" โ€ข Check your add() and multiply() methods") - print(" โ€ข Verify operator overloading (__add__, __mul__, __sub__, __truediv__)") - print(" โ€ข Make sure scalar operations work (convert scalar to Tensor)") - print(" โ€ข Test with different tensor shapes") - return False - -# Run the comprehensive test -success = test_tensor_arithmetic() and success - -# %% [markdown] -""" -### ๐Ÿงช Comprehensive Test: Real ML Scenario - -Let's test your tensor with a realistic machine learning scenario to make sure everything works together. -""" - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-comprehensive", "locked": true, "points": 10, "schema_version": 3, "solution": false, "task": false} -def test_tensor(): - """Comprehensive test with realistic ML scenario.""" - print("๐Ÿ”ฌ Testing tensor comprehensively with ML scenario...") - - try: - print("๐Ÿง  Simulating a simple neural network forward pass...") - - # Simulate input data (batch of 2 samples, 3 features each) - X = Tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) - print(f"๐Ÿ“Š Input data shape: {X.shape}") - - # Simulate weights (3 input features, 2 output neurons) - W = Tensor([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]) - print(f"๐ŸŽฏ Weights shape: {W.shape}") - - # Simulate bias (2 output neurons) - b = Tensor([0.1, 0.2]) - print(f"โš–๏ธ Bias shape: {b.shape}") - - # Simple linear transformation: y = X * W + b - # Note: This is a simplified version - real matrix multiplication would be different - # But we can test element-wise operations - - # Test that we can do basic operations needed for ML - sample = Tensor([1.0, 2.0, 3.0]) # Single sample - weight_col = Tensor([0.1, 0.3, 0.5]) # First column of weights - - # Compute dot product manually using element-wise operations - products = sample * weight_col # Element-wise multiplication - print(f"โœ… Element-wise multiplication works: {products.data}") - - # Test addition for bias - result = products + Tensor([0.1, 0.1, 0.1]) - print(f"โœ… Bias addition works: {result.data}") - - # Test with different shapes - matrix_a = Tensor([[1, 2], [3, 4]]) - matrix_b = Tensor([[0.1, 0.2], [0.3, 0.4]]) - matrix_result = matrix_a * matrix_b - print(f"โœ… Matrix operations work: {matrix_result.data}") - - # Test scalar operations (common in ML) - scaled = sample * 0.5 # Learning rate scaling - print(f"โœ… Scalar scaling works: {scaled.data}") - - # Test normalization-like operations - mean_val = Tensor([2.0, 2.0, 2.0]) # Simulate mean - normalized = sample - mean_val - print(f"โœ… Mean subtraction works: {normalized.data}") - - print("\n๐ŸŽ‰ Comprehensive test passed! Your tensor class can handle:") - print(" โ€ข Multi-dimensional data (batches, features)") - print(" โ€ข Element-wise operations needed for ML") - print(" โ€ข Scalar operations (learning rates, normalization)") - print(" โ€ข Matrix operations (weights, transformations)") - print("๐Ÿ“ˆ Progress: All tensor functionality โœ“") - print("๐Ÿš€ Ready for neural network layers!") - - return True - - except Exception as e: - print(f"โŒ Comprehensive test failed: {e}") - print("\n๐Ÿ’ก This suggests an issue with:") - print(" โ€ข Basic tensor operations not working together") - print(" โ€ข Shape handling problems") - print(" โ€ข Arithmetic operation implementation") - print(" โ€ข Check your tensor creation and arithmetic methods") - return False - -# Run the comprehensive test -success = test_tensor() and success - -# Print final summary -print(f"\n{'='*60}") -print("๐ŸŽฏ TENSOR MODULE TESTING COMPLETE") -print(f"{'='*60}") - -if success: - print("๐ŸŽ‰ CONGRATULATIONS! All tensor tests passed!") - print("\nโœ… Your Tensor class successfully implements:") - print(" โ€ข Comprehensive tensor creation (scalars, vectors, matrices)") - print(" โ€ข All essential properties (shape, size, dtype, data access)") - print(" โ€ข Complete arithmetic operations (methods and operators)") - print(" โ€ข Scalar and matrix operations") - print(" โ€ข Real ML scenario compatibility") - print("\n๐Ÿš€ You're ready to move to the next module!") - print("๐Ÿ“ˆ Final Progress: Tensor Module โœ“ COMPLETE") -else: - print("โš ๏ธ Some tests failed. Please review the error messages above.") - print("\n๐Ÿ”ง To fix issues:") - print(" 1. Check the specific test that failed") - print(" 2. Review the error message and hints") - print(" 3. Fix your implementation") - print(" 4. Re-run the notebook cells") - print("\n๐Ÿ’ช Don't give up! Debugging is part of learning.") - -# %% [markdown] -""" -## Step 3: Tensor Arithmetic Operations - -### Why Arithmetic Matters -Tensor arithmetic is the foundation of all neural network operations: -- **Forward pass**: Matrix multiplications and additions -- **Activation functions**: Element-wise operations -- **Loss computation**: Differences and squares -- **Gradient computation**: Chain rule applications - -### Operations We'll Implement -- **Addition**: Element-wise addition of tensors -- **Multiplication**: Element-wise multiplication -- **Python operators**: `+`, `-`, `*`, `/` for natural syntax -- **Broadcasting**: Handle different shapes automatically -""" - -# %% [markdown] -""" -## Step 3: Tensor Arithmetic Methods - -The arithmetic methods are now part of the Tensor class above. Let's test them! -""" - -# %% [markdown] -""" -## Step 4: Python Operator Overloading - -### Why Operator Overloading? -Python's magic methods allow us to use natural syntax: -- `a + b` instead of `a.add(b)` -- `a * b` instead of `a.multiply(b)` -- `a - b` for subtraction -- `a / b` for division - -This makes tensor operations feel natural and readable. -""" - -# %% [markdown] -""" -## Step 4: Operator Overloading - -The operator methods (__add__, __mul__, __sub__, __truediv__) are now part of the Tensor class above. This enables natural syntax like `a + b` and `a * b`. -""" - -# %% [markdown] -""" -### ๐Ÿงช Test Your Tensor Implementation - -Once you implement the Tensor class above, run these cells to test your implementation: -""" - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-creation", "locked": true, "points": 25, "schema_version": 3, "solution": false, "task": false} -# Test tensor creation and properties -print("๐Ÿ”ฌ Unit Test: Tensor Creation...") - -# Test scalar creation -scalar = Tensor(5.0) -assert scalar.shape == (), f"Scalar shape should be (), got {scalar.shape}" -assert scalar.size == 1, f"Scalar size should be 1, got {scalar.size}" -assert scalar.data.item() == 5.0, f"Scalar value should be 5.0, got {scalar.data.item()}" - -# Test vector creation -vector = Tensor([1, 2, 3]) -assert vector.shape == (3,), f"Vector shape should be (3,), got {vector.shape}" -assert vector.size == 3, f"Vector size should be 3, got {vector.size}" -assert np.array_equal(vector.data, np.array([1, 2, 3])), "Vector data mismatch" - -# Test matrix creation -matrix = Tensor([[1, 2], [3, 4]]) -assert matrix.shape == (2, 2), f"Matrix shape should be (2, 2), got {matrix.shape}" -assert matrix.size == 4, f"Matrix size should be 4, got {matrix.size}" -assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), "Matrix data mismatch" - -# Test dtype handling -float_tensor = Tensor([1.0, 2.0, 3.0]) -assert float_tensor.dtype == np.float32, f"Float tensor dtype should be float32, got {float_tensor.dtype}" - -int_tensor = Tensor([1, 2, 3]) -# Note: NumPy may default to int64 on some systems, so we check for integer types -assert int_tensor.dtype in [np.int32, np.int64], f"Int tensor dtype should be int32 or int64, got {int_tensor.dtype}" - -print("โœ… Tensor creation tests passed!") -print(f"โœ… Scalar: {scalar}") -print(f"โœ… Vector: {vector}") -print(f"โœ… Matrix: {matrix}") - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-arithmetic", "locked": true, "points": 25, "schema_version": 3, "solution": false, "task": false} -# Test tensor arithmetic operations -print("๐Ÿ”ฌ Unit Test: Tensor Arithmetic...") - -# Test addition -a = Tensor([1, 2, 3]) -b = Tensor([4, 5, 6]) -c = a + b -expected = np.array([5, 7, 9]) -assert np.array_equal(c.data, expected), f"Addition failed: expected {expected}, got {c.data}" - -# Test multiplication -d = a * b -expected = np.array([4, 10, 18]) -assert np.array_equal(d.data, expected), f"Multiplication failed: expected {expected}, got {d.data}" - -# Test subtraction -e = b - a -expected = np.array([3, 3, 3]) -assert np.array_equal(e.data, expected), f"Subtraction failed: expected {expected}, got {e.data}" - -# Test division -f = b / a -expected = np.array([4.0, 2.5, 2.0]) -assert np.allclose(f.data, expected), f"Division failed: expected {expected}, got {f.data}" - -# Test scalar operations -g = a + 10 -expected = np.array([11, 12, 13]) -assert np.array_equal(g.data, expected), f"Scalar addition failed: expected {expected}, got {g.data}" - -h = a * 2 -expected = np.array([2, 4, 6]) -assert np.array_equal(h.data, expected), f"Scalar multiplication failed: expected {expected}, got {h.data}" - -print("โœ… Tensor arithmetic tests passed!") -print(f"โœ… Addition: {a} + {b} = {c}") -print(f"โœ… Multiplication: {a} * {b} = {d}") -print(f"โœ… Subtraction: {b} - {a} = {e}") -print(f"โœ… Division: {b} / {a} = {f}") - -# %% nbgrader={"grade": true, "grade_id": "test-tensor-broadcasting", "locked": true, "points": 25, "schema_version": 3, "solution": false, "task": false} -# Test tensor broadcasting -print("๐Ÿ”ฌ Unit Test: Tensor Broadcasting...") - -# Test scalar broadcasting -matrix = Tensor([[1, 2], [3, 4]]) -scalar = Tensor(10) -result = matrix + scalar -expected = np.array([[11, 12], [13, 14]]) -assert np.array_equal(result.data, expected), f"Scalar broadcasting failed: expected {expected}, got {result.data}" - -# Test vector broadcasting -vector = Tensor([1, 2]) -result = matrix + vector -expected = np.array([[2, 4], [4, 6]]) -assert np.array_equal(result.data, expected), f"Vector broadcasting failed: expected {expected}, got {result.data}" - -# Test different shapes -a = Tensor([[1], [2], [3]]) # (3, 1) -b = Tensor([10, 20]) # (2,) -result = a + b -expected = np.array([[11, 21], [12, 22], [13, 23]]) -assert np.array_equal(result.data, expected), f"Shape broadcasting failed: expected {expected}, got {result.data}" - -print("โœ… Tensor broadcasting tests passed!") -print(f"โœ… Matrix + Scalar: {matrix} + {scalar} = {result}") -print(f"โœ… Broadcasting works correctly!") - -# %% [markdown] -""" -## ๐ŸŽฏ Module Summary - -Congratulations! You've successfully implemented the core Tensor class for TinyTorch: - -### What You've Accomplished -โœ… **Tensor Creation**: Handle scalars, vectors, matrices, and higher-dimensional arrays -โœ… **Data Types**: Proper dtype handling with auto-detection and conversion -โœ… **Properties**: Shape, size, dtype, and data access -โœ… **Arithmetic**: Addition, multiplication, subtraction, division -โœ… **Operators**: Natural Python syntax with `+`, `-`, `*`, `/` -โœ… **Broadcasting**: Automatic shape compatibility like NumPy - -### Key Concepts You've Learned -- **Tensors** are the fundamental data structure for ML systems -- **NumPy backend** provides efficient computation with ML-friendly API -- **Operator overloading** makes tensor operations feel natural -- **Broadcasting** enables flexible operations between different shapes -- **Type safety** ensures consistent behavior across operations -""" - -# %% [markdown] -""" -## ๐Ÿงช Module Testing - -Time to test your implementation! This section uses TinyTorch's standardized testing framework to ensure your implementation works correctly. - -**This testing section is locked** - it provides consistent feedback across all modules and cannot be modified. -""" - -# %% nbgrader={"grade": false, "grade_id": "standardized-testing", "locked": true, "schema_version": 3, "solution": false, "task": false} -# ============================================================================= -# STANDARDIZED MODULE TESTING - DO NOT MODIFY -# This cell is locked to ensure consistent testing across all TinyTorch modules -# ============================================================================= - -if __name__ == "__main__": - from tito.tools.testing import run_module_tests_auto - - # Automatically discover and run all tests in this module - success = run_module_tests_auto("Tensor") - -# %% [markdown] -""" -## ๐ŸŽฏ Module Summary - Congratulations! You've successfully implemented the core Tensor class for TinyTorch: ### What You've Accomplished @@ -1576,4 +813,68 @@ Congratulations! You've successfully implemented the core Tensor class for TinyT 4. **Move to Module 2**: Start building activation functions! **Ready for the next challenge?** Let's add the mathematical functions that make neural networks powerful! -""" \ No newline at end of file +""" + +def test_unit_tensor_creation(): + """Comprehensive test of tensor creation with all data types and shapes.""" + print("๐Ÿ”ฌ Testing comprehensive tensor creation...") + + # Test scalar creation + scalar_int = Tensor(42) + assert scalar_int.shape == () + + # Test vector creation + vector_int = Tensor([1, 2, 3]) + assert vector_int.shape == (3,) + + # Test matrix creation + matrix_2x2 = Tensor([[1, 2], [3, 4]]) + assert matrix_2x2.shape == (2, 2) + print("โœ… Tensor creation tests passed!") + +def test_unit_tensor_properties(): + """Comprehensive test of tensor properties (shape, size, dtype, data access).""" + print("๐Ÿ”ฌ Testing comprehensive tensor properties...") + + tensor = Tensor([[1, 2, 3], [4, 5, 6]]) + + # Test shape property + assert tensor.shape == (2, 3) + + # Test size property + assert tensor.size == 6 + + # Test data property + assert np.array_equal(tensor.data, np.array([[1, 2, 3], [4, 5, 6]])) + + # Test dtype property + assert tensor.dtype in [np.int32, np.int64] + print("โœ… Tensor properties tests passed!") + +def test_unit_tensor_arithmetic(): + """Comprehensive test of tensor arithmetic operations.""" + print("๐Ÿ”ฌ Testing comprehensive tensor arithmetic...") + + a = Tensor([1, 2, 3]) + b = Tensor([4, 5, 6]) + + # Test addition + c = a + b + expected = np.array([5, 7, 9]) + assert np.array_equal(c.data, expected) + + # Test multiplication + d = a * b + expected = np.array([4, 10, 18]) + assert np.array_equal(d.data, expected) + + # Test subtraction + e = b - a + expected = np.array([3, 3, 3]) + assert np.array_equal(e.data, expected) + + # Test division + f = b / a + expected = np.array([4.0, 2.5, 2.0]) + assert np.allclose(f.data, expected) + print("โœ… Tensor arithmetic tests passed!") \ No newline at end of file