Files
TinyTorch/tinytorch/core/tensor.py
Vijay Janapa Reddi cf1c9362c3 feat: Add matrix multiplication scaffolding to Layers module
- Add matmul_naive function with for-loop implementation for learning
- Update Dense layer to support both NumPy (@) and naive matrix multiplication
- Add comprehensive tests comparing both implementations (correctness & performance)
- Include step-by-step computation visualization for 2x2 matrices
- Fix missing imports in tensor.py and activations.py
- Export both tensor and activations modules to package

This provides students with immediate success using NumPy while allowing them to
understand the underlying computation through explicit for-loops. The scaffolding
includes performance comparisons and educational insights about why NumPy is faster.
2025-07-10 23:27:02 -04:00

217 lines
7.6 KiB
Python

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../modules/tensor/tensor_dev.ipynb.
# %% auto 0
__all__ = ['Tensor']
# %% ../../modules/tensor/tensor_dev.ipynb 3
import numpy as np
import sys
from typing import Union, List, Tuple, Optional, Any
# %% ../../modules/tensor/tensor_dev.ipynb 4
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.
TODO: Implement the core Tensor class with data handling and properties.
"""
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.
"""
raise NotImplementedError("Student implementation required")
@property
def data(self) -> np.ndarray:
"""Access underlying numpy array."""
raise NotImplementedError("Student implementation required")
@property
def shape(self) -> Tuple[int, ...]:
"""Get tensor shape."""
raise NotImplementedError("Student implementation required")
@property
def size(self) -> int:
"""Get total number of elements."""
raise NotImplementedError("Student implementation required")
@property
def dtype(self) -> np.dtype:
"""Get data type as numpy dtype."""
raise NotImplementedError("Student implementation required")
def __repr__(self) -> str:
"""String representation."""
raise NotImplementedError("Student implementation required")
# %% ../../modules/tensor/tensor_dev.ipynb 5
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.
"""
# 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:
# Keep NumPy's auto-detected type, but prefer common ML types
if np.issubdtype(temp_array.dtype, np.integer):
dtype = 'int32'
elif np.issubdtype(temp_array.dtype, np.floating):
dtype = 'float32'
else:
dtype = temp_array.dtype
self._data = temp_array.astype(dtype)
elif isinstance(data, np.ndarray):
self._data = data.astype(dtype or data.dtype)
else:
raise TypeError(f"Cannot create tensor from {type(data)}")
@property
def data(self) -> np.ndarray:
"""Access underlying numpy array."""
return self._data
@property
def shape(self) -> Tuple[int, ...]:
"""Get tensor shape."""
return self._data.shape
@property
def size(self) -> int:
"""Get total number of elements."""
return self._data.size
@property
def dtype(self) -> np.dtype:
"""Get data type as numpy dtype."""
return self._data.dtype
def __repr__(self) -> str:
"""String representation."""
return f"Tensor({self._data.tolist()}, shape={self.shape}, dtype={self.dtype})"
# %% ../../modules/tensor/tensor_dev.ipynb 9
def _add_arithmetic_methods():
"""
Add arithmetic operations to Tensor class.
TODO: Implement arithmetic methods (__add__, __sub__, __mul__, __truediv__)
and their reverse operations (__radd__, __rsub__, etc.)
"""
def __add__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Addition: tensor + other"""
raise NotImplementedError("Student implementation required")
def __sub__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Subtraction: tensor - other"""
raise NotImplementedError("Student implementation required")
def __mul__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Multiplication: tensor * other"""
raise NotImplementedError("Student implementation required")
def __truediv__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Division: tensor / other"""
raise NotImplementedError("Student implementation required")
# Add methods to Tensor class
Tensor.__add__ = __add__
Tensor.__sub__ = __sub__
Tensor.__mul__ = __mul__
Tensor.__truediv__ = __truediv__
# %% ../../modules/tensor/tensor_dev.ipynb 10
def _add_arithmetic_methods():
"""Add arithmetic operations to Tensor class."""
def __add__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Addition: tensor + other"""
if isinstance(other, Tensor):
return Tensor(self._data + other._data)
else: # scalar
return Tensor(self._data + other)
def __sub__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Subtraction: tensor - other"""
if isinstance(other, Tensor):
return Tensor(self._data - other._data)
else: # scalar
return Tensor(self._data - other)
def __mul__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Multiplication: tensor * other"""
if isinstance(other, Tensor):
return Tensor(self._data * other._data)
else: # scalar
return Tensor(self._data * other)
def __truediv__(self, other: Union['Tensor', int, float]) -> 'Tensor':
"""Division: tensor / other"""
if isinstance(other, Tensor):
return Tensor(self._data / other._data)
else: # scalar
return Tensor(self._data / other)
def __radd__(self, other: Union[int, float]) -> 'Tensor':
"""Reverse addition: scalar + tensor"""
return Tensor(other + self._data)
def __rsub__(self, other: Union[int, float]) -> 'Tensor':
"""Reverse subtraction: scalar - tensor"""
return Tensor(other - self._data)
def __rmul__(self, other: Union[int, float]) -> 'Tensor':
"""Reverse multiplication: scalar * tensor"""
return Tensor(other * self._data)
def __rtruediv__(self, other: Union[int, float]) -> 'Tensor':
"""Reverse division: scalar / tensor"""
return Tensor(other / self._data)
# Add methods to Tensor class
Tensor.__add__ = __add__
Tensor.__sub__ = __sub__
Tensor.__mul__ = __mul__
Tensor.__truediv__ = __truediv__
Tensor.__radd__ = __radd__
Tensor.__rsub__ = __rsub__
Tensor.__rmul__ = __rmul__
Tensor.__rtruediv__ = __rtruediv__
# Call the function to add arithmetic methods
_add_arithmetic_methods()