""" Module 11: Progressive Integration Tests Tests that Module 11 (Training) works correctly AND that the entire prior stack works. DEPENDENCY CHAIN: 01_setup → 02_tensor → 03_activations → 04_layers → 05_dense → 06_spatial → 07_attention → 08_dataloader → 09_autograd → 10_optimizers → 11_training This is where we enable complete end-to-end training loops. """ 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 TestPriorStackStillWorking: """Quick regression checks that prior modules (01→10) still work.""" def test_complete_ml_pipeline_stable(self): """Verify complete ML pipeline remains stable.""" # Environment (Module 01) assert sys.version_info >= (3, 8), "Foundation broken: Python version" # Complete pipeline should work try: from tinytorch.core.tensor import Tensor from tinytorch.core.layers import Dense from tinytorch.core.data import Dataset, DataLoader from tinytorch.core.optimizers import SGD # All components should be available layer = Dense(5, 2) optimizer = SGD(layer.parameters(), lr=0.01) # Basic functionality should work x = Tensor(np.random.randn(3, 5)) output = layer(x) assert output.shape == (3, 2), "ML pipeline broken" except ImportError: assert True, "ML pipeline not implemented yet" def test_optimization_stable(self): """Verify Module 10 (Optimizers) still works.""" try: from tinytorch.core.optimizers import SGD, Adam from tinytorch.core.layers import Dense # Optimizers should work layer = Dense(3, 1) sgd = SGD(layer.parameters(), lr=0.01) adam = Adam(layer.parameters(), lr=0.001) assert hasattr(sgd, 'step'), "Optimizers broken: SGD step" assert hasattr(adam, 'step'), "Optimizers broken: Adam step" except ImportError: assert True, "Optimizers not implemented yet" class TestModule11TrainingCore: """Test Module 11 (Training) core functionality.""" def test_training_loop_creation(self): """Test basic training loop functionality.""" try: from tinytorch.core.training import Trainer from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD from tinytorch.core.data import Dataset, DataLoader # Create model and optimizer model = Dense(10, 3) optimizer = SGD(model.parameters(), lr=0.01) # Create simple dataset class SimpleDataset(Dataset): def __init__(self): self.data = np.random.randn(20, 10) self.targets = np.random.randint(0, 3, 20) def __len__(self): return 20 def __getitem__(self, idx): return self.data[idx], self.targets[idx] dataset = SimpleDataset() dataloader = DataLoader(dataset, batch_size=4) # Create trainer trainer = Trainer(model, optimizer) # Should have training methods assert hasattr(trainer, 'train') or hasattr(trainer, 'fit'), "Trainer broken: No train method" except ImportError: assert True, "Training loop not implemented yet" def test_loss_function_support(self): """Test loss function integration.""" try: from tinytorch.core.training import CrossEntropyLoss, MSELoss from tinytorch.core.tensor import Tensor # Test MSE loss mse = MSELoss() pred = Tensor(np.array([1.0, 2.0, 3.0])) target = Tensor(np.array([1.5, 2.5, 2.5])) loss = mse(pred, target) assert hasattr(loss, 'data') or isinstance(loss, (float, np.ndarray)), "MSE loss broken" # Test CrossEntropy loss (if implemented) if 'CrossEntropyLoss' in locals(): ce = CrossEntropyLoss() logits = Tensor(np.random.randn(4, 3)) # 4 samples, 3 classes targets = np.array([0, 1, 2, 1]) # Class indices ce_loss = ce(logits, targets) assert hasattr(ce_loss, 'data') or isinstance(ce_loss, (float, np.ndarray)), "CrossEntropy loss broken" except ImportError: assert True, "Loss functions not implemented yet" def test_metrics_computation(self): """Test training metrics computation.""" try: from tinytorch.core.training import accuracy, compute_metrics from tinytorch.core.tensor import Tensor # Test accuracy computation predictions = Tensor(np.array([[0.1, 0.9], [0.8, 0.2], [0.3, 0.7]])) targets = np.array([1, 0, 1]) # True class indices acc = accuracy(predictions, targets) assert isinstance(acc, (float, np.ndarray)), "Accuracy computation broken" assert 0.0 <= acc <= 1.0, "Accuracy not in valid range" # Test comprehensive metrics if 'compute_metrics' in locals(): metrics = compute_metrics(predictions, targets) assert isinstance(metrics, dict), "Metrics should return dict" assert 'accuracy' in metrics, "Metrics missing accuracy" except ImportError: assert True, "Metrics computation not implemented yet" class TestProgressiveStackIntegration: """Test that the complete stack (01→11) works together.""" def test_end_to_end_training(self): """Test complete end-to-end training process.""" try: from tinytorch.core.tensor import Tensor from tinytorch.core.layers import Dense from tinytorch.core.activations import ReLU, Softmax from tinytorch.core.optimizers import SGD from tinytorch.core.training import Trainer, CrossEntropyLoss from tinytorch.core.data import Dataset, DataLoader # Create complete model class SimpleModel: def __init__(self): self.layer1 = Dense(10, 16) self.relu = ReLU() self.layer2 = Dense(16, 3) self.softmax = Softmax() def __call__(self, x): h = self.relu(self.layer1(x)) logits = self.layer2(h) return self.softmax(logits) def parameters(self): params = [] if hasattr(self.layer1, 'parameters'): params.extend(self.layer1.parameters()) if hasattr(self.layer2, 'parameters'): params.extend(self.layer2.parameters()) return params # Create dataset class TrainingDataset(Dataset): def __init__(self): self.data = np.random.randn(50, 10) self.targets = np.random.randint(0, 3, 50) def __len__(self): return 50 def __getitem__(self, idx): return Tensor(self.data[idx]), self.targets[idx] # Setup training model = SimpleModel() optimizer = SGD(model.parameters(), lr=0.01) loss_fn = CrossEntropyLoss() dataset = TrainingDataset() dataloader = DataLoader(dataset, batch_size=8) # Training loop (simplified) for epoch in range(2): # Just 2 epochs for testing for batch_x, batch_y in dataloader: # Forward pass predictions = model(batch_x) loss = loss_fn(predictions, batch_y) # Backward pass (if available) if hasattr(loss, 'backward'): optimizer.zero_grad() loss.backward() optimizer.step() # Verify shapes assert predictions.shape[0] == len(batch_y), "Training batch size mismatch" break # Test one batch per epoch assert True, "End-to-end training successful" except ImportError: assert True, "End-to-end training not ready yet" def test_cnn_training_pipeline(self): """Test CNN training with spatial operations.""" try: from tinytorch.core.spatial import Conv2D, MaxPool2D from tinytorch.core.layers import Dense from tinytorch.core.activations import ReLU from tinytorch.core.optimizers import Adam from tinytorch.core.data import Dataset, DataLoader from tinytorch.core.tensor import Tensor # CNN model class SimpleCNN: def __init__(self): self.conv1 = Conv2D(in_channels=3, out_channels=16, kernel_size=3) self.pool = MaxPool2D(kernel_size=2) self.relu = ReLU() self.fc = Dense(16 * 15 * 15, 5) # Approximate size def __call__(self, x): h = self.relu(self.conv1(x)) h = self.pool(h) # Flatten (simplified) h_flat = h.reshape(h.shape[0], -1) return self.fc(h_flat) def parameters(self): params = [] for module in [self.conv1, self.fc]: if hasattr(module, 'parameters'): params.extend(module.parameters()) return params # Image dataset class ImageDataset(Dataset): def __init__(self): self.data = np.random.randn(20, 3, 32, 32) self.targets = np.random.randint(0, 5, 20) def __len__(self): return 20 def __getitem__(self, idx): return Tensor(self.data[idx]), self.targets[idx] # Setup CNN training cnn_model = SimpleCNN() optimizer = Adam(cnn_model.parameters(), lr=0.001) dataset = ImageDataset() dataloader = DataLoader(dataset, batch_size=4) # Test CNN training step for batch_x, batch_y in dataloader: assert batch_x.shape == (4, 3, 32, 32), "CNN input shape broken" # Forward pass if hasattr(cnn_model.conv1, '__call__'): predictions = cnn_model(batch_x) assert len(predictions.shape) == 2, "CNN output shape broken" break # Test one batch except ImportError: assert True, "CNN training pipeline not ready yet" class TestAdvancedTrainingFeatures: """Test advanced training features and techniques.""" def test_validation_loop(self): """Test validation during training.""" try: from tinytorch.core.training import Trainer from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD from tinytorch.core.data import Dataset, DataLoader # Model and optimizer model = Dense(5, 2) optimizer = SGD(model.parameters(), lr=0.01) # Train and validation datasets class Dataset(Dataset): def __init__(self, size): self.data = np.random.randn(size, 5) self.targets = np.random.randint(0, 2, size) def __len__(self): return len(self.data) def __getitem__(self, idx): return self.data[idx], self.targets[idx] train_dataset = Dataset(30) val_dataset = Dataset(10) train_loader = DataLoader(train_dataset, batch_size=5) val_loader = DataLoader(val_dataset, batch_size=5) # Trainer with validation trainer = Trainer(model, optimizer) if hasattr(trainer, 'validate') or hasattr(trainer, 'evaluate'): # Should be able to run validation assert True, "Validation capability available" except ImportError: assert True, "Validation loop not ready yet" def test_checkpointing_and_early_stopping(self): """Test model checkpointing and early stopping.""" try: from tinytorch.core.training import Trainer, ModelCheckpoint, EarlyStopping from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD model = Dense(5, 1) optimizer = SGD(model.parameters(), lr=0.01) # Checkpointing if 'ModelCheckpoint' in locals(): checkpoint = ModelCheckpoint(filepath='model.pth', save_best=True) assert hasattr(checkpoint, 'save'), "Checkpointing broken" # Early stopping if 'EarlyStopping' in locals(): early_stop = EarlyStopping(patience=5, min_delta=0.001) assert hasattr(early_stop, 'check'), "Early stopping broken" # Training with callbacks trainer = Trainer(model, optimizer) if hasattr(trainer, 'callbacks'): trainer.callbacks = [checkpoint, early_stop] except ImportError: assert True, "Advanced training features not ready yet" def test_learning_rate_scheduling(self): """Test learning rate scheduling during training.""" try: from tinytorch.core.training import LRScheduler, StepLR from tinytorch.core.optimizers import SGD from tinytorch.core.layers import Dense model = Dense(5, 1) optimizer = SGD(model.parameters(), lr=0.1) # Learning rate scheduler if 'StepLR' in locals(): scheduler = StepLR(optimizer, step_size=10, gamma=0.5) initial_lr = optimizer.lr # Step the scheduler for _ in range(15): if hasattr(scheduler, 'step'): scheduler.step() # Learning rate should have decreased if hasattr(optimizer, 'lr'): final_lr = optimizer.lr assert final_lr < initial_lr, "Learning rate scheduling not working" except ImportError: assert True, "Learning rate scheduling not ready yet" class TestProductionTrainingFeatures: """Test production-ready training features.""" def test_distributed_training_support(self): """Test distributed training capabilities.""" try: from tinytorch.core.training import DistributedTrainer from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD model = Dense(10, 3) optimizer = SGD(model.parameters(), lr=0.01) # Distributed trainer (if available) if 'DistributedTrainer' in locals(): dist_trainer = DistributedTrainer(model, optimizer, world_size=1, rank=0) assert hasattr(dist_trainer, 'train'), "Distributed training broken" except ImportError: assert True, "Distributed training not ready yet" def test_mixed_precision_training(self): """Test mixed precision training support.""" try: from tinytorch.core.training import Trainer from tinytorch.core.layers import Dense from tinytorch.core.optimizers import Adam model = Dense(20, 10) optimizer = Adam(model.parameters(), lr=0.001) # Mixed precision trainer trainer = Trainer(model, optimizer, mixed_precision=True) if hasattr(trainer, 'mixed_precision'): assert trainer.mixed_precision == True, "Mixed precision not enabled" except ImportError: assert True, "Mixed precision training not ready yet" def test_gradient_accumulation(self): """Test gradient accumulation for large effective batch sizes.""" try: from tinytorch.core.training import Trainer from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD model = Dense(10, 3) optimizer = SGD(model.parameters(), lr=0.01) # Trainer with gradient accumulation trainer = Trainer(model, optimizer, accumulate_grad_batches=4) if hasattr(trainer, 'accumulate_grad_batches'): assert trainer.accumulate_grad_batches == 4, "Gradient accumulation not set" except ImportError: assert True, "Gradient accumulation not ready yet" class TestRegressionPrevention: """Ensure previous modules still work after Module 11 development.""" def test_no_complete_pipeline_regression(self): """Verify complete ML pipeline (01→10) unchanged.""" # Core functionality should remain stable assert sys.version_info.major >= 3, "Foundation: Python detection broken" # Complete pipeline should still work try: from tinytorch.core.tensor import Tensor from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD from tinytorch.core.data import Dataset # All pipeline components should work layer = Dense(3, 2) optimizer = SGD(layer.parameters(), lr=0.01) x = Tensor(np.random.randn(1, 3)) output = layer(x) assert output.shape == (1, 2), "Pipeline regression: Forward pass broken" except ImportError: import numpy as np assert np.random is not None, "Pipeline regression: Basic functionality broken" def test_no_optimization_regression(self): """Verify optimization (10) and data loading (08) unchanged.""" try: from tinytorch.core.optimizers import SGD, Adam from tinytorch.core.data import Dataset, DataLoader # Optimizers should still work class DummyModule: def parameters(self): return [np.array([1.0, 2.0])] module = DummyModule() sgd = SGD(module.parameters(), lr=0.01) adam = Adam(module.parameters(), lr=0.001) assert hasattr(sgd, 'step'), "Optimization regression: SGD broken" assert hasattr(adam, 'step'), "Optimization regression: Adam broken" # Data loading should still work class TestDataset(Dataset): def __len__(self): return 5 def __getitem__(self, idx): return idx, idx * 2 dataset = TestDataset() dataloader = DataLoader(dataset, batch_size=2) assert len(dataset) == 5, "Data regression: Dataset broken" except ImportError: # Basic functionality should work import numpy as np assert np is not None, "Optimization/Data regression: Basic functionality broken" def test_progressive_stability(self): """Test the progressive stack is stable through training.""" # Stack should be stable through: Setup → ... → Optimizers → Training # Setup level import numpy as np assert np is not None, "Setup level broken" # Complete ML pipeline level (if available) try: from tinytorch.core.tensor import Tensor from tinytorch.core.layers import Dense from tinytorch.core.optimizers import SGD # Complete training components should work together model = Dense(5, 2) optimizer = SGD(model.parameters(), lr=0.01) x = Tensor(np.random.randn(3, 5)) output = model(x) assert output.shape == (3, 2), "ML pipeline level broken" except ImportError: pass # Not implemented yet # Training level (if available) try: from tinytorch.core.training import Trainer class DummyModel: def parameters(self): return [np.array([1.0])] class DummyOptimizer: def __init__(self, params, lr): self.lr = lr def step(self): pass def zero_grad(self): pass model = DummyModel() optimizer = DummyOptimizer(model.parameters(), 0.01) trainer = Trainer(model, optimizer) assert hasattr(trainer, 'train') or hasattr(trainer, 'fit'), "Training level broken" except ImportError: pass # Not implemented yet