mirror of
https://github.com/MLSysBook/TinyTorch.git
synced 2026-04-27 17:27: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
298 lines
9.4 KiB
Python
298 lines
9.4 KiB
Python
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../modules/source/01_tensor/tensor_dev.ipynb.
|
|
|
|
# %% auto 0
|
|
__all__ = ['Tensor']
|
|
|
|
# %% ../../modules/source/01_tensor/tensor_dev.ipynb 1
|
|
import numpy as np
|
|
import sys
|
|
from typing import Union, List, Tuple, Optional, Any
|
|
|
|
# %% ../../modules/source/01_tensor/tensor_dev.ipynb 7
|
|
class Tensor:
|
|
"""
|
|
TinyTorch Tensor: N-dimensional array with ML operations.
|
|
|
|
The fundamental data structure for all TinyTorch operations.
|
|
Wraps NumPy arrays with ML-specific functionality.
|
|
"""
|
|
|
|
def __init__(self, data: Union[int, float, List, np.ndarray], dtype: Optional[str] = None):
|
|
"""
|
|
Create a new tensor from data.
|
|
|
|
Args:
|
|
data: Input data (scalar, list, or numpy array)
|
|
dtype: Data type ('float32', 'int32', etc.). Defaults to auto-detect.
|
|
|
|
TODO: Implement tensor creation with proper type handling.
|
|
|
|
STEP-BY-STEP:
|
|
1. Check if data is a scalar (int/float) - convert to numpy array
|
|
2. Check if data is a list - convert to numpy array
|
|
3. Check if data is already a numpy array - use as-is
|
|
4. Apply dtype conversion if specified
|
|
5. Store the result in self._data
|
|
|
|
EXAMPLE:
|
|
Tensor(5) → stores np.array(5)
|
|
Tensor([1, 2, 3]) → stores np.array([1, 2, 3])
|
|
Tensor(np.array([1, 2, 3])) → stores the array directly
|
|
|
|
HINTS:
|
|
- Use isinstance() to check data types
|
|
- Use np.array() for conversion
|
|
- Handle dtype parameter for type conversion
|
|
- Store the array in self._data
|
|
"""
|
|
### BEGIN SOLUTION
|
|
# Convert input to numpy array
|
|
if isinstance(data, (int, float, np.number)):
|
|
# Handle Python and NumPy scalars
|
|
if dtype is None:
|
|
# Auto-detect type: int for integers, float32 for floats
|
|
if isinstance(data, int) or (isinstance(data, np.number) and np.issubdtype(type(data), np.integer)):
|
|
dtype = 'int32'
|
|
else:
|
|
dtype = 'float32'
|
|
self._data = np.array(data, dtype=dtype)
|
|
elif isinstance(data, list):
|
|
# Let NumPy auto-detect type, then convert if needed
|
|
temp_array = np.array(data)
|
|
if dtype is None:
|
|
# Use NumPy's auto-detected type, but prefer float32 for floats
|
|
if temp_array.dtype == np.float64:
|
|
dtype = 'float32'
|
|
else:
|
|
dtype = str(temp_array.dtype)
|
|
self._data = np.array(data, dtype=dtype)
|
|
elif isinstance(data, np.ndarray):
|
|
# Already a numpy array
|
|
if dtype is None:
|
|
# Keep existing dtype, but prefer float32 for float64
|
|
if data.dtype == np.float64:
|
|
dtype = 'float32'
|
|
else:
|
|
dtype = str(data.dtype)
|
|
self._data = data.astype(dtype) if dtype != data.dtype else data.copy()
|
|
else:
|
|
# Try to convert unknown types
|
|
self._data = np.array(data, dtype=dtype)
|
|
### END SOLUTION
|
|
|
|
@property
|
|
def data(self) -> np.ndarray:
|
|
"""
|
|
Access underlying numpy array.
|
|
|
|
TODO: Return the stored numpy array.
|
|
|
|
HINT: Return self._data (the array you stored in __init__)
|
|
"""
|
|
### BEGIN SOLUTION
|
|
return self._data
|
|
### END SOLUTION
|
|
|
|
@property
|
|
def shape(self) -> Tuple[int, ...]:
|
|
"""
|
|
Get tensor shape.
|
|
|
|
TODO: Return the shape of the stored numpy array.
|
|
|
|
HINT: Use .shape attribute of the numpy array
|
|
EXAMPLE: Tensor([1, 2, 3]).shape should return (3,)
|
|
"""
|
|
### BEGIN SOLUTION
|
|
return self._data.shape
|
|
### END SOLUTION
|
|
|
|
@property
|
|
def size(self) -> int:
|
|
"""
|
|
Get total number of elements.
|
|
|
|
TODO: Return the total number of elements in the tensor.
|
|
|
|
HINT: Use .size attribute of the numpy array
|
|
EXAMPLE: Tensor([1, 2, 3]).size should return 3
|
|
"""
|
|
### BEGIN SOLUTION
|
|
return self._data.size
|
|
### END SOLUTION
|
|
|
|
@property
|
|
def dtype(self) -> np.dtype:
|
|
"""
|
|
Get data type as numpy dtype.
|
|
|
|
TODO: Return the data type of the stored numpy array.
|
|
|
|
HINT: Use .dtype attribute of the numpy array
|
|
EXAMPLE: Tensor([1, 2, 3]).dtype should return dtype('int32')
|
|
"""
|
|
### BEGIN SOLUTION
|
|
return self._data.dtype
|
|
### END SOLUTION
|
|
|
|
def __repr__(self) -> str:
|
|
"""
|
|
String representation.
|
|
|
|
TODO: Create a clear string representation of the tensor.
|
|
|
|
APPROACH:
|
|
1. Convert the numpy array to a list for readable output
|
|
2. Include the shape and dtype information
|
|
3. Format: "Tensor([data], shape=shape, dtype=dtype)"
|
|
|
|
EXAMPLE:
|
|
Tensor([1, 2, 3]) → "Tensor([1, 2, 3], shape=(3,), dtype=int32)"
|
|
|
|
HINTS:
|
|
- Use .tolist() to convert numpy array to list
|
|
- Include shape and dtype information
|
|
- Keep format consistent and readable
|
|
"""
|
|
### BEGIN SOLUTION
|
|
return f"Tensor({self._data.tolist()}, shape={self.shape}, dtype={self.dtype})"
|
|
### END SOLUTION
|
|
|
|
def add(self, other: 'Tensor') -> 'Tensor':
|
|
"""
|
|
Add two tensors element-wise.
|
|
|
|
TODO: Implement tensor addition.
|
|
|
|
APPROACH:
|
|
1. Add the numpy arrays using +
|
|
2. Return a new Tensor with the result
|
|
3. Handle broadcasting automatically
|
|
|
|
EXAMPLE:
|
|
Tensor([1, 2]) + Tensor([3, 4]) → Tensor([4, 6])
|
|
|
|
HINTS:
|
|
- Use self._data + other._data
|
|
- Return Tensor(result)
|
|
- NumPy handles broadcasting automatically
|
|
"""
|
|
### BEGIN SOLUTION
|
|
result = self._data + other._data
|
|
return Tensor(result)
|
|
### END SOLUTION
|
|
|
|
def multiply(self, other: 'Tensor') -> 'Tensor':
|
|
"""
|
|
Multiply two tensors element-wise.
|
|
|
|
TODO: Implement tensor multiplication.
|
|
|
|
APPROACH:
|
|
1. Multiply the numpy arrays using *
|
|
2. Return a new Tensor with the result
|
|
3. Handle broadcasting automatically
|
|
|
|
EXAMPLE:
|
|
Tensor([1, 2]) * Tensor([3, 4]) → Tensor([3, 8])
|
|
|
|
HINTS:
|
|
- Use self._data * other._data
|
|
- Return Tensor(result)
|
|
- This is element-wise, not matrix multiplication
|
|
"""
|
|
### BEGIN SOLUTION
|
|
result = self._data * other._data
|
|
return Tensor(result)
|
|
### END SOLUTION
|
|
|
|
def __add__(self, other: Union['Tensor', int, float]) -> 'Tensor':
|
|
"""
|
|
Addition operator: tensor + other
|
|
|
|
TODO: Implement + operator for tensors.
|
|
|
|
APPROACH:
|
|
1. If other is a Tensor, use tensor addition
|
|
2. If other is a scalar, convert to Tensor first
|
|
3. Return the result
|
|
|
|
EXAMPLE:
|
|
Tensor([1, 2]) + Tensor([3, 4]) → Tensor([4, 6])
|
|
Tensor([1, 2]) + 5 → Tensor([6, 7])
|
|
"""
|
|
### BEGIN SOLUTION
|
|
if isinstance(other, Tensor):
|
|
return self.add(other)
|
|
else:
|
|
return self.add(Tensor(other))
|
|
### END SOLUTION
|
|
|
|
def __mul__(self, other: Union['Tensor', int, float]) -> 'Tensor':
|
|
"""
|
|
Multiplication operator: tensor * other
|
|
|
|
TODO: Implement * operator for tensors.
|
|
|
|
APPROACH:
|
|
1. If other is a Tensor, use tensor multiplication
|
|
2. If other is a scalar, convert to Tensor first
|
|
3. Return the result
|
|
|
|
EXAMPLE:
|
|
Tensor([1, 2]) * Tensor([3, 4]) → Tensor([3, 8])
|
|
Tensor([1, 2]) * 3 → Tensor([3, 6])
|
|
"""
|
|
### BEGIN SOLUTION
|
|
if isinstance(other, Tensor):
|
|
return self.multiply(other)
|
|
else:
|
|
return self.multiply(Tensor(other))
|
|
### END SOLUTION
|
|
|
|
def __sub__(self, other: Union['Tensor', int, float]) -> 'Tensor':
|
|
"""
|
|
Subtraction operator: tensor - other
|
|
|
|
TODO: Implement - operator for tensors.
|
|
|
|
APPROACH:
|
|
1. Convert other to Tensor if needed
|
|
2. Subtract using numpy arrays
|
|
3. Return new Tensor with result
|
|
|
|
EXAMPLE:
|
|
Tensor([5, 6]) - Tensor([1, 2]) → Tensor([4, 4])
|
|
Tensor([5, 6]) - 1 → Tensor([4, 5])
|
|
"""
|
|
### BEGIN SOLUTION
|
|
if isinstance(other, Tensor):
|
|
result = self._data - other._data
|
|
else:
|
|
result = self._data - other
|
|
return Tensor(result)
|
|
### END SOLUTION
|
|
|
|
def __truediv__(self, other: Union['Tensor', int, float]) -> 'Tensor':
|
|
"""
|
|
Division operator: tensor / other
|
|
|
|
TODO: Implement / operator for tensors.
|
|
|
|
APPROACH:
|
|
1. Convert other to Tensor if needed
|
|
2. Divide using numpy arrays
|
|
3. Return new Tensor with result
|
|
|
|
EXAMPLE:
|
|
Tensor([6, 8]) / Tensor([2, 4]) → Tensor([3, 2])
|
|
Tensor([6, 8]) / 2 → Tensor([3, 4])
|
|
"""
|
|
### BEGIN SOLUTION
|
|
if isinstance(other, Tensor):
|
|
result = self._data / other._data
|
|
else:
|
|
result = self._data / other
|
|
return Tensor(result)
|
|
### END SOLUTION
|