mirror of
https://github.com/MLSysBook/TinyTorch.git
synced 2026-05-01 10:45:54 -05:00
🔧 TESTING INFRASTRUCTURE FIXES: - Fixed pytest configuration (removed duplicate timeout) - Exported all modules to tinytorch package using nbdev - Converted .py files to .ipynb for proper NBDev processing - Fixed import issues in test files with fallback strategies 📊 TESTING RESULTS: - 145 tests passing, 15 failing, 16 skipped - Major improvement from previous import errors - All modules now properly exported and testable - Analysis tool working correctly on all modules 🎯 MODULE QUALITY STATUS: - Most modules: Grade C, Scaffolding 3/5 - 01_tensor: Grade C, Scaffolding 2/5 (needs improvement) - 07_autograd: Grade D, Scaffolding 2/5 (needs improvement) - Overall: Functional but needs educational enhancement ✅ RESOLVED ISSUES: - All import errors resolved - NBDev export process working - Test infrastructure functional - Analysis tools operational 🚀 READY FOR NEXT PHASE: Professional report cards and improvements
1853 lines
76 KiB
Plaintext
1853 lines
76 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d889922d",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"# Module 1: Tensor - Core Data Structure\n",
|
||
"\n",
|
||
"Welcome to the Tensor module! This is where TinyTorch really begins. You'll implement the fundamental data structure that powers all ML systems.\n",
|
||
"\n",
|
||
"## Learning Goals\n",
|
||
"- Understand tensors as N-dimensional arrays with ML-specific operations\n",
|
||
"- Implement a complete Tensor class with arithmetic operations\n",
|
||
"- Handle shape management, data types, and memory layout\n",
|
||
"- Build the foundation for neural networks and automatic differentiation\n",
|
||
"- Master the NBGrader workflow with comprehensive testing\n",
|
||
"\n",
|
||
"## Build → Use → Understand\n",
|
||
"1. **Build**: Create the Tensor class with core operations\n",
|
||
"2. **Use**: Perform tensor arithmetic and transformations\n",
|
||
"3. **Understand**: How tensors form the foundation of ML systems"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "4a146c17",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "tensor-imports",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#| default_exp core.tensor\n",
|
||
"\n",
|
||
"#| export\n",
|
||
"import numpy as np\n",
|
||
"import sys\n",
|
||
"from typing import Union, List, Tuple, Optional, Any"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "bcebdf84",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "tensor-setup",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"print(\"🔥 TinyTorch Tensor Module\")\n",
|
||
"print(f\"NumPy version: {np.__version__}\")\n",
|
||
"print(f\"Python version: {sys.version_info.major}.{sys.version_info.minor}\")\n",
|
||
"print(\"Ready to build tensors!\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "ab96dce5",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## 📦 Where This Code Lives in the Final Package\n",
|
||
"\n",
|
||
"**Learning Side:** You work in `modules/source/01_tensor/tensor_dev.py` \n",
|
||
"**Building Side:** Code exports to `tinytorch.core.tensor`\n",
|
||
"\n",
|
||
"```python\n",
|
||
"# Final package structure:\n",
|
||
"from tinytorch.core.tensor import Tensor # The foundation of everything!\n",
|
||
"from tinytorch.core.activations import ReLU, Sigmoid, Tanh\n",
|
||
"from tinytorch.core.layers import Dense, Conv2D\n",
|
||
"```\n",
|
||
"\n",
|
||
"**Why this matters:**\n",
|
||
"- **Learning:** Focused modules for deep understanding\n",
|
||
"- **Production:** Proper organization like PyTorch's `torch.Tensor`\n",
|
||
"- **Consistency:** All tensor operations live together in `core.tensor`\n",
|
||
"- **Foundation:** Every other module depends on Tensor"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "7f474d65",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## Step 1: What is a Tensor?\n",
|
||
"\n",
|
||
"### Definition\n",
|
||
"A **tensor** is an N-dimensional array with ML-specific operations. Think of it as a container that can hold data in multiple dimensions:\n",
|
||
"\n",
|
||
"- **Scalar** (0D): A single number - `5.0`\n",
|
||
"- **Vector** (1D): A list of numbers - `[1, 2, 3]` \n",
|
||
"- **Matrix** (2D): A 2D array - `[[1, 2], [3, 4]]`\n",
|
||
"- **Higher dimensions**: 3D, 4D, etc. for images, video, batches\n",
|
||
"\n",
|
||
"### The Mathematical Foundation: From Scalars to Tensors\n",
|
||
"Understanding tensors requires building from mathematical fundamentals:\n",
|
||
"\n",
|
||
"#### **Scalars (Rank 0)**\n",
|
||
"- **Definition**: A single number with no direction\n",
|
||
"- **Examples**: Temperature (25°C), mass (5.2 kg), probability (0.7)\n",
|
||
"- **Operations**: Addition, multiplication, comparison\n",
|
||
"- **ML Context**: Loss values, learning rates, regularization parameters\n",
|
||
"\n",
|
||
"#### **Vectors (Rank 1)**\n",
|
||
"- **Definition**: An ordered list of numbers with direction and magnitude\n",
|
||
"- **Examples**: Position [x, y, z], RGB color [255, 128, 0], word embedding [0.1, -0.5, 0.8]\n",
|
||
"- **Operations**: Dot product, cross product, norm calculation\n",
|
||
"- **ML Context**: Feature vectors, gradients, model parameters\n",
|
||
"\n",
|
||
"#### **Matrices (Rank 2)**\n",
|
||
"- **Definition**: A 2D array organizing data in rows and columns\n",
|
||
"- **Examples**: Image (height × width), weight matrix (input × output), covariance matrix\n",
|
||
"- **Operations**: Matrix multiplication, transpose, inverse, eigendecomposition\n",
|
||
"- **ML Context**: Linear layer weights, attention matrices, batch data\n",
|
||
"\n",
|
||
"#### **Higher-Order Tensors (Rank 3+)**\n",
|
||
"- **Definition**: Multi-dimensional arrays extending matrices\n",
|
||
"- **Examples**: \n",
|
||
" - **3D**: Video frames (time × height × width), RGB images (height × width × channels)\n",
|
||
" - **4D**: Image batches (batch × height × width × channels)\n",
|
||
" - **5D**: Video batches (batch × time × height × width × channels)\n",
|
||
"- **Operations**: Tensor products, contractions, decompositions\n",
|
||
"- **ML Context**: Convolutional features, RNN states, transformer attention\n",
|
||
"\n",
|
||
"### Why Tensors Matter in ML: The Computational Foundation\n",
|
||
"\n",
|
||
"#### **1. Unified Data Representation**\n",
|
||
"Tensors provide a consistent way to represent all ML data:\n",
|
||
"```python\n",
|
||
"# All of these are tensors with different shapes\n",
|
||
"scalar_loss = Tensor(0.5) # Shape: ()\n",
|
||
"feature_vector = Tensor([1, 2, 3]) # Shape: (3,)\n",
|
||
"weight_matrix = Tensor([[1, 2], [3, 4]]) # Shape: (2, 2)\n",
|
||
"image_batch = Tensor(np.random.rand(32, 224, 224, 3)) # Shape: (32, 224, 224, 3)\n",
|
||
"```\n",
|
||
"\n",
|
||
"#### **2. Efficient Batch Processing**\n",
|
||
"ML systems process multiple samples simultaneously:\n",
|
||
"```python\n",
|
||
"# Instead of processing one image at a time:\n",
|
||
"for image in images:\n",
|
||
" result = model(image) # Slow: 1000 separate operations\n",
|
||
"\n",
|
||
"# Process entire batch at once:\n",
|
||
"batch_result = model(image_batch) # Fast: 1 vectorized operation\n",
|
||
"```\n",
|
||
"\n",
|
||
"#### **3. Hardware Acceleration**\n",
|
||
"Modern hardware (GPUs, TPUs) excels at tensor operations:\n",
|
||
"- **Parallel processing**: Multiple operations simultaneously\n",
|
||
"- **Vectorization**: SIMD (Single Instruction, Multiple Data) operations\n",
|
||
"- **Memory optimization**: Contiguous memory layout for cache efficiency\n",
|
||
"\n",
|
||
"#### **4. Automatic Differentiation**\n",
|
||
"Tensors enable gradient computation through computational graphs:\n",
|
||
"```python\n",
|
||
"# Each tensor operation creates a node in the computation graph\n",
|
||
"x = Tensor([1, 2, 3])\n",
|
||
"y = x * 2 # Node: multiplication\n",
|
||
"z = y + 1 # Node: addition\n",
|
||
"loss = z.sum() # Node: summation\n",
|
||
"# Gradients flow backward through this graph\n",
|
||
"```\n",
|
||
"\n",
|
||
"### Real-World Examples: Tensors in Action\n",
|
||
"\n",
|
||
"#### **Computer Vision**\n",
|
||
"- **Grayscale image**: 2D tensor `(height, width)` - `(28, 28)` for MNIST\n",
|
||
"- **Color image**: 3D tensor `(height, width, channels)` - `(224, 224, 3)` for RGB\n",
|
||
"- **Image batch**: 4D tensor `(batch, height, width, channels)` - `(32, 224, 224, 3)`\n",
|
||
"- **Video**: 5D tensor `(batch, time, height, width, channels)`\n",
|
||
"\n",
|
||
"#### **Natural Language Processing**\n",
|
||
"- **Word embedding**: 1D tensor `(embedding_dim,)` - `(300,)` for Word2Vec\n",
|
||
"- **Sentence**: 2D tensor `(sequence_length, embedding_dim)` - `(50, 768)` for BERT\n",
|
||
"- **Batch of sentences**: 3D tensor `(batch, sequence_length, embedding_dim)`\n",
|
||
"\n",
|
||
"#### **Audio Processing**\n",
|
||
"- **Audio signal**: 1D tensor `(time_steps,)` - `(16000,)` for 1 second at 16kHz\n",
|
||
"- **Spectrogram**: 2D tensor `(time_frames, frequency_bins)`\n",
|
||
"- **Batch of audio**: 3D tensor `(batch, time_steps, features)`\n",
|
||
"\n",
|
||
"#### **Time Series**\n",
|
||
"- **Single series**: 2D tensor `(time_steps, features)`\n",
|
||
"- **Multiple series**: 3D tensor `(batch, time_steps, features)`\n",
|
||
"- **Multivariate forecasting**: 4D tensor `(batch, time_steps, features, predictions)`\n",
|
||
"\n",
|
||
"### Why Not Just Use NumPy?\n",
|
||
"\n",
|
||
"While we use NumPy internally, our Tensor class adds ML-specific functionality:\n",
|
||
"\n",
|
||
"#### **1. ML-Specific Operations**\n",
|
||
"- **Gradient tracking**: For automatic differentiation (coming in Module 7)\n",
|
||
"- **GPU support**: For hardware acceleration (future extension)\n",
|
||
"- **Broadcasting semantics**: ML-friendly dimension handling\n",
|
||
"\n",
|
||
"#### **2. Consistent API**\n",
|
||
"- **Type safety**: Predictable behavior across operations\n",
|
||
"- **Error checking**: Clear error messages for debugging\n",
|
||
"- **Integration**: Seamless work with other TinyTorch components\n",
|
||
"\n",
|
||
"#### **3. Educational Value**\n",
|
||
"- **Conceptual clarity**: Understand what tensors really are\n",
|
||
"- **Implementation insight**: See how frameworks work internally\n",
|
||
"- **Debugging skills**: Trace through tensor operations step by step\n",
|
||
"\n",
|
||
"#### **4. Extensibility**\n",
|
||
"- **Future features**: Ready for gradients, GPU, distributed computing\n",
|
||
"- **Customization**: Add domain-specific operations\n",
|
||
"- **Optimization**: Profile and optimize specific use cases\n",
|
||
"\n",
|
||
"### Performance Considerations: Building Efficient Tensors\n",
|
||
"\n",
|
||
"#### **Memory Layout**\n",
|
||
"- **Contiguous arrays**: Better cache locality and performance\n",
|
||
"- **Data types**: `float32` vs `float64` trade-offs\n",
|
||
"- **Memory sharing**: Avoid unnecessary copies\n",
|
||
"\n",
|
||
"#### **Vectorization**\n",
|
||
"- **SIMD operations**: Single Instruction, Multiple Data\n",
|
||
"- **Broadcasting**: Efficient operations on different shapes\n",
|
||
"- **Batch operations**: Process multiple samples simultaneously\n",
|
||
"\n",
|
||
"#### **Numerical Stability**\n",
|
||
"- **Precision**: Balancing speed and accuracy\n",
|
||
"- **Overflow/underflow**: Handling extreme values\n",
|
||
"- **Gradient flow**: Maintaining numerical stability for training\n",
|
||
"\n",
|
||
"Let's start building our tensor foundation!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1cba0ba4",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## 🧠 The Mathematical Foundation\n",
|
||
"\n",
|
||
"### Linear Algebra Refresher\n",
|
||
"Tensors are generalizations of scalars, vectors, and matrices:\n",
|
||
"\n",
|
||
"```\n",
|
||
"Scalar (0D): 5\n",
|
||
"Vector (1D): [1, 2, 3]\n",
|
||
"Matrix (2D): [[1, 2], [3, 4]]\n",
|
||
"Tensor (3D): [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]\n",
|
||
"```\n",
|
||
"\n",
|
||
"### Why This Matters for Neural Networks\n",
|
||
"- **Forward Pass**: Matrix multiplication between layers\n",
|
||
"- **Batch Processing**: Multiple samples processed simultaneously\n",
|
||
"- **Convolutions**: 3D operations on image data\n",
|
||
"- **Gradients**: Derivatives computed across all dimensions\n",
|
||
"\n",
|
||
"### Connection to Real ML Systems\n",
|
||
"Every major ML framework uses tensors:\n",
|
||
"- **PyTorch**: `torch.Tensor`\n",
|
||
"- **TensorFlow**: `tf.Tensor`\n",
|
||
"- **JAX**: `jax.numpy.ndarray`\n",
|
||
"- **TinyTorch**: `tinytorch.core.tensor.Tensor` (what we're building!)\n",
|
||
"\n",
|
||
"### Performance Considerations\n",
|
||
"- **Memory Layout**: Contiguous arrays for cache efficiency\n",
|
||
"- **Vectorization**: SIMD operations for speed\n",
|
||
"- **Broadcasting**: Efficient operations on different shapes\n",
|
||
"- **Type Consistency**: Avoiding unnecessary conversions"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0b755b99",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\"",
|
||
"lines_to_next_cell": 1
|
||
},
|
||
"source": [
|
||
"## Step 2: The Tensor Class Foundation\n",
|
||
"\n",
|
||
"### Core Concept: Wrapping NumPy with ML Intelligence\n",
|
||
"Our Tensor class wraps NumPy arrays with ML-specific functionality. This design pattern is used by all major ML frameworks:\n",
|
||
"\n",
|
||
"- **PyTorch**: `torch.Tensor` wraps ATen (C++ tensor library)\n",
|
||
"- **TensorFlow**: `tf.Tensor` wraps Eigen (C++ linear algebra library)\n",
|
||
"- **JAX**: `jax.numpy.ndarray` wraps XLA (Google's linear algebra compiler)\n",
|
||
"- **TinyTorch**: `Tensor` wraps NumPy (Python's numerical computing library)\n",
|
||
"\n",
|
||
"### Design Requirements Analysis\n",
|
||
"\n",
|
||
"#### **1. Input Flexibility**\n",
|
||
"Our tensor must handle diverse input types:\n",
|
||
"```python\n",
|
||
"# Scalars (Python numbers)\n",
|
||
"t1 = Tensor(5) # int → numpy array\n",
|
||
"t2 = Tensor(3.14) # float → numpy array\n",
|
||
"\n",
|
||
"# Lists (Python sequences)\n",
|
||
"t3 = Tensor([1, 2, 3]) # list → numpy array\n",
|
||
"t4 = Tensor([[1, 2], [3, 4]]) # nested list → 2D array\n",
|
||
"\n",
|
||
"# NumPy arrays (existing arrays)\n",
|
||
"t5 = Tensor(np.array([1, 2, 3])) # array → tensor wrapper\n",
|
||
"```\n",
|
||
"\n",
|
||
"#### **2. Type Management**\n",
|
||
"ML systems need consistent, predictable types:\n",
|
||
"- **Default behavior**: Auto-detect appropriate types\n",
|
||
"- **Explicit control**: Allow manual type specification\n",
|
||
"- **Performance optimization**: Prefer `float32` over `float64`\n",
|
||
"- **Memory efficiency**: Use appropriate precision\n",
|
||
"\n",
|
||
"#### **3. Property Access**\n",
|
||
"Essential tensor properties for ML operations:\n",
|
||
"- **Shape**: Dimensions for compatibility checking\n",
|
||
"- **Size**: Total elements for memory estimation\n",
|
||
"- **Data type**: For numerical computation planning\n",
|
||
"- **Data access**: For integration with other libraries\n",
|
||
"\n",
|
||
"#### **4. Arithmetic Operations**\n",
|
||
"Support for mathematical operations:\n",
|
||
"- **Element-wise**: Addition, multiplication, subtraction, division\n",
|
||
"- **Broadcasting**: Operations on different shapes\n",
|
||
"- **Type promotion**: Consistent result types\n",
|
||
"- **Error handling**: Clear messages for incompatible operations\n",
|
||
"\n",
|
||
"### Implementation Strategy\n",
|
||
"\n",
|
||
"#### **Memory Management**\n",
|
||
"- **Copy vs. Reference**: When to copy data vs. share memory\n",
|
||
"- **Type conversion**: Efficient dtype changes\n",
|
||
"- **Contiguous layout**: Ensure optimal memory access patterns\n",
|
||
"\n",
|
||
"#### **Error Handling**\n",
|
||
"- **Input validation**: Check for valid input types\n",
|
||
"- **Shape compatibility**: Verify operations are mathematically valid\n",
|
||
"- **Informative messages**: Help users debug issues quickly\n",
|
||
"\n",
|
||
"#### **Performance Optimization**\n",
|
||
"- **Lazy evaluation**: Defer expensive operations when possible\n",
|
||
"- **Vectorization**: Use NumPy's optimized operations\n",
|
||
"- **Memory reuse**: Minimize unnecessary allocations\n",
|
||
"\n",
|
||
"### Learning Objectives for Implementation\n",
|
||
"\n",
|
||
"By implementing this Tensor class, you'll learn:\n",
|
||
"1. **Wrapper pattern**: How to extend existing libraries\n",
|
||
"2. **Type system design**: Managing data types in numerical computing\n",
|
||
"3. **API design**: Creating intuitive, consistent interfaces\n",
|
||
"4. **Performance considerations**: Balancing flexibility and speed\n",
|
||
"5. **Error handling**: Providing helpful feedback to users\n",
|
||
"\n",
|
||
"Let's implement our tensor foundation!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "8e4f7ece",
|
||
"metadata": {
|
||
"lines_to_next_cell": 1,
|
||
"nbgrader": {
|
||
"grade": false,
|
||
"grade_id": "tensor-class",
|
||
"locked": false,
|
||
"schema_version": 3,
|
||
"solution": true,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"#| export\n",
|
||
"class Tensor:\n",
|
||
" \"\"\"\n",
|
||
" TinyTorch Tensor: N-dimensional array with ML operations.\n",
|
||
" \n",
|
||
" The fundamental data structure for all TinyTorch operations.\n",
|
||
" Wraps NumPy arrays with ML-specific functionality.\n",
|
||
" \"\"\"\n",
|
||
" \n",
|
||
" def __init__(self, data: Union[int, float, List, np.ndarray], dtype: Optional[str] = None):\n",
|
||
" \"\"\"\n",
|
||
" Create a new tensor from data.\n",
|
||
" \n",
|
||
" Args:\n",
|
||
" data: Input data (scalar, list, or numpy array)\n",
|
||
" dtype: Data type ('float32', 'int32', etc.). Defaults to auto-detect.\n",
|
||
" \n",
|
||
" TODO: Implement tensor creation with proper type handling.\n",
|
||
" \n",
|
||
" STEP-BY-STEP:\n",
|
||
" 1. Check if data is a scalar (int/float) - convert to numpy array\n",
|
||
" 2. Check if data is a list - convert to numpy array \n",
|
||
" 3. Check if data is already a numpy array - use as-is\n",
|
||
" 4. Apply dtype conversion if specified\n",
|
||
" 5. Store the result in self._data\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor(5) → stores np.array(5)\n",
|
||
" Tensor([1, 2, 3]) → stores np.array([1, 2, 3])\n",
|
||
" Tensor(np.array([1, 2, 3])) → stores the array directly\n",
|
||
" \n",
|
||
" HINTS:\n",
|
||
" - Use isinstance() to check data types\n",
|
||
" - Use np.array() for conversion\n",
|
||
" - Handle dtype parameter for type conversion\n",
|
||
" - Store the array in self._data\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" # Convert input to numpy array\n",
|
||
" if isinstance(data, (int, float, np.number)):\n",
|
||
" # Handle Python and NumPy scalars\n",
|
||
" if dtype is None:\n",
|
||
" # Auto-detect type: int for integers, float32 for floats\n",
|
||
" if isinstance(data, int) or (isinstance(data, np.number) and np.issubdtype(type(data), np.integer)):\n",
|
||
" dtype = 'int32'\n",
|
||
" else:\n",
|
||
" dtype = 'float32'\n",
|
||
" self._data = np.array(data, dtype=dtype)\n",
|
||
" elif isinstance(data, list):\n",
|
||
" # Let NumPy auto-detect type, then convert if needed\n",
|
||
" temp_array = np.array(data)\n",
|
||
" if dtype is None:\n",
|
||
" # Use NumPy's auto-detected type, but prefer float32 for floats\n",
|
||
" if temp_array.dtype == np.float64:\n",
|
||
" dtype = 'float32'\n",
|
||
" else:\n",
|
||
" dtype = str(temp_array.dtype)\n",
|
||
" self._data = np.array(data, dtype=dtype)\n",
|
||
" elif isinstance(data, np.ndarray):\n",
|
||
" # Already a numpy array\n",
|
||
" if dtype is None:\n",
|
||
" # Keep existing dtype, but prefer float32 for float64\n",
|
||
" if data.dtype == np.float64:\n",
|
||
" dtype = 'float32'\n",
|
||
" else:\n",
|
||
" dtype = str(data.dtype)\n",
|
||
" self._data = data.astype(dtype) if dtype != data.dtype else data.copy()\n",
|
||
" else:\n",
|
||
" # Try to convert unknown types\n",
|
||
" self._data = np.array(data, dtype=dtype)\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" @property\n",
|
||
" def data(self) -> np.ndarray:\n",
|
||
" \"\"\"\n",
|
||
" Access underlying numpy array.\n",
|
||
" \n",
|
||
" TODO: Return the stored numpy array.\n",
|
||
" \n",
|
||
" HINT: Return self._data (the array you stored in __init__)\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" return self._data\n",
|
||
" ### END SOLUTION\n",
|
||
" \n",
|
||
" @property\n",
|
||
" def shape(self) -> Tuple[int, ...]:\n",
|
||
" \"\"\"\n",
|
||
" Get tensor shape.\n",
|
||
" \n",
|
||
" TODO: Return the shape of the stored numpy array.\n",
|
||
" \n",
|
||
" HINT: Use .shape attribute of the numpy array\n",
|
||
" EXAMPLE: Tensor([1, 2, 3]).shape should return (3,)\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" return self._data.shape\n",
|
||
" ### END SOLUTION\n",
|
||
" \n",
|
||
" @property\n",
|
||
" def size(self) -> int:\n",
|
||
" \"\"\"\n",
|
||
" Get total number of elements.\n",
|
||
" \n",
|
||
" TODO: Return the total number of elements in the tensor.\n",
|
||
" \n",
|
||
" HINT: Use .size attribute of the numpy array\n",
|
||
" EXAMPLE: Tensor([1, 2, 3]).size should return 3\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" return self._data.size\n",
|
||
" ### END SOLUTION\n",
|
||
" \n",
|
||
" @property\n",
|
||
" def dtype(self) -> np.dtype:\n",
|
||
" \"\"\"\n",
|
||
" Get data type as numpy dtype.\n",
|
||
" \n",
|
||
" TODO: Return the data type of the stored numpy array.\n",
|
||
" \n",
|
||
" HINT: Use .dtype attribute of the numpy array\n",
|
||
" EXAMPLE: Tensor([1, 2, 3]).dtype should return dtype('int32')\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" return self._data.dtype\n",
|
||
" ### END SOLUTION\n",
|
||
" \n",
|
||
" def __repr__(self) -> str:\n",
|
||
" \"\"\"\n",
|
||
" String representation.\n",
|
||
" \n",
|
||
" TODO: Create a clear string representation of the tensor.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. Convert the numpy array to a list for readable output\n",
|
||
" 2. Include the shape and dtype information\n",
|
||
" 3. Format: \"Tensor([data], shape=shape, dtype=dtype)\"\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([1, 2, 3]) → \"Tensor([1, 2, 3], shape=(3,), dtype=int32)\"\n",
|
||
" \n",
|
||
" HINTS:\n",
|
||
" - Use .tolist() to convert numpy array to list\n",
|
||
" - Include shape and dtype information\n",
|
||
" - Keep format consistent and readable\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" return f\"Tensor({self._data.tolist()}, shape={self.shape}, dtype={self.dtype})\"\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def add(self, other: 'Tensor') -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Add two tensors element-wise.\n",
|
||
" \n",
|
||
" TODO: Implement tensor addition.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. Add the numpy arrays using +\n",
|
||
" 2. Return a new Tensor with the result\n",
|
||
" 3. Handle broadcasting automatically\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([1, 2]) + Tensor([3, 4]) → Tensor([4, 6])\n",
|
||
" \n",
|
||
" HINTS:\n",
|
||
" - Use self._data + other._data\n",
|
||
" - Return Tensor(result)\n",
|
||
" - NumPy handles broadcasting automatically\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" result = self._data + other._data\n",
|
||
" return Tensor(result)\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def multiply(self, other: 'Tensor') -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Multiply two tensors element-wise.\n",
|
||
" \n",
|
||
" TODO: Implement tensor multiplication.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. Multiply the numpy arrays using *\n",
|
||
" 2. Return a new Tensor with the result\n",
|
||
" 3. Handle broadcasting automatically\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([1, 2]) * Tensor([3, 4]) → Tensor([3, 8])\n",
|
||
" \n",
|
||
" HINTS:\n",
|
||
" - Use self._data * other._data\n",
|
||
" - Return Tensor(result)\n",
|
||
" - This is element-wise, not matrix multiplication\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" result = self._data * other._data\n",
|
||
" return Tensor(result)\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def __add__(self, other: Union['Tensor', int, float]) -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Addition operator: tensor + other\n",
|
||
" \n",
|
||
" TODO: Implement + operator for tensors.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. If other is a Tensor, use tensor addition\n",
|
||
" 2. If other is a scalar, convert to Tensor first\n",
|
||
" 3. Return the result\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([1, 2]) + Tensor([3, 4]) → Tensor([4, 6])\n",
|
||
" Tensor([1, 2]) + 5 → Tensor([6, 7])\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" if isinstance(other, Tensor):\n",
|
||
" return self.add(other)\n",
|
||
" else:\n",
|
||
" return self.add(Tensor(other))\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def __mul__(self, other: Union['Tensor', int, float]) -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Multiplication operator: tensor * other\n",
|
||
" \n",
|
||
" TODO: Implement * operator for tensors.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. If other is a Tensor, use tensor multiplication\n",
|
||
" 2. If other is a scalar, convert to Tensor first\n",
|
||
" 3. Return the result\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([1, 2]) * Tensor([3, 4]) → Tensor([3, 8])\n",
|
||
" Tensor([1, 2]) * 3 → Tensor([3, 6])\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" if isinstance(other, Tensor):\n",
|
||
" return self.multiply(other)\n",
|
||
" else:\n",
|
||
" return self.multiply(Tensor(other))\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def __sub__(self, other: Union['Tensor', int, float]) -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Subtraction operator: tensor - other\n",
|
||
" \n",
|
||
" TODO: Implement - operator for tensors.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. Convert other to Tensor if needed\n",
|
||
" 2. Subtract using numpy arrays\n",
|
||
" 3. Return new Tensor with result\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([5, 6]) - Tensor([1, 2]) → Tensor([4, 4])\n",
|
||
" Tensor([5, 6]) - 1 → Tensor([4, 5])\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" if isinstance(other, Tensor):\n",
|
||
" result = self._data - other._data\n",
|
||
" else:\n",
|
||
" result = self._data - other\n",
|
||
" return Tensor(result)\n",
|
||
" ### END SOLUTION\n",
|
||
"\n",
|
||
" def __truediv__(self, other: Union['Tensor', int, float]) -> 'Tensor':\n",
|
||
" \"\"\"\n",
|
||
" Division operator: tensor / other\n",
|
||
" \n",
|
||
" TODO: Implement / operator for tensors.\n",
|
||
" \n",
|
||
" APPROACH:\n",
|
||
" 1. Convert other to Tensor if needed\n",
|
||
" 2. Divide using numpy arrays\n",
|
||
" 3. Return new Tensor with result\n",
|
||
" \n",
|
||
" EXAMPLE:\n",
|
||
" Tensor([6, 8]) / Tensor([2, 4]) → Tensor([3, 2])\n",
|
||
" Tensor([6, 8]) / 2 → Tensor([3, 4])\n",
|
||
" \"\"\"\n",
|
||
" ### BEGIN SOLUTION\n",
|
||
" if isinstance(other, Tensor):\n",
|
||
" result = self._data / other._data\n",
|
||
" else:\n",
|
||
" result = self._data / other\n",
|
||
" return Tensor(result)\n",
|
||
" ### END SOLUTION"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "087dce88",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"### 🧪 Unit Test: Tensor Creation\n",
|
||
"\n",
|
||
"Let's test your tensor creation implementation right away! This gives you immediate feedback on whether your `__init__` method works correctly.\n",
|
||
"\n",
|
||
"**This is a unit test** - it tests one specific function (tensor creation) in isolation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "6530d563",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-creation-immediate",
|
||
"locked": true,
|
||
"points": 5,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor creation immediately after implementation\n",
|
||
"print(\"🔬 Unit Test: Tensor Creation...\")\n",
|
||
"\n",
|
||
"# Test basic tensor creation\n",
|
||
"try:\n",
|
||
" # Test scalar\n",
|
||
" scalar = Tensor(5.0)\n",
|
||
" assert hasattr(scalar, '_data'), \"Tensor should have _data attribute\"\n",
|
||
" assert scalar._data.shape == (), f\"Scalar should have shape (), got {scalar._data.shape}\"\n",
|
||
" print(\"✅ Scalar creation works\")\n",
|
||
" \n",
|
||
" # Test vector\n",
|
||
" vector = Tensor([1, 2, 3])\n",
|
||
" assert vector._data.shape == (3,), f\"Vector should have shape (3,), got {vector._data.shape}\"\n",
|
||
" print(\"✅ Vector creation works\")\n",
|
||
" \n",
|
||
" # Test matrix\n",
|
||
" matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
" assert matrix._data.shape == (2, 2), f\"Matrix should have shape (2, 2), got {matrix._data.shape}\"\n",
|
||
" print(\"✅ Matrix creation works\")\n",
|
||
" \n",
|
||
" print(\"📈 Progress: Tensor Creation ✓\")\n",
|
||
" \n",
|
||
"except Exception as e:\n",
|
||
" print(f\"❌ Tensor creation test failed: {e}\")\n",
|
||
" raise\n",
|
||
"\n",
|
||
"print(\"🎯 Tensor creation behavior:\")\n",
|
||
"print(\" Converts data to NumPy arrays\")\n",
|
||
"print(\" Preserves shape and data type\")\n",
|
||
"print(\" Stores in _data attribute\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9f5392ac",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"### 🧪 Unit Test: Tensor Properties\n",
|
||
"\n",
|
||
"Now let's test that your tensor properties work correctly. This tests the @property methods you implemented.\n",
|
||
"\n",
|
||
"**This is a unit test** - it tests specific properties (shape, size, dtype, data) in isolation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "7a21015c",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-properties-immediate",
|
||
"locked": true,
|
||
"points": 5,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor properties immediately after implementation\n",
|
||
"print(\"🔬 Unit Test: Tensor Properties...\")\n",
|
||
"\n",
|
||
"# Test properties with simple examples\n",
|
||
"try:\n",
|
||
" # Test with a simple matrix\n",
|
||
" tensor = Tensor([[1, 2, 3], [4, 5, 6]])\n",
|
||
" \n",
|
||
" # Test shape property\n",
|
||
" assert tensor.shape == (2, 3), f\"Shape should be (2, 3), got {tensor.shape}\"\n",
|
||
" print(\"✅ Shape property works\")\n",
|
||
" \n",
|
||
" # Test size property\n",
|
||
" assert tensor.size == 6, f\"Size should be 6, got {tensor.size}\"\n",
|
||
" print(\"✅ Size property works\")\n",
|
||
" \n",
|
||
" # Test data property\n",
|
||
" assert np.array_equal(tensor.data, np.array([[1, 2, 3], [4, 5, 6]])), \"Data property should return numpy array\"\n",
|
||
" print(\"✅ Data property works\")\n",
|
||
" \n",
|
||
" # Test dtype property\n",
|
||
" assert tensor.dtype in [np.int32, np.int64], f\"Dtype should be int32 or int64, got {tensor.dtype}\"\n",
|
||
" print(\"✅ Dtype property works\")\n",
|
||
" \n",
|
||
" print(\"📈 Progress: Tensor Properties ✓\")\n",
|
||
" \n",
|
||
"except Exception as e:\n",
|
||
" print(f\"❌ Tensor properties test failed: {e}\")\n",
|
||
" raise\n",
|
||
"\n",
|
||
"print(\"🎯 Tensor properties behavior:\")\n",
|
||
"print(\" shape: Returns tuple of dimensions\")\n",
|
||
"print(\" size: Returns total number of elements\")\n",
|
||
"print(\" data: Returns underlying NumPy array\")\n",
|
||
"print(\" dtype: Returns NumPy data type\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "38be4d01",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"### 🧪 Unit Test: Tensor Arithmetic\n",
|
||
"\n",
|
||
"Let's test your tensor arithmetic operations. This tests the __add__, __mul__, __sub__, __truediv__ methods.\n",
|
||
"\n",
|
||
"**This is a unit test** - it tests specific arithmetic operations in isolation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "6049f928",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-arithmetic-immediate",
|
||
"locked": true,
|
||
"points": 5,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor arithmetic immediately after implementation\n",
|
||
"print(\"🔬 Unit Test: Tensor Arithmetic...\")\n",
|
||
"\n",
|
||
"# Test basic arithmetic with simple examples\n",
|
||
"try:\n",
|
||
" # Test addition\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" result = a + b\n",
|
||
" expected = np.array([5, 7, 9])\n",
|
||
" assert np.array_equal(result.data, expected), f\"Addition failed: expected {expected}, got {result.data}\"\n",
|
||
" print(\"✅ Addition works\")\n",
|
||
" \n",
|
||
" # Test scalar addition\n",
|
||
" result_scalar = a + 10\n",
|
||
" expected_scalar = np.array([11, 12, 13])\n",
|
||
" assert np.array_equal(result_scalar.data, expected_scalar), f\"Scalar addition failed: expected {expected_scalar}, got {result_scalar.data}\"\n",
|
||
" print(\"✅ Scalar addition works\")\n",
|
||
" \n",
|
||
" # Test multiplication\n",
|
||
" result_mul = a * b\n",
|
||
" expected_mul = np.array([4, 10, 18])\n",
|
||
" assert np.array_equal(result_mul.data, expected_mul), f\"Multiplication failed: expected {expected_mul}, got {result_mul.data}\"\n",
|
||
" print(\"✅ Multiplication works\")\n",
|
||
" \n",
|
||
" # Test scalar multiplication\n",
|
||
" result_scalar_mul = a * 2\n",
|
||
" expected_scalar_mul = np.array([2, 4, 6])\n",
|
||
" assert np.array_equal(result_scalar_mul.data, expected_scalar_mul), f\"Scalar multiplication failed: expected {expected_scalar_mul}, got {result_scalar_mul.data}\"\n",
|
||
" print(\"✅ Scalar multiplication works\")\n",
|
||
" \n",
|
||
" print(\"📈 Progress: Tensor Arithmetic ✓\")\n",
|
||
" \n",
|
||
"except Exception as e:\n",
|
||
" print(f\"❌ Tensor arithmetic test failed: {e}\")\n",
|
||
" raise\n",
|
||
"\n",
|
||
"print(\"🎯 Tensor arithmetic behavior:\")\n",
|
||
"print(\" Element-wise operations on tensors\")\n",
|
||
"print(\" Broadcasting with scalars\")\n",
|
||
"print(\" Returns new Tensor objects\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1c166248",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\"",
|
||
"lines_to_next_cell": 1
|
||
},
|
||
"source": [
|
||
"### 🧪 Comprehensive Test: Tensor Creation\n",
|
||
"\n",
|
||
"Let's thoroughly test your tensor creation to make sure it handles all the cases you'll encounter in ML.\n",
|
||
"This tests the foundation of everything else we'll build."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "71cac50f",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-creation-comprehensive",
|
||
"locked": true,
|
||
"points": 15,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def test_tensor_creation_comprehensive():\n",
|
||
" \"\"\"Comprehensive test of tensor creation with all data types and shapes.\"\"\"\n",
|
||
" print(\"🔬 Testing comprehensive tensor creation...\")\n",
|
||
" \n",
|
||
" tests_passed = 0\n",
|
||
" total_tests = 8\n",
|
||
" \n",
|
||
" # Test 1: Scalar creation (0D tensor)\n",
|
||
" try:\n",
|
||
" scalar_int = Tensor(42)\n",
|
||
" scalar_float = Tensor(3.14)\n",
|
||
" scalar_zero = Tensor(0)\n",
|
||
" \n",
|
||
" assert hasattr(scalar_int, '_data'), \"Tensor should have _data attribute\"\n",
|
||
" assert scalar_int._data.shape == (), f\"Scalar should have shape (), got {scalar_int._data.shape}\"\n",
|
||
" assert scalar_float._data.shape == (), f\"Float scalar should have shape (), got {scalar_float._data.shape}\"\n",
|
||
" assert scalar_zero._data.shape == (), f\"Zero scalar should have shape (), got {scalar_zero._data.shape}\"\n",
|
||
" \n",
|
||
" print(\"✅ Scalar creation: integers, floats, and zero\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Scalar creation failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 2: Vector creation (1D tensor)\n",
|
||
" try:\n",
|
||
" vector_int = Tensor([1, 2, 3, 4, 5])\n",
|
||
" vector_float = Tensor([1.0, 2.5, 3.7])\n",
|
||
" vector_single = Tensor([42])\n",
|
||
" vector_empty = Tensor([])\n",
|
||
" \n",
|
||
" assert vector_int._data.shape == (5,), f\"Int vector should have shape (5,), got {vector_int._data.shape}\"\n",
|
||
" assert vector_float._data.shape == (3,), f\"Float vector should have shape (3,), got {vector_float._data.shape}\"\n",
|
||
" assert vector_single._data.shape == (1,), f\"Single element vector should have shape (1,), got {vector_single._data.shape}\"\n",
|
||
" assert vector_empty._data.shape == (0,), f\"Empty vector should have shape (0,), got {vector_empty._data.shape}\"\n",
|
||
" \n",
|
||
" print(\"✅ Vector creation: integers, floats, single element, and empty\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Vector creation failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 3: Matrix creation (2D tensor)\n",
|
||
" try:\n",
|
||
" matrix_2x2 = Tensor([[1, 2], [3, 4]])\n",
|
||
" matrix_3x2 = Tensor([[1, 2], [3, 4], [5, 6]])\n",
|
||
" matrix_1x3 = Tensor([[1, 2, 3]])\n",
|
||
" \n",
|
||
" assert matrix_2x2._data.shape == (2, 2), f\"2x2 matrix should have shape (2, 2), got {matrix_2x2._data.shape}\"\n",
|
||
" assert matrix_3x2._data.shape == (3, 2), f\"3x2 matrix should have shape (3, 2), got {matrix_3x2._data.shape}\"\n",
|
||
" assert matrix_1x3._data.shape == (1, 3), f\"1x3 matrix should have shape (1, 3), got {matrix_1x3._data.shape}\"\n",
|
||
" \n",
|
||
" print(\"✅ Matrix creation: 2x2, 3x2, and 1x3 matrices\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Matrix creation failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 4: Data type handling\n",
|
||
" try:\n",
|
||
" int_tensor = Tensor([1, 2, 3])\n",
|
||
" float_tensor = Tensor([1.0, 2.0, 3.0])\n",
|
||
" mixed_tensor = Tensor([1, 2.5, 3]) # Should convert to float\n",
|
||
" \n",
|
||
" # Check that data types are reasonable\n",
|
||
" assert int_tensor._data.dtype in [np.int32, np.int64], f\"Int tensor has unexpected dtype: {int_tensor._data.dtype}\"\n",
|
||
" assert float_tensor._data.dtype in [np.float32, np.float64], f\"Float tensor has unexpected dtype: {float_tensor._data.dtype}\"\n",
|
||
" assert mixed_tensor._data.dtype in [np.float32, np.float64], f\"Mixed tensor should be float, got: {mixed_tensor._data.dtype}\"\n",
|
||
" \n",
|
||
" print(\"✅ Data type handling: integers, floats, and mixed types\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Data type handling failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 5: NumPy array input\n",
|
||
" try:\n",
|
||
" np_array = np.array([1, 2, 3, 4])\n",
|
||
" tensor_from_np = Tensor(np_array)\n",
|
||
" \n",
|
||
" assert tensor_from_np._data.shape == (4,), f\"Tensor from NumPy should have shape (4,), got {tensor_from_np._data.shape}\"\n",
|
||
" assert np.array_equal(tensor_from_np._data, np_array), \"Tensor from NumPy should preserve data\"\n",
|
||
" \n",
|
||
" print(\"✅ NumPy array input: conversion works correctly\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ NumPy array input failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 6: Large tensor creation\n",
|
||
" try:\n",
|
||
" large_tensor = Tensor(list(range(1000)))\n",
|
||
" assert large_tensor._data.shape == (1000,), f\"Large tensor should have shape (1000,), got {large_tensor._data.shape}\"\n",
|
||
" assert large_tensor._data[0] == 0, \"Large tensor should start with 0\"\n",
|
||
" assert large_tensor._data[-1] == 999, \"Large tensor should end with 999\"\n",
|
||
" \n",
|
||
" print(\"✅ Large tensor creation: 1000 elements\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Large tensor creation failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 7: Negative numbers\n",
|
||
" try:\n",
|
||
" negative_tensor = Tensor([-1, -2, -3])\n",
|
||
" mixed_signs = Tensor([-1, 0, 1])\n",
|
||
" \n",
|
||
" assert negative_tensor._data.shape == (3,), f\"Negative tensor should have shape (3,), got {negative_tensor._data.shape}\"\n",
|
||
" assert np.array_equal(negative_tensor._data, np.array([-1, -2, -3])), \"Negative numbers should be preserved\"\n",
|
||
" assert np.array_equal(mixed_signs._data, np.array([-1, 0, 1])), \"Mixed signs should be preserved\"\n",
|
||
" \n",
|
||
" print(\"✅ Negative numbers: handled correctly\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Negative numbers failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 8: Edge cases\n",
|
||
" try:\n",
|
||
" # Very large numbers\n",
|
||
" big_tensor = Tensor([1e6, 1e-6])\n",
|
||
" assert big_tensor._data.shape == (2,), \"Big numbers tensor should have correct shape\"\n",
|
||
" \n",
|
||
" # Zero tensor\n",
|
||
" zero_tensor = Tensor([0, 0, 0])\n",
|
||
" assert np.all(zero_tensor._data == 0), \"Zero tensor should contain all zeros\"\n",
|
||
" \n",
|
||
" print(\"✅ Edge cases: large numbers and zeros\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Edge cases failed: {e}\")\n",
|
||
" \n",
|
||
" # Results summary\n",
|
||
" print(f\"\\n📊 Tensor Creation Results: {tests_passed}/{total_tests} tests passed\")\n",
|
||
" \n",
|
||
" if tests_passed == total_tests:\n",
|
||
" print(\"🎉 All tensor creation tests passed! Your Tensor class can handle:\")\n",
|
||
" print(\" • Scalars, vectors, and matrices\")\n",
|
||
" print(\" • Different data types (int, float)\")\n",
|
||
" print(\" • NumPy arrays\")\n",
|
||
" print(\" • Large tensors and edge cases\")\n",
|
||
" print(\"📈 Progress: Tensor Creation ✓\")\n",
|
||
" return True\n",
|
||
" else:\n",
|
||
" print(\"⚠️ Some tensor creation tests failed. Common issues:\")\n",
|
||
" print(\" • Check your __init__ method implementation\")\n",
|
||
" print(\" • Make sure you're storing data in self._data\")\n",
|
||
" print(\" • Verify NumPy array conversion works correctly\")\n",
|
||
" print(\" • Test with different input types (int, float, list, np.array)\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
"# Run the comprehensive test\n",
|
||
"success = test_tensor_creation_comprehensive()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "e9fef08c",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\"",
|
||
"lines_to_next_cell": 1
|
||
},
|
||
"source": [
|
||
"### 🧪 Comprehensive Test: Tensor Properties\n",
|
||
"\n",
|
||
"Now let's test all the properties your tensor should have. These properties are essential for ML operations."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "61017a82",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-properties-comprehensive",
|
||
"locked": true,
|
||
"points": 15,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def test_tensor_properties_comprehensive():\n",
|
||
" \"\"\"Comprehensive test of tensor properties (shape, size, dtype, data access).\"\"\"\n",
|
||
" print(\"🔬 Testing comprehensive tensor properties...\")\n",
|
||
" \n",
|
||
" tests_passed = 0\n",
|
||
" total_tests = 6\n",
|
||
" \n",
|
||
" # Test 1: Shape property\n",
|
||
" try:\n",
|
||
" scalar = Tensor(5.0)\n",
|
||
" vector = Tensor([1, 2, 3])\n",
|
||
" matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
" tensor_3d = Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])\n",
|
||
" \n",
|
||
" assert scalar.shape == (), f\"Scalar shape should be (), got {scalar.shape}\"\n",
|
||
" assert vector.shape == (3,), f\"Vector shape should be (3,), got {vector.shape}\"\n",
|
||
" assert matrix.shape == (2, 2), f\"Matrix shape should be (2, 2), got {matrix.shape}\"\n",
|
||
" assert tensor_3d.shape == (2, 2, 2), f\"3D tensor shape should be (2, 2, 2), got {tensor_3d.shape}\"\n",
|
||
" \n",
|
||
" print(\"✅ Shape property: scalar, vector, matrix, and 3D tensor\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Shape property failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 2: Size property\n",
|
||
" try:\n",
|
||
" scalar = Tensor(5.0)\n",
|
||
" vector = Tensor([1, 2, 3])\n",
|
||
" matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
" empty = Tensor([])\n",
|
||
" \n",
|
||
" assert scalar.size == 1, f\"Scalar size should be 1, got {scalar.size}\"\n",
|
||
" assert vector.size == 3, f\"Vector size should be 3, got {vector.size}\"\n",
|
||
" assert matrix.size == 4, f\"Matrix size should be 4, got {matrix.size}\"\n",
|
||
" assert empty.size == 0, f\"Empty tensor size should be 0, got {empty.size}\"\n",
|
||
" \n",
|
||
" print(\"✅ Size property: scalar, vector, matrix, and empty tensor\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Size property failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 3: Data type property\n",
|
||
" try:\n",
|
||
" int_tensor = Tensor([1, 2, 3])\n",
|
||
" float_tensor = Tensor([1.0, 2.0, 3.0])\n",
|
||
" \n",
|
||
" # Check that dtype is accessible and reasonable\n",
|
||
" assert hasattr(int_tensor, 'dtype'), \"Tensor should have dtype property\"\n",
|
||
" assert hasattr(float_tensor, 'dtype'), \"Tensor should have dtype property\"\n",
|
||
" \n",
|
||
" # Data types should be NumPy dtypes\n",
|
||
" assert isinstance(int_tensor.dtype, np.dtype), f\"dtype should be np.dtype, got {type(int_tensor.dtype)}\"\n",
|
||
" assert isinstance(float_tensor.dtype, np.dtype), f\"dtype should be np.dtype, got {type(float_tensor.dtype)}\"\n",
|
||
" \n",
|
||
" print(f\"✅ Data type property: int tensor is {int_tensor.dtype}, float tensor is {float_tensor.dtype}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Data type property failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 4: Data access property\n",
|
||
" try:\n",
|
||
" scalar = Tensor(5.0)\n",
|
||
" vector = Tensor([1, 2, 3])\n",
|
||
" matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
" \n",
|
||
" # Test data access\n",
|
||
" assert hasattr(scalar, 'data'), \"Tensor should have data property\"\n",
|
||
" assert hasattr(vector, 'data'), \"Tensor should have data property\"\n",
|
||
" assert hasattr(matrix, 'data'), \"Tensor should have data property\"\n",
|
||
" \n",
|
||
" # Test data content\n",
|
||
" assert scalar.data.item() == 5.0, f\"Scalar data should be 5.0, got {scalar.data.item()}\"\n",
|
||
" assert np.array_equal(vector.data, np.array([1, 2, 3])), \"Vector data mismatch\"\n",
|
||
" assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), \"Matrix data mismatch\"\n",
|
||
" \n",
|
||
" print(\"✅ Data access: scalar, vector, and matrix data retrieval\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Data access failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 5: String representation\n",
|
||
" try:\n",
|
||
" scalar = Tensor(5.0)\n",
|
||
" vector = Tensor([1, 2, 3])\n",
|
||
" \n",
|
||
" # Test that __repr__ works\n",
|
||
" scalar_str = str(scalar)\n",
|
||
" vector_str = str(vector)\n",
|
||
" \n",
|
||
" assert isinstance(scalar_str, str), \"Tensor string representation should be a string\"\n",
|
||
" assert isinstance(vector_str, str), \"Tensor string representation should be a string\"\n",
|
||
" assert len(scalar_str) > 0, \"Tensor string representation should not be empty\"\n",
|
||
" assert len(vector_str) > 0, \"Tensor string representation should not be empty\"\n",
|
||
" \n",
|
||
" print(f\"✅ String representation: scalar={scalar_str[:50]}{'...' if len(scalar_str) > 50 else ''}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ String representation failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 6: Property consistency\n",
|
||
" try:\n",
|
||
" test_cases = [\n",
|
||
" Tensor(42),\n",
|
||
" Tensor([1, 2, 3, 4, 5]),\n",
|
||
" Tensor([[1, 2, 3], [4, 5, 6]]),\n",
|
||
" Tensor([])\n",
|
||
" ]\n",
|
||
" \n",
|
||
" for i, tensor in enumerate(test_cases):\n",
|
||
" # Size should equal product of shape\n",
|
||
" expected_size = np.prod(tensor.shape) if tensor.shape else 1\n",
|
||
" assert tensor.size == expected_size, f\"Test case {i}: size {tensor.size} doesn't match shape {tensor.shape}\"\n",
|
||
" \n",
|
||
" # Data shape should match tensor shape\n",
|
||
" assert tensor.data.shape == tensor.shape, f\"Test case {i}: data shape {tensor.data.shape} doesn't match tensor shape {tensor.shape}\"\n",
|
||
" \n",
|
||
" print(\"✅ Property consistency: size matches shape, data shape matches tensor shape\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Property consistency failed: {e}\")\n",
|
||
" \n",
|
||
" # Results summary\n",
|
||
" print(f\"\\n📊 Tensor Properties Results: {tests_passed}/{total_tests} tests passed\")\n",
|
||
" \n",
|
||
" if tests_passed == total_tests:\n",
|
||
" print(\"🎉 All tensor property tests passed! Your tensor has:\")\n",
|
||
" print(\" • Correct shape property for all dimensions\")\n",
|
||
" print(\" • Accurate size calculation\")\n",
|
||
" print(\" • Proper data type handling\")\n",
|
||
" print(\" • Working data access\")\n",
|
||
" print(\" • Good string representation\")\n",
|
||
" print(\"📈 Progress: Tensor Creation ✓, Properties ✓\")\n",
|
||
" return True\n",
|
||
" else:\n",
|
||
" print(\"⚠️ Some property tests failed. Common issues:\")\n",
|
||
" print(\" • Check your @property decorators\")\n",
|
||
" print(\" • Verify shape returns self._data.shape\")\n",
|
||
" print(\" • Make sure size returns self._data.size\")\n",
|
||
" print(\" • Ensure dtype returns self._data.dtype\")\n",
|
||
" print(\" • Test your __repr__ method\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
"# Run the comprehensive test\n",
|
||
"success = test_tensor_properties_comprehensive() and success"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "8467b780",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\"",
|
||
"lines_to_next_cell": 1
|
||
},
|
||
"source": [
|
||
"### 🧪 Comprehensive Test: Tensor Arithmetic\n",
|
||
"\n",
|
||
"Let's test all arithmetic operations. These are the foundation of neural network computations!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "3883fcf9",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-arithmetic-comprehensive",
|
||
"locked": true,
|
||
"points": 20,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def test_tensor_arithmetic_comprehensive():\n",
|
||
" \"\"\"Comprehensive test of tensor arithmetic operations.\"\"\"\n",
|
||
" print(\"🔬 Testing comprehensive tensor arithmetic...\")\n",
|
||
" \n",
|
||
" tests_passed = 0\n",
|
||
" total_tests = 8\n",
|
||
" \n",
|
||
" # Test 1: Basic addition method\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" c = a.add(b)\n",
|
||
" \n",
|
||
" expected = np.array([5, 7, 9])\n",
|
||
" assert np.array_equal(c.data, expected), f\"Addition method failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"Addition should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ Addition method: {a.data} + {b.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Addition method failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 2: Basic multiplication method\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" c = a.multiply(b)\n",
|
||
" \n",
|
||
" expected = np.array([4, 10, 18])\n",
|
||
" assert np.array_equal(c.data, expected), f\"Multiplication method failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"Multiplication should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ Multiplication method: {a.data} * {b.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Multiplication method failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 3: Addition operator (+)\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" c = a + b\n",
|
||
" \n",
|
||
" expected = np.array([5, 7, 9])\n",
|
||
" assert np.array_equal(c.data, expected), f\"+ operator failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"+ operator should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ + operator: {a.data} + {b.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ + operator failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 4: Multiplication operator (*)\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" c = a * b\n",
|
||
" \n",
|
||
" expected = np.array([4, 10, 18])\n",
|
||
" assert np.array_equal(c.data, expected), f\"* operator failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"* operator should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ * operator: {a.data} * {b.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ * operator failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 5: Subtraction operator (-)\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" b = Tensor([4, 5, 6])\n",
|
||
" c = b - a\n",
|
||
" \n",
|
||
" expected = np.array([3, 3, 3])\n",
|
||
" assert np.array_equal(c.data, expected), f\"- operator failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"- operator should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ - operator: {b.data} - {a.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ - operator failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 6: Division operator (/)\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 4])\n",
|
||
" b = Tensor([2, 4, 8])\n",
|
||
" c = b / a\n",
|
||
" \n",
|
||
" expected = np.array([2.0, 2.0, 2.0])\n",
|
||
" assert np.allclose(c.data, expected), f\"/ operator failed: expected {expected}, got {c.data}\"\n",
|
||
" assert isinstance(c, Tensor), \"/ operator should return a Tensor\"\n",
|
||
" \n",
|
||
" print(f\"✅ / operator: {b.data} / {a.data} = {c.data}\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ / operator failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 7: Scalar operations\n",
|
||
" try:\n",
|
||
" a = Tensor([1, 2, 3])\n",
|
||
" \n",
|
||
" # Addition with scalar\n",
|
||
" b = a + 10\n",
|
||
" expected_add = np.array([11, 12, 13])\n",
|
||
" assert np.array_equal(b.data, expected_add), f\"Scalar addition failed: expected {expected_add}, got {b.data}\"\n",
|
||
" \n",
|
||
" # Multiplication with scalar\n",
|
||
" c = a * 2\n",
|
||
" expected_mul = np.array([2, 4, 6])\n",
|
||
" assert np.array_equal(c.data, expected_mul), f\"Scalar multiplication failed: expected {expected_mul}, got {c.data}\"\n",
|
||
" \n",
|
||
" # Subtraction with scalar\n",
|
||
" d = a - 1\n",
|
||
" expected_sub = np.array([0, 1, 2])\n",
|
||
" assert np.array_equal(d.data, expected_sub), f\"Scalar subtraction failed: expected {expected_sub}, got {d.data}\"\n",
|
||
" \n",
|
||
" # Division with scalar\n",
|
||
" e = a / 2\n",
|
||
" expected_div = np.array([0.5, 1.0, 1.5])\n",
|
||
" assert np.allclose(e.data, expected_div), f\"Scalar division failed: expected {expected_div}, got {e.data}\"\n",
|
||
" \n",
|
||
" print(f\"✅ Scalar operations: +10, *2, -1, /2 all work correctly\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Scalar operations failed: {e}\")\n",
|
||
" \n",
|
||
" # Test 8: Matrix operations\n",
|
||
" try:\n",
|
||
" matrix_a = Tensor([[1, 2], [3, 4]])\n",
|
||
" matrix_b = Tensor([[5, 6], [7, 8]])\n",
|
||
" \n",
|
||
" # Matrix addition\n",
|
||
" c = matrix_a + matrix_b\n",
|
||
" expected = np.array([[6, 8], [10, 12]])\n",
|
||
" assert np.array_equal(c.data, expected), f\"Matrix addition failed: expected {expected}, got {c.data}\"\n",
|
||
" assert c.shape == (2, 2), f\"Matrix addition should preserve shape, got {c.shape}\"\n",
|
||
" \n",
|
||
" # Matrix multiplication (element-wise)\n",
|
||
" d = matrix_a * matrix_b\n",
|
||
" expected_mul = np.array([[5, 12], [21, 32]])\n",
|
||
" assert np.array_equal(d.data, expected_mul), f\"Matrix multiplication failed: expected {expected_mul}, got {d.data}\"\n",
|
||
" \n",
|
||
" print(f\"✅ Matrix operations: 2x2 matrix addition and multiplication\")\n",
|
||
" tests_passed += 1\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Matrix operations failed: {e}\")\n",
|
||
" \n",
|
||
" # Results summary\n",
|
||
" print(f\"\\n📊 Tensor Arithmetic Results: {tests_passed}/{total_tests} tests passed\")\n",
|
||
" \n",
|
||
" if tests_passed == total_tests:\n",
|
||
" print(\"🎉 All tensor arithmetic tests passed! Your tensor supports:\")\n",
|
||
" print(\" • Basic methods: add(), multiply()\")\n",
|
||
" print(\" • Python operators: +, -, *, /\")\n",
|
||
" print(\" • Scalar operations: tensor + number\")\n",
|
||
" print(\" • Matrix operations: element-wise operations\")\n",
|
||
" print(\"📈 Progress: Tensor Creation ✓, Properties ✓, Arithmetic ✓\")\n",
|
||
" return True\n",
|
||
" else:\n",
|
||
" print(\"⚠️ Some arithmetic tests failed. Common issues:\")\n",
|
||
" print(\" • Check your add() and multiply() methods\")\n",
|
||
" print(\" • Verify operator overloading (__add__, __mul__, __sub__, __truediv__)\")\n",
|
||
" print(\" • Make sure scalar operations work (convert scalar to Tensor)\")\n",
|
||
" print(\" • Test with different tensor shapes\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
"# Run the comprehensive test\n",
|
||
"success = test_tensor_arithmetic_comprehensive() and success"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "fe61e372",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\"",
|
||
"lines_to_next_cell": 1
|
||
},
|
||
"source": [
|
||
"### 🧪 Final Integration Test: Real ML Scenario\n",
|
||
"\n",
|
||
"Let's test your tensor with a realistic machine learning scenario to make sure everything works together."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b5650653",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-integration",
|
||
"locked": true,
|
||
"points": 10,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"def test_tensor_integration():\n",
|
||
" \"\"\"Integration test with realistic ML scenario.\"\"\"\n",
|
||
" print(\"🔬 Testing tensor integration with ML scenario...\")\n",
|
||
" \n",
|
||
" try:\n",
|
||
" print(\"🧠 Simulating a simple neural network forward pass...\")\n",
|
||
" \n",
|
||
" # Simulate input data (batch of 2 samples, 3 features each)\n",
|
||
" X = Tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])\n",
|
||
" print(f\"📊 Input data shape: {X.shape}\")\n",
|
||
" \n",
|
||
" # Simulate weights (3 input features, 2 output neurons)\n",
|
||
" W = Tensor([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]])\n",
|
||
" print(f\"🎯 Weights shape: {W.shape}\")\n",
|
||
" \n",
|
||
" # Simulate bias (2 output neurons)\n",
|
||
" b = Tensor([0.1, 0.2])\n",
|
||
" print(f\"⚖️ Bias shape: {b.shape}\")\n",
|
||
" \n",
|
||
" # Simple linear transformation: y = X * W + b\n",
|
||
" # Note: This is a simplified version - real matrix multiplication would be different\n",
|
||
" # But we can test element-wise operations\n",
|
||
" \n",
|
||
" # Test that we can do basic operations needed for ML\n",
|
||
" sample = Tensor([1.0, 2.0, 3.0]) # Single sample\n",
|
||
" weight_col = Tensor([0.1, 0.3, 0.5]) # First column of weights\n",
|
||
" \n",
|
||
" # Compute dot product manually using element-wise operations\n",
|
||
" products = sample * weight_col # Element-wise multiplication\n",
|
||
" print(f\"✅ Element-wise multiplication works: {products.data}\")\n",
|
||
" \n",
|
||
" # Test addition for bias\n",
|
||
" result = products + Tensor([0.1, 0.1, 0.1])\n",
|
||
" print(f\"✅ Bias addition works: {result.data}\")\n",
|
||
" \n",
|
||
" # Test with different shapes\n",
|
||
" matrix_a = Tensor([[1, 2], [3, 4]])\n",
|
||
" matrix_b = Tensor([[0.1, 0.2], [0.3, 0.4]])\n",
|
||
" matrix_result = matrix_a * matrix_b\n",
|
||
" print(f\"✅ Matrix operations work: {matrix_result.data}\")\n",
|
||
" \n",
|
||
" # Test scalar operations (common in ML)\n",
|
||
" scaled = sample * 0.5 # Learning rate scaling\n",
|
||
" print(f\"✅ Scalar scaling works: {scaled.data}\")\n",
|
||
" \n",
|
||
" # Test normalization-like operations\n",
|
||
" mean_val = Tensor([2.0, 2.0, 2.0]) # Simulate mean\n",
|
||
" normalized = sample - mean_val\n",
|
||
" print(f\"✅ Mean subtraction works: {normalized.data}\")\n",
|
||
" \n",
|
||
" print(\"\\n🎉 Integration test passed! Your tensor class can handle:\")\n",
|
||
" print(\" • Multi-dimensional data (batches, features)\")\n",
|
||
" print(\" • Element-wise operations needed for ML\")\n",
|
||
" print(\" • Scalar operations (learning rates, normalization)\")\n",
|
||
" print(\" • Matrix operations (weights, transformations)\")\n",
|
||
" print(\"📈 Progress: All tensor functionality ✓\")\n",
|
||
" print(\"🚀 Ready for neural network layers!\")\n",
|
||
" \n",
|
||
" return True\n",
|
||
" \n",
|
||
" except Exception as e:\n",
|
||
" print(f\"❌ Integration test failed: {e}\")\n",
|
||
" print(\"\\n💡 This suggests an issue with:\")\n",
|
||
" print(\" • Basic tensor operations not working together\")\n",
|
||
" print(\" • Shape handling problems\")\n",
|
||
" print(\" • Arithmetic operation implementation\")\n",
|
||
" print(\" • Check your tensor creation and arithmetic methods\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
"# Run the integration test\n",
|
||
"success = test_tensor_integration() and success\n",
|
||
"\n",
|
||
"# Print final summary\n",
|
||
"print(f\"\\n{'='*60}\")\n",
|
||
"print(\"🎯 TENSOR MODULE TESTING COMPLETE\")\n",
|
||
"print(f\"{'='*60}\")\n",
|
||
"\n",
|
||
"if success:\n",
|
||
" print(\"🎉 CONGRATULATIONS! All tensor tests passed!\")\n",
|
||
" print(\"\\n✅ Your Tensor class successfully implements:\")\n",
|
||
" print(\" • Comprehensive tensor creation (scalars, vectors, matrices)\")\n",
|
||
" print(\" • All essential properties (shape, size, dtype, data access)\")\n",
|
||
" print(\" • Complete arithmetic operations (methods and operators)\")\n",
|
||
" print(\" • Scalar and matrix operations\")\n",
|
||
" print(\" • Real ML scenario compatibility\")\n",
|
||
" print(\"\\n🚀 You're ready to move to the next module!\")\n",
|
||
" print(\"📈 Final Progress: Tensor Module ✓ COMPLETE\")\n",
|
||
"else:\n",
|
||
" print(\"⚠️ Some tests failed. Please review the error messages above.\")\n",
|
||
" print(\"\\n🔧 To fix issues:\")\n",
|
||
" print(\" 1. Check the specific test that failed\")\n",
|
||
" print(\" 2. Review the error message and hints\")\n",
|
||
" print(\" 3. Fix your implementation\")\n",
|
||
" print(\" 4. Re-run the notebook cells\")\n",
|
||
" print(\"\\n💪 Don't give up! Debugging is part of learning.\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9287bb44",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## Step 3: Tensor Arithmetic Operations\n",
|
||
"\n",
|
||
"### Why Arithmetic Matters\n",
|
||
"Tensor arithmetic is the foundation of all neural network operations:\n",
|
||
"- **Forward pass**: Matrix multiplications and additions\n",
|
||
"- **Activation functions**: Element-wise operations\n",
|
||
"- **Loss computation**: Differences and squares\n",
|
||
"- **Gradient computation**: Chain rule applications\n",
|
||
"\n",
|
||
"### Operations We'll Implement\n",
|
||
"- **Addition**: Element-wise addition of tensors\n",
|
||
"- **Multiplication**: Element-wise multiplication\n",
|
||
"- **Python operators**: `+`, `-`, `*`, `/` for natural syntax\n",
|
||
"- **Broadcasting**: Handle different shapes automatically"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a5c68c19",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## Step 3: Tensor Arithmetic Methods\n",
|
||
"\n",
|
||
"The arithmetic methods are now part of the Tensor class above. Let's test them!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b8d0e58f",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## Step 4: Python Operator Overloading\n",
|
||
"\n",
|
||
"### Why Operator Overloading?\n",
|
||
"Python's magic methods allow us to use natural syntax:\n",
|
||
"- `a + b` instead of `a.add(b)`\n",
|
||
"- `a * b` instead of `a.multiply(b)`\n",
|
||
"- `a - b` for subtraction\n",
|
||
"- `a / b` for division\n",
|
||
"\n",
|
||
"This makes tensor operations feel natural and readable."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "2fb25e3c",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## Step 4: Operator Overloading\n",
|
||
"\n",
|
||
"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`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1ce7233e",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"### 🧪 Test Your Tensor Implementation\n",
|
||
"\n",
|
||
"Once you implement the Tensor class above, run these cells to test your implementation:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "e45b9b7d",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-creation",
|
||
"locked": true,
|
||
"points": 25,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor creation and properties\n",
|
||
"print(\"Testing tensor creation...\")\n",
|
||
"\n",
|
||
"# Test scalar creation\n",
|
||
"scalar = Tensor(5.0)\n",
|
||
"assert scalar.shape == (), f\"Scalar shape should be (), got {scalar.shape}\"\n",
|
||
"assert scalar.size == 1, f\"Scalar size should be 1, got {scalar.size}\"\n",
|
||
"assert scalar.data.item() == 5.0, f\"Scalar value should be 5.0, got {scalar.data.item()}\"\n",
|
||
"\n",
|
||
"# Test vector creation\n",
|
||
"vector = Tensor([1, 2, 3])\n",
|
||
"assert vector.shape == (3,), f\"Vector shape should be (3,), got {vector.shape}\"\n",
|
||
"assert vector.size == 3, f\"Vector size should be 3, got {vector.size}\"\n",
|
||
"assert np.array_equal(vector.data, np.array([1, 2, 3])), \"Vector data mismatch\"\n",
|
||
"\n",
|
||
"# Test matrix creation\n",
|
||
"matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
"assert matrix.shape == (2, 2), f\"Matrix shape should be (2, 2), got {matrix.shape}\"\n",
|
||
"assert matrix.size == 4, f\"Matrix size should be 4, got {matrix.size}\"\n",
|
||
"assert np.array_equal(matrix.data, np.array([[1, 2], [3, 4]])), \"Matrix data mismatch\"\n",
|
||
"\n",
|
||
"# Test dtype handling\n",
|
||
"float_tensor = Tensor([1.0, 2.0, 3.0])\n",
|
||
"assert float_tensor.dtype == np.float32, f\"Float tensor dtype should be float32, got {float_tensor.dtype}\"\n",
|
||
"\n",
|
||
"int_tensor = Tensor([1, 2, 3])\n",
|
||
"# Note: NumPy may default to int64 on some systems, so we check for integer types\n",
|
||
"assert int_tensor.dtype in [np.int32, np.int64], f\"Int tensor dtype should be int32 or int64, got {int_tensor.dtype}\"\n",
|
||
"\n",
|
||
"print(\"✅ Tensor creation tests passed!\")\n",
|
||
"print(f\"✅ Scalar: {scalar}\")\n",
|
||
"print(f\"✅ Vector: {vector}\")\n",
|
||
"print(f\"✅ Matrix: {matrix}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "01b4a2ba",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-arithmetic",
|
||
"locked": true,
|
||
"points": 25,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor arithmetic operations\n",
|
||
"print(\"Testing tensor arithmetic...\")\n",
|
||
"\n",
|
||
"# Test addition\n",
|
||
"a = Tensor([1, 2, 3])\n",
|
||
"b = Tensor([4, 5, 6])\n",
|
||
"c = a + b\n",
|
||
"expected = np.array([5, 7, 9])\n",
|
||
"assert np.array_equal(c.data, expected), f\"Addition failed: expected {expected}, got {c.data}\"\n",
|
||
"\n",
|
||
"# Test multiplication\n",
|
||
"d = a * b\n",
|
||
"expected = np.array([4, 10, 18])\n",
|
||
"assert np.array_equal(d.data, expected), f\"Multiplication failed: expected {expected}, got {d.data}\"\n",
|
||
"\n",
|
||
"# Test subtraction\n",
|
||
"e = b - a\n",
|
||
"expected = np.array([3, 3, 3])\n",
|
||
"assert np.array_equal(e.data, expected), f\"Subtraction failed: expected {expected}, got {e.data}\"\n",
|
||
"\n",
|
||
"# Test division\n",
|
||
"f = b / a\n",
|
||
"expected = np.array([4.0, 2.5, 2.0])\n",
|
||
"assert np.allclose(f.data, expected), f\"Division failed: expected {expected}, got {f.data}\"\n",
|
||
"\n",
|
||
"# Test scalar operations\n",
|
||
"g = a + 10\n",
|
||
"expected = np.array([11, 12, 13])\n",
|
||
"assert np.array_equal(g.data, expected), f\"Scalar addition failed: expected {expected}, got {g.data}\"\n",
|
||
"\n",
|
||
"h = a * 2\n",
|
||
"expected = np.array([2, 4, 6])\n",
|
||
"assert np.array_equal(h.data, expected), f\"Scalar multiplication failed: expected {expected}, got {h.data}\"\n",
|
||
"\n",
|
||
"print(\"✅ Tensor arithmetic tests passed!\")\n",
|
||
"print(f\"✅ Addition: {a} + {b} = {c}\")\n",
|
||
"print(f\"✅ Multiplication: {a} * {b} = {d}\")\n",
|
||
"print(f\"✅ Subtraction: {b} - {a} = {e}\")\n",
|
||
"print(f\"✅ Division: {b} / {a} = {f}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "d268a516",
|
||
"metadata": {
|
||
"nbgrader": {
|
||
"grade": true,
|
||
"grade_id": "test-tensor-broadcasting",
|
||
"locked": true,
|
||
"points": 25,
|
||
"schema_version": 3,
|
||
"solution": false,
|
||
"task": false
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Test tensor broadcasting\n",
|
||
"print(\"Testing tensor broadcasting...\")\n",
|
||
"\n",
|
||
"# Test scalar broadcasting\n",
|
||
"matrix = Tensor([[1, 2], [3, 4]])\n",
|
||
"scalar = Tensor(10)\n",
|
||
"result = matrix + scalar\n",
|
||
"expected = np.array([[11, 12], [13, 14]])\n",
|
||
"assert np.array_equal(result.data, expected), f\"Scalar broadcasting failed: expected {expected}, got {result.data}\"\n",
|
||
"\n",
|
||
"# Test vector broadcasting\n",
|
||
"vector = Tensor([1, 2])\n",
|
||
"result = matrix + vector\n",
|
||
"expected = np.array([[2, 4], [4, 6]])\n",
|
||
"assert np.array_equal(result.data, expected), f\"Vector broadcasting failed: expected {expected}, got {result.data}\"\n",
|
||
"\n",
|
||
"# Test different shapes\n",
|
||
"a = Tensor([[1], [2], [3]]) # (3, 1)\n",
|
||
"b = Tensor([10, 20]) # (2,)\n",
|
||
"result = a + b\n",
|
||
"expected = np.array([[11, 21], [12, 22], [13, 23]])\n",
|
||
"assert np.array_equal(result.data, expected), f\"Shape broadcasting failed: expected {expected}, got {result.data}\"\n",
|
||
"\n",
|
||
"print(\"✅ Tensor broadcasting tests passed!\")\n",
|
||
"print(f\"✅ Matrix + Scalar: {matrix} + {scalar} = {result}\")\n",
|
||
"print(f\"✅ Broadcasting works correctly!\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "57b99fdc",
|
||
"metadata": {
|
||
"cell_marker": "\"\"\""
|
||
},
|
||
"source": [
|
||
"## 🎯 Module Summary\n",
|
||
"\n",
|
||
"Congratulations! You've successfully implemented the core Tensor class for TinyTorch:\n",
|
||
"\n",
|
||
"### What You've Accomplished\n",
|
||
"✅ **Tensor Creation**: Handle scalars, vectors, matrices, and higher-dimensional arrays \n",
|
||
"✅ **Data Types**: Proper dtype handling with auto-detection and conversion \n",
|
||
"✅ **Properties**: Shape, size, dtype, and data access \n",
|
||
"✅ **Arithmetic**: Addition, multiplication, subtraction, division \n",
|
||
"✅ **Operators**: Natural Python syntax with `+`, `-`, `*`, `/` \n",
|
||
"✅ **Broadcasting**: Automatic shape compatibility like NumPy \n",
|
||
"\n",
|
||
"### Key Concepts You've Learned\n",
|
||
"- **Tensors** are the fundamental data structure for ML systems\n",
|
||
"- **NumPy backend** provides efficient computation with ML-friendly API\n",
|
||
"- **Operator overloading** makes tensor operations feel natural\n",
|
||
"- **Broadcasting** enables flexible operations between different shapes\n",
|
||
"- **Type safety** ensures consistent behavior across operations\n",
|
||
"\n",
|
||
"### Next Steps\n",
|
||
"1. **Export your code**: `tito package nbdev --export 01_tensor`\n",
|
||
"2. **Test your implementation**: `tito module test 01_tensor`\n",
|
||
"3. **Use your tensors**: \n",
|
||
" ```python\n",
|
||
" from tinytorch.core.tensor import Tensor\n",
|
||
" t = Tensor([1, 2, 3])\n",
|
||
" print(t + 5) # Your tensor in action!\n",
|
||
" ```\n",
|
||
"4. **Move to Module 2**: Start building activation functions!\n",
|
||
"\n",
|
||
"**Ready for the next challenge?** Let's add the mathematical functions that make neural networks powerful!"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"jupytext": {
|
||
"main_language": "python"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|