Files
TinyTorch/tests/module_04/test_progressive_integration.py
Vijay Janapa Reddi 86b908fe5c Add TinyTorch examples gallery and fix module integration issues
- Create professional examples directory showcasing TinyTorch as real ML framework
- Add examples: XOR, MNIST, CIFAR-10, text generation, autograd demo, optimizer comparison
- Fix import paths in exported modules (training.py, dense.py)
- Update training module with autograd integration for loss functions
- Add progressive integration tests for all 16 modules
- Document framework capabilities and usage patterns

This commit establishes the examples gallery that demonstrates TinyTorch
works like PyTorch/TensorFlow, validating the complete framework.
2025-09-21 10:00:11 -04:00

995 lines
38 KiB
Python

"""
Module 04: Progressive Integration Tests
Tests that Module 04 (Layers) works correctly AND that the foundation stack (01→03) still works.
DEPENDENCY CHAIN: 01_setup → 02_tensor → 03_activations → 04_layers
This is where we create reusable building blocks for neural networks.
🎯 WHAT THIS TESTS:
- Module 04: Layer base class and interface design
- Integration: Layers work with tensors and activations from previous modules
- Regression: Previous modules (01→03) still work correctly
- Preparation: Foundation ready for Module 05 (Dense networks)
💡 FOR STUDENTS: If tests fail, check:
1. Does your Layer base class exist in tinytorch.core.layers?
2. Does Layer have a forward() method?
3. Is Layer callable (has __call__ method)?
4. Do layers work with Tensor objects from Module 02?
"""
import numpy as np
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
class TestPriorModulesStillWork:
"""
🔄 REGRESSION CHECK: Verify Modules 01→03 still work after Layer development.
💡 If these fail: You may have broken something in the foundation while working on layers.
🔧 Fix: Check that your layer code doesn't interfere with basic tensor/activation functionality.
"""
def test_setup_environment_stable(self):
"""
✅ TEST: Module 01 (Setup) - Environment should still work
📋 CHECKS:
- Python 3.8+ available
- NumPy working correctly
- Project structure intact
🚨 IF FAILS: Your development environment is broken
🔧 FIX: Check Python installation, reinstall NumPy if needed
"""
try:
# Environment checks
assert sys.version_info >= (3, 8), \
"❌ Python 3.8+ required. Current: Python {}.{}".format(
sys.version_info.major, sys.version_info.minor)
# NumPy functionality
import numpy as np
test_array = np.array([1, 2, 3])
assert test_array.shape == (3,), \
"❌ NumPy broken. Cannot create basic arrays."
# Project structure
project_root = Path(__file__).parent.parent.parent
assert (project_root / "modules").exists(), \
"❌ Project structure broken. Missing 'modules' directory."
except Exception as e:
assert False, f"""
❌ MODULE 01 REGRESSION: Setup environment broken!
🔍 ERROR: {str(e)}
🔧 HOW TO FIX:
1. Check Python version: python --version (need 3.8+)
2. Reinstall NumPy: pip install numpy
3. Verify project structure exists
4. Run Module 01 tests separately: python tests/run_all_modules.py --module module_01
"""
def test_tensor_operations_stable(self):
"""
✅ TEST: Module 02 (Tensor) - Tensors should still work
📋 CHECKS:
- Tensor class can be imported
- Basic tensor creation works
- Tensor operations function correctly
🚨 IF FAILS: Tensor implementation is broken
🔧 FIX: Check your tensor implementation, ensure exports are correct
"""
try:
from tinytorch.core.tensor import Tensor
# Basic tensor creation
t = Tensor([1, 2, 3])
assert t.shape == (3,), \
f"❌ Tensor shape broken. Expected (3,), got {t.shape}"
# Multi-dimensional tensors
t2 = Tensor(np.random.randn(4, 5))
assert t2.shape == (4, 5), \
f"❌ Multi-dim tensor broken. Expected (4, 5), got {t2.shape}"
except ImportError as e:
assert False, f"""
❌ MODULE 02 REGRESSION: Cannot import Tensor!
🔍 IMPORT ERROR: {str(e)}
🔧 HOW TO FIX:
1. Implement Tensor class in modules/source/02_tensor/
2. Export module: tito module complete 02_tensor
3. Check tinytorch.core.tensor exists
4. Verify Tensor class is exported correctly
📚 EXPECTED STRUCTURE:
class Tensor:
def __init__(self, data): ...
@property
def shape(self): ...
"""
except Exception as e:
assert False, f"""
❌ MODULE 02 REGRESSION: Tensor functionality broken!
🔍 ERROR: {str(e)}
🔧 HOW TO FIX:
1. Check Tensor.__init__ accepts data parameter
2. Ensure Tensor.shape property returns correct tuple
3. Test with: t = Tensor([1,2,3]); print(t.shape)
4. Run Module 02 tests: python tests/run_all_modules.py --module module_02
"""
def test_activations_stable(self):
"""
✅ TEST: Module 03 (Activations) - Activations should still work
📋 CHECKS:
- Activation functions can be imported
- ReLU and Sigmoid work correctly
- Activations work with tensors
🚨 IF FAILS: Activation implementation is broken
🔧 FIX: Check activation function implementations
"""
try:
from tinytorch.core.activations import ReLU, Sigmoid
from tinytorch.core.tensor import Tensor
# ReLU functionality
relu = ReLU()
x = Tensor(np.array([-1, 0, 1]))
output = relu(x)
expected = np.array([0, 0, 1])
assert np.array_equal(output.data, expected), \
f"❌ ReLU broken. Expected {expected}, got {output.data}"
# Sigmoid functionality
sigmoid = Sigmoid()
x = Tensor(np.array([0]))
output = sigmoid(x)
assert np.isclose(output.data[0], 0.5, atol=1e-6), \
f"❌ Sigmoid broken. Expected ~0.5, got {output.data[0]}"
except ImportError as e:
assert False, f"""
❌ MODULE 03 REGRESSION: Cannot import activations!
🔍 IMPORT ERROR: {str(e)}
🔧 HOW TO FIX:
1. Implement ReLU and Sigmoid in modules/source/03_activations/
2. Export module: tito module complete 03_activations
3. Check tinytorch.core.activations exists
4. Verify activation classes are exported
📚 EXPECTED STRUCTURE:
class ReLU:
def __call__(self, x): return max(0, x)
class Sigmoid:
def __call__(self, x): return 1/(1+exp(-x))
"""
except Exception as e:
assert False, f"""
❌ MODULE 03 REGRESSION: Activation functionality broken!
🔍 ERROR: {str(e)}
🔧 HOW TO FIX:
1. Check ReLU returns max(0, x) element-wise
2. Check Sigmoid returns 1/(1+exp(-x))
3. Ensure activations are callable: relu(tensor)
4. Test activations work with Tensor objects
5. Run Module 03 tests: python tests/run_all_modules.py --module module_03
"""
class TestModule04LayersCore:
"""
🆕 NEW FUNCTIONALITY: Test Module 04 (Layers) core implementation.
💡 What you're implementing: Base Layer class that all neural network layers inherit from.
🎯 Goal: Create a standard interface for all layers (Dense, Conv2D, etc.)
"""
def test_layer_base_class_exists(self):
"""
✅ TEST: Layer base class - Foundation for all neural network layers
📋 WHAT YOU NEED TO IMPLEMENT:
class Layer:
def forward(self, x): raise NotImplementedError
def __call__(self, x): return self.forward(x)
🚨 IF FAILS: Layer base class doesn't exist or missing methods
"""
try:
from tinytorch.core.layers import Layer
# Layer class should exist
assert hasattr(Layer, 'forward'), \
"❌ Layer missing forward() method. All layers need forward(x) -> output"
assert hasattr(Layer, '__call__'), \
"❌ Layer missing __call__() method. Layers should be callable: layer(x)"
# Test instantiation
layer = Layer()
assert layer is not None, \
"❌ Cannot create Layer instance"
except ImportError as e:
assert False, f"""
❌ LAYER BASE CLASS MISSING!
🔍 IMPORT ERROR: {str(e)}
🔧 HOW TO IMPLEMENT:
1. Create in modules/source/04_layers/04_layers_dev.py:
class Layer:
'''Base class for all neural network layers.'''
def forward(self, x):
'''Forward pass through the layer.'''
raise NotImplementedError("Subclasses must implement forward()")
def __call__(self, x):
'''Make layer callable: layer(x) calls layer.forward(x)'''
return self.forward(x)
2. Export the module:
tito module complete 04_layers
📚 WHY THIS MATTERS:
- All layers (Dense, Conv2D, etc.) will inherit from Layer
- Provides consistent interface: layer(input) -> output
- Enables polymorphism: treat all layers the same way
"""
except Exception as e:
assert False, f"""
❌ LAYER BASE CLASS BROKEN!
🔍 ERROR: {str(e)}
🔧 HOW TO FIX:
1. Check Layer class has forward() method
2. Check Layer class has __call__() method
3. Ensure Layer can be instantiated
4. Test with: layer = Layer(); print(type(layer))
"""
def test_layer_interface_design(self):
"""
✅ TEST: Layer interface - Proper object-oriented design
📋 CHECKS:
- Layer is callable (can use layer(x) syntax)
- forward() method raises NotImplementedError (abstract method)
- __call__ delegates to forward()
🎯 DESIGN GOAL: Clean, consistent interface for all layer types
"""
try:
from tinytorch.core.layers import Layer
layer = Layer()
# Test that forward raises NotImplementedError (abstract method)
try:
layer.forward(None)
assert False, "❌ Layer.forward() should raise NotImplementedError"
except NotImplementedError:
pass # This is expected
# Test that __call__ delegates to forward
assert callable(layer), \
"❌ Layer should be callable. Add __call__ method."
# Test __call__ behavior
try:
layer(None) # Should call forward and raise NotImplementedError
assert False, "❌ layer(x) should raise NotImplementedError (calls forward)"
except NotImplementedError:
pass # This is expected
except ImportError:
assert False, """
❌ Cannot test layer interface - Layer class missing!
See test_layer_base_class_exists for implementation guide.
"""
except Exception as e:
assert False, f"""
❌ LAYER INTERFACE DESIGN BROKEN!
🔍 ERROR: {str(e)}
🔧 HOW TO FIX:
Your Layer class should look like:
class Layer:
def forward(self, x):
raise NotImplementedError("Subclasses must implement forward()")
def __call__(self, x):
return self.forward(x)
📝 DESIGN PRINCIPLES:
- forward(): Abstract method, subclasses override this
- __call__(): Makes layer callable, delegates to forward()
- Inheritance: All layers inherit this interface
"""
def test_custom_layer_implementation(self):
"""
✅ TEST: Custom layer example - Verify inheritance works
📋 WHAT THIS TESTS:
- Can create custom layers that inherit from Layer
- Custom forward() method works correctly
- Layer interface is flexible and extensible
💡 FOR STUDENTS: This shows how your Dense layer (Module 05) will work
"""
try:
from tinytorch.core.layers import Layer
from tinytorch.core.tensor import Tensor
# Create a simple custom layer for testing
class TestLayer(Layer):
def __init__(self, multiplier=2):
self.multiplier = multiplier
def forward(self, x):
# Simple operation: multiply input by constant
return Tensor(x.data * self.multiplier)
# Test custom layer
test_layer = TestLayer(multiplier=3)
# Test with tensor input
x = Tensor([1, 2, 3])
output = test_layer(x) # Should call __call__ -> forward()
expected = np.array([3, 6, 9])
assert np.array_equal(output.data, expected), \
f"❌ Custom layer broken. Expected {expected}, got {output.data}"
# Test that it's using the Layer interface
assert isinstance(test_layer, Layer), \
"❌ Custom layer should inherit from Layer"
except ImportError:
assert False, """
❌ Cannot test custom layer - missing dependencies!
Need both Layer (Module 04) and Tensor (Module 02) working.
"""
except Exception as e:
assert False, f"""
❌ CUSTOM LAYER IMPLEMENTATION BROKEN!
🔍 ERROR: {str(e)}
🔧 POSSIBLE ISSUES:
1. Layer.__call__ not working correctly
2. Layer inheritance broken
3. forward() method implementation issues
4. Tensor compatibility problems
💡 DEBUG STEPS:
1. Test Layer base class separately
2. Check Tensor operations work
3. Verify inheritance: isinstance(custom_layer, Layer)
4. Test method calls: custom_layer.forward(x)
"""
class TestProgressiveStackIntegration:
"""
🔗 INTEGRATION TEST: Layer + Tensor + Activations working together.
💡 This tests the "progressive stack" - ensuring all modules 01→04 work together.
🎯 Goal: Verify you can build neural network components using everything so far.
"""
def test_layer_tensor_integration(self):
"""
✅ TEST: Layers work correctly with Tensors
📋 INTEGRATION POINTS:
- Layer.forward() accepts Tensor objects
- Layer returns Tensor objects
- Tensor operations work within layers
💡 This is crucial for Module 05 (Dense layers)
"""
try:
from tinytorch.core.layers import Layer
from tinytorch.core.tensor import Tensor
# Create a layer that actually works with tensors
class IdentityLayer(Layer):
def forward(self, x):
# Return input unchanged (identity function)
return Tensor(x.data)
layer = IdentityLayer()
# Test with different tensor shapes
test_cases = [
([1, 2, 3], (3,)),
([[1, 2], [3, 4]], (2, 2)),
(np.random.randn(5, 10), (5, 10))
]
for input_data, expected_shape in test_cases:
x = Tensor(input_data)
output = layer(x)
assert isinstance(output, Tensor), \
f"❌ Layer should return Tensor, got {type(output)}"
assert output.shape == expected_shape, \
f"❌ Wrong output shape. Expected {expected_shape}, got {output.shape}"
assert np.array_equal(output.data, x.data), \
"❌ Identity layer should preserve data"
except Exception as e:
assert False, f"""
❌ LAYER-TENSOR INTEGRATION BROKEN!
🔍 ERROR: {str(e)}
🔧 COMMON ISSUES:
1. Layer.forward() doesn't accept Tensor objects
2. Layer doesn't return Tensor objects
3. Tensor.data access broken
4. Shape handling incorrect
💡 INTEGRATION REQUIREMENTS:
- Layers must accept Tensor inputs
- Layers must return Tensor outputs
- Preserve tensor properties (shape, data)
🧪 DEBUG TEST:
from tinytorch.core.tensor import Tensor
from tinytorch.core.layers import Layer
t = Tensor([1, 2, 3])
print(f"Tensor shape: {{t.shape}}")
print(f"Tensor data: {{t.data}}")
"""
def test_layer_activation_integration(self):
"""
✅ TEST: Layers work with activation functions
📋 INTEGRATION POINTS:
- Layer output can be fed to activations
- Activations can be chained with layers
- Combined operations preserve tensor properties
💡 This is the foundation for neural networks: layer -> activation -> layer
"""
try:
from tinytorch.core.layers import Layer
from tinytorch.core.tensor import Tensor
from tinytorch.core.activations import ReLU
# Simple layer for testing
class ScaleLayer(Layer):
def forward(self, x):
# Scale input by 2, then subtract 1 (some values will be negative)
return Tensor(x.data * 2 - 1)
# Test layer -> activation chain
layer = ScaleLayer()
relu = ReLU()
# Input that will produce both positive and negative values after scaling
x = Tensor([0, 0.5, 1.0]) # After scaling: [-1, 0, 1]
# Layer -> Activation chain
layer_output = layer(x)
final_output = relu(layer_output)
# Check the chain works
expected_after_layer = np.array([-1, 0, 1])
expected_after_relu = np.array([0, 0, 1])
assert np.array_equal(layer_output.data, expected_after_layer), \
f"❌ Layer output wrong. Expected {expected_after_layer}, got {layer_output.data}"
assert np.array_equal(final_output.data, expected_after_relu), \
f"❌ ReLU after layer wrong. Expected {expected_after_relu}, got {final_output.data}"
except Exception as e:
assert False, f"""
❌ LAYER-ACTIVATION INTEGRATION BROKEN!
🔍 ERROR: {str(e)}
🔧 INTEGRATION REQUIREMENTS:
1. Layer output should be valid Tensor
2. Activation should accept layer output
3. Chain: input -> layer -> activation -> output
💡 NEURAL NETWORK BUILDING BLOCKS:
This integration is crucial because neural networks are:
input -> layer -> activation -> layer -> activation -> ... -> output
🧪 DEBUG TEST:
x = Tensor([1, 2, 3])
layer_out = layer(x)
activation_out = relu(layer_out)
print(f"Layer: {{layer_out.data}}")
print(f"ReLU: {{activation_out.data}}")
"""
def test_complete_stack_readiness(self):
"""
✅ TEST: Complete stack (01→04) ready for neural networks
📋 COMPREHENSIVE CHECK:
- Environment (01) working
- Tensors (02) working
- Activations (03) working
- Layers (04) working
- All components work together
🎯 MILESTONE: Ready for Module 05 (Dense Networks)!
"""
try:
# Import all components
from tinytorch.core.tensor import Tensor
from tinytorch.core.activations import ReLU, Sigmoid
from tinytorch.core.layers import Layer
# Create complete neural network building blocks
class LinearLayer(Layer):
def forward(self, x):
# Simple linear transformation: y = x + 1
return Tensor(x.data + 1)
# Test complete pipeline
x = Tensor([-2, -1, 0, 1, 2])
# Build mini neural network: input -> layer -> relu -> layer -> sigmoid
layer1 = LinearLayer() # x + 1: [-1, 0, 1, 2, 3]
relu = ReLU() # max(0, x): [0, 0, 1, 2, 3]
layer2 = LinearLayer() # x + 1: [1, 1, 2, 3, 4]
sigmoid = Sigmoid() # 1/(1+exp(-x)): ~[0.73, 0.73, 0.88, 0.95, 0.98]
# Forward pass through complete stack
h1 = layer1(x)
h1_act = relu(h1)
h2 = layer2(h1_act)
output = sigmoid(h2)
# Verify each step
assert h1.shape == x.shape, "❌ Layer 1 output shape wrong"
assert h1_act.shape == x.shape, "❌ ReLU output shape wrong"
assert h2.shape == x.shape, "❌ Layer 2 output shape wrong"
assert output.shape == x.shape, "❌ Sigmoid output shape wrong"
# Verify sigmoid output range
assert np.all(output.data >= 0) and np.all(output.data <= 1), \
"❌ Sigmoid output not in range [0,1]"
# Verify the complete computation chain worked
assert len(output.data) == 5, "❌ Complete stack pipeline broken"
except Exception as e:
assert False, f"""
❌ COMPLETE STACK NOT READY!
🔍 ERROR: {str(e)}
🎯 STACK REQUIREMENTS (Modules 01→04):
✅ Module 01: Python environment, NumPy, project structure
✅ Module 02: Tensor class with data and shape
✅ Module 03: ReLU and Sigmoid activation functions
✅ Module 04: Layer base class with forward() and __call__()
🔧 FIX EACH MODULE:
1. Run individual module tests to isolate the issue
2. Check exports: tito module complete XX_modulename
3. Verify imports work: from tinytorch.core.* import *
💡 DEBUGGING STRATEGY:
Test each component separately:
- Tensor: t = Tensor([1,2,3]); print(t.shape)
- ReLU: r = ReLU(); print(r(Tensor([-1,1])).data)
- Layer: l = Layer(); print(callable(l))
"""
class TestNeuralNetworkReadiness:
"""
🧠 NEURAL NETWORK READINESS: Test foundation is ready for real neural networks.
💡 These tests verify you have everything needed for Module 05 (Dense Networks).
🎯 Goal: Confirm the foundation supports building actual neural networks.
"""
def test_parameter_handling_foundation(self):
"""
✅ TEST: Foundation supports neural network parameters (weights, biases)
📋 WHAT NEURAL NETWORKS NEED:
- Store parameters (weights, biases)
- Update parameters during training
- Initialize parameters properly
💡 This prepares for Dense layer implementation
"""
try:
from tinytorch.core.tensor import Tensor
from tinytorch.core.layers import Layer
# Test parameter storage and manipulation
class ParameterizedLayer(Layer):
def __init__(self, input_size, output_size):
# Neural network parameters
self.weights = Tensor(np.random.randn(input_size, output_size))
self.bias = Tensor(np.random.randn(output_size))
def forward(self, x):
# Simple linear transformation: y = x @ W + b
# Note: Using numpy for now, will use Tensor ops later
output_data = np.dot(x.data, self.weights.data) + self.bias.data
return Tensor(output_data)
# Test parameterized layer
layer = ParameterizedLayer(3, 2) # 3 inputs -> 2 outputs
# Check parameters exist and have correct shapes
assert hasattr(layer, 'weights'), "❌ Layer missing weights parameter"
assert hasattr(layer, 'bias'), "❌ Layer missing bias parameter"
assert layer.weights.shape == (3, 2), \
f"❌ Wrong weight shape. Expected (3, 2), got {layer.weights.shape}"
assert layer.bias.shape == (2,), \
f"❌ Wrong bias shape. Expected (2,), got {layer.bias.shape}"
# Test forward pass
x = Tensor([[1, 2, 3], [4, 5, 6]]) # 2 samples, 3 features each
output = layer(x)
assert output.shape == (2, 2), \
f"❌ Wrong output shape. Expected (2, 2), got {output.shape}"
except Exception as e:
assert False, f"""
❌ PARAMETER HANDLING FOUNDATION BROKEN!
🔍 ERROR: {str(e)}
🔧 NEURAL NETWORK REQUIREMENTS:
1. Layers must store parameters (weights, bias)
2. Parameters must be Tensor objects
3. Forward pass must use parameters correctly
4. Matrix operations must work
💡 WHAT THIS ENABLES:
- Dense layers (Module 05)
- Convolutional layers (Module 06)
- All parameterized neural network components
🧪 DEBUG CHECKLIST:
□ Can create Tensor with random data?
□ Can access Tensor.shape property?
□ Can do matrix multiplication with numpy?
□ Can store Tensors as instance variables?
"""
def test_batch_processing_foundation(self):
"""
✅ TEST: Foundation supports batch processing (multiple samples at once)
📋 BATCH PROCESSING:
- Process multiple inputs simultaneously
- Maintain correct dimensions
- Enable efficient training
💡 Essential for real neural network training
"""
try:
from tinytorch.core.tensor import Tensor
from tinytorch.core.activations import ReLU
# Test batch processing
batch_size = 32
feature_dim = 10
# Create batch of data
batch_data = Tensor(np.random.randn(batch_size, feature_dim))
# Test batch processing through activation
relu = ReLU()
batch_output = relu(batch_data)
# Verify batch dimensions preserved
assert batch_output.shape == (batch_size, feature_dim), \
f"❌ Batch processing broken. Expected {(batch_size, feature_dim)}, got {batch_output.shape}"
# Verify ReLU applied element-wise to entire batch
assert np.all(batch_output.data >= 0), \
"❌ ReLU not applied correctly to batch"
# Test that we can process different batch sizes
for test_batch_size in [1, 16, 64]:
test_batch = Tensor(np.random.randn(test_batch_size, feature_dim))
test_output = relu(test_batch)
assert test_output.shape == (test_batch_size, feature_dim), \
f"❌ Batch size {test_batch_size} processing broken"
except Exception as e:
assert False, f"""
❌ BATCH PROCESSING FOUNDATION BROKEN!
🔍 ERROR: {str(e)}
🔧 BATCH PROCESSING REQUIREMENTS:
1. Tensors must support 2D+ arrays (batch_size, features)
2. Operations must work element-wise on batches
3. Shape preservation through operations
4. Support variable batch sizes
💡 WHY BATCH PROCESSING MATTERS:
- Training uses batches of data (e.g., 32 samples at once)
- Much more efficient than processing one sample at a time
- Essential for modern deep learning
🧪 DEBUG TEST:
batch = Tensor(np.random.randn(4, 3)) # 4 samples, 3 features
print(f"Batch shape: {{batch.shape}}")
relu = ReLU()
output = relu(batch)
print(f"Output shape: {{output.shape}}")
"""
def test_neural_network_workflow_ready(self):
"""
✅ TEST: Complete neural network workflow foundation
📋 WORKFLOW COMPONENTS:
- Data input (batches)
- Layer processing
- Activation functions
- Output generation
🎯 MILESTONE: Ready for Module 05 Dense Networks!
"""
try:
from tinytorch.core.tensor import Tensor
from tinytorch.core.layers import Layer
from tinytorch.core.activations import ReLU, Sigmoid
# Simulate complete neural network workflow
class MockDenseLayer(Layer):
def __init__(self, in_features, out_features):
self.weights = Tensor(np.random.randn(in_features, out_features) * 0.1)
self.bias = Tensor(np.random.randn(out_features) * 0.1)
def forward(self, x):
# Dense layer: y = x @ W + b
output = np.dot(x.data, self.weights.data) + self.bias.data
return Tensor(output)
# Build mini neural network: 784 -> 128 -> 64 -> 10 (like MNIST)
layer1 = MockDenseLayer(784, 128)
layer2 = MockDenseLayer(128, 64)
layer3 = MockDenseLayer(64, 10)
relu = ReLU()
sigmoid = Sigmoid()
# Simulate MNIST-like input
batch_size = 32
x = Tensor(np.random.randn(batch_size, 784)) # Flattened 28x28 images
# Forward pass through network
h1 = relu(layer1(x)) # 32 x 128
h2 = relu(layer2(h1)) # 32 x 64
logits = layer3(h2) # 32 x 10
output = sigmoid(logits) # 32 x 10
# Verify complete workflow
assert output.shape == (32, 10), \
f"❌ Neural network output shape wrong. Expected (32, 10), got {output.shape}"
# Verify output is valid probabilities (after sigmoid)
assert np.all(output.data >= 0) and np.all(output.data <= 1), \
"❌ Neural network output not in valid range [0, 1]"
# Verify no NaN or infinity values
assert not np.any(np.isnan(output.data)), \
"❌ Neural network produced NaN values"
assert not np.any(np.isinf(output.data)), \
"❌ Neural network produced infinity values"
except Exception as e:
assert False, f"""
❌ NEURAL NETWORK WORKFLOW NOT READY!
🔍 ERROR: {str(e)}
🎯 COMPLETE WORKFLOW REQUIREMENTS:
1. ✅ Layers can be chained together
2. ✅ Activations work between layers
3. ✅ Batch processing throughout
4. ✅ Proper shapes maintained
5. ✅ Numerical stability
🎉 SUCCESS MEANS:
You can build real neural networks like:
- MNIST digit classification (784 -> 128 -> 64 -> 10)
- Image classification CNNs
- Language models with transformers
🔧 IF FAILING:
Check each component individually:
1. Layer parameter storage and forward pass
2. Activation function batch processing
3. Tensor shape handling
4. Matrix multiplication correctness
💡 YOU'RE ALMOST THERE!
This failure means you're very close to building real neural networks.
Fix the specific error above and you'll have a working foundation!
"""
class TestModuleCompletionReadiness:
"""
✅ COMPLETION CHECK: Module 04 ready and foundation set for Module 05.
🎯 This is the final validation that everything works before moving to Dense Networks.
"""
def test_module_04_complete(self):
"""
✅ FINAL TEST: Module 04 (Layers) is complete and working
📋 COMPLETION CHECKLIST:
□ Layer base class implemented
□ Proper interface design (__call__, forward)
□ Integration with previous modules
□ Ready for inheritance (Dense, Conv2D, etc.)
🎯 SUCCESS = Ready for Module 05: Dense Networks!
"""
completion_checklist = {
"Layer base class exists": False,
"Layer has forward() method": False,
"Layer is callable": False,
"Layer works with Tensors": False,
"Layer chains with activations": False,
"Foundation supports parameters": False,
"Foundation supports batches": False,
"Ready for neural networks": False
}
try:
# Check 1: Layer base class
from tinytorch.core.layers import Layer
completion_checklist["Layer base class exists"] = True
# Check 2: forward method
layer = Layer()
assert hasattr(layer, 'forward')
completion_checklist["Layer has forward() method"] = True
# Check 3: callable
assert callable(layer)
completion_checklist["Layer is callable"] = True
# Check 4: works with tensors
from tinytorch.core.tensor import Tensor
class TestLayer(Layer):
def forward(self, x):
return Tensor(x.data)
test_layer = TestLayer()
x = Tensor([1, 2, 3])
output = test_layer(x)
assert isinstance(output, Tensor)
completion_checklist["Layer works with Tensors"] = True
# Check 5: chains with activations
from tinytorch.core.activations import ReLU
relu = ReLU()
chained_output = relu(output)
assert isinstance(chained_output, Tensor)
completion_checklist["Layer chains with activations"] = True
# Check 6: supports parameters
class ParameterLayer(Layer):
def __init__(self):
self.weight = Tensor([0.5])
def forward(self, x):
return Tensor(x.data * self.weight.data)
param_layer = ParameterLayer()
param_output = param_layer(x)
completion_checklist["Foundation supports parameters"] = True
# Check 7: supports batches
batch_x = Tensor(np.random.randn(4, 3))
batch_output = test_layer(batch_x)
assert batch_output.shape == (4, 3)
completion_checklist["Foundation supports batches"] = True
# Check 8: ready for neural networks
completion_checklist["Ready for neural networks"] = True
except Exception as e:
# Show progress even if not complete
completed_count = sum(completion_checklist.values())
total_count = len(completion_checklist)
progress_report = "\n🔍 COMPLETION PROGRESS:\n"
for check, completed in completion_checklist.items():
status = "" if completed else ""
progress_report += f" {status} {check}\n"
progress_report += f"\n📊 Progress: {completed_count}/{total_count} checks passed"
assert False, f"""
❌ MODULE 04 NOT COMPLETE!
🔍 ERROR: {str(e)}
{progress_report}
🔧 NEXT STEPS:
1. Fix the failing check above
2. Re-run this test
3. When all ✅, you're ready for Module 05!
💡 ALMOST THERE!
You've completed {completed_count}/{total_count} requirements.
Just fix the error above and you'll have a complete Layer foundation!
"""
# If we get here, everything passed!
assert True, """
🎉 MODULE 04 COMPLETE! 🎉
✅ Layer base class implemented
✅ Proper interface design
✅ Integration with foundation
✅ Ready for neural networks
🚀 READY FOR MODULE 05: DENSE NETWORKS!
💡 What you can now do:
- Inherit from Layer to create Dense layers
- Build multi-layer neural networks
- Train on real datasets
- Solve non-linear problems like XOR
🎯 Next: Implement Dense layer in Module 05!
"""
# Note: No regression prevention section needed here since we ARE testing regression
# by checking that modules 01-03 still work after implementing module 04.