#include "optical_model.hpp" #include "utils.hpp" #include #include #include #include // --- Kernel Declarations --- __global__ void k_modulate(const float* x, const float* A, const float* P, cufftComplex* field, int N_pixels); __global__ void k_intensity_log1p(const cufftComplex* freq, float* y, int N_elements); // NEW: Two-layer MLP kernels __global__ void k_linear_relu_forward(const float* input, const float* W, const float* b, float* output, int B, int input_size, int output_size); __global__ void k_linear_forward_mlp(const float* input, const float* W, const float* b, float* output, int B, int input_size, int output_size); __global__ void k_relu_backward(const float* grad_output, const float* forward_output, float* grad_input, int N); __global__ void k_linear_backward_input(const float* grad_output, const float* W, float* grad_input, int B, int input_size, int output_size); __global__ void k_accum_linear_grads(const float* input, const float* grad_output, float* gW, float* gb, int B, int input_size, int output_size); __global__ void k_softmax_xent_loss_grad(const float* logits, const uint8_t* labels, float* grad_logits, float* total_loss, int B, int C); __global__ void k_reduce_grad_map(const float* grad_y, int B, int S, float* grad_map); __global__ void k_sigmoid(const float* logits, float* probs, int N); // NEW: Multi-scale optical processing kernels __global__ void k_downsample_2x2(const float* input, float* output, int input_h, int input_w, int B); __global__ void k_concatenate_features(const float* scale1, const float* scale2, const float* scale3, float* multiscale, int B, int s1_size, int s2_size, int s3_size); // NEW: 6-scale mirror concatenation kernel __global__ void k_concatenate_6scale_mirror(const float* s1, const float* s2, const float* s3, const float* s1_mir, const float* s2_mir, const float* s3_mir, float* multiscale, int B, int s1_size, int s2_size, int s3_size); // NEW: Memory-efficient flip kernels for mirror architecture __global__ void k_flip_horizontal(const float* input, float* output, int height, int width, int B); __global__ void k_flip_vertical(const float* input, float* output, int height, int width, int B); // NEW: Diagnostic kernels for bottleneck analysis __global__ void k_analyze_activation_saturation(const float* activations, float* stats, int N); __global__ void k_analyze_gradient_flow(const float* gradients, float* stats, int N); __global__ void k_bottleneck_detector(const float* input_features, const float* hidden_act, const float* logits, float* bottleneck_metrics, int batch_size, int input_size, int hidden_size, int output_size); // BREAKTHROUGH: Rich FFT extraction preserving ALL complex information __global__ void k_intensity_magnitude_phase(const cufftComplex* freq, float* y, int N_elements); __global__ void k_rich_fft_extraction(const cufftComplex* freq, float* magnitude_out, float* phase_out, int N_elements); __global__ void k_concatenate_dual_channels(const float* magnitude_channel, const float* phase_channel, float* rich_features, int B, int channel_size); // --- Device Memory Management --- // C++ OPTIMIZATION: Allocate persistent GPU buffers once void allocate_device_buffers(DeviceBuffers& db, int B) { const size_t S = IMG_SIZE, H = HIDDEN_SIZE, C = NUM_CLASSES; const size_t MS = MULTISCALE_SIZE; // Enhanced 6-scale mirror feature size = 2058 // Batch-dependent buffers check_cuda(cudaMalloc(&db.d_batch_in, sizeof(float) * B * S), "alloc d_batch_in"); check_cuda(cudaMalloc(&db.d_batch_lbl, sizeof(uint8_t) * B), "alloc d_batch_lbl"); // Multi-scale optical processing buffers check_cuda(cudaMalloc(&db.d_field_scale1, sizeof(cufftComplex) * B * SCALE_1_SIZE), "alloc d_field_scale1"); check_cuda(cudaMalloc(&db.d_freq_scale1, sizeof(cufftComplex) * B * SCALE_1_SIZE), "alloc d_freq_scale1"); check_cuda(cudaMalloc(&db.d_features_scale1, sizeof(float) * B * SCALE_1_SIZE), "alloc d_features_scale1"); check_cuda(cudaMalloc(&db.d_field_scale2, sizeof(cufftComplex) * B * SCALE_2_SIZE), "alloc d_field_scale2"); check_cuda(cudaMalloc(&db.d_freq_scale2, sizeof(cufftComplex) * B * SCALE_2_SIZE), "alloc d_freq_scale2"); check_cuda(cudaMalloc(&db.d_features_scale2, sizeof(float) * B * SCALE_2_SIZE), "alloc d_features_scale2"); check_cuda(cudaMalloc(&db.d_field_scale3, sizeof(cufftComplex) * B * SCALE_3_SIZE), "alloc d_field_scale3"); check_cuda(cudaMalloc(&db.d_freq_scale3, sizeof(cufftComplex) * B * SCALE_3_SIZE), "alloc d_freq_scale3"); check_cuda(cudaMalloc(&db.d_features_scale3, sizeof(float) * B * SCALE_3_SIZE), "alloc d_features_scale3"); // Mirror architecture: allocate mirrored feature buffers check_cuda(cudaMalloc(&db.d_features_scale1_mirror, sizeof(float) * B * SCALE_1_SIZE), "alloc d_features_scale1_mirror"); check_cuda(cudaMalloc(&db.d_features_scale2_mirror, sizeof(float) * B * SCALE_2_SIZE), "alloc d_features_scale2_mirror"); check_cuda(cudaMalloc(&db.d_features_scale3_mirror, sizeof(float) * B * SCALE_3_SIZE), "alloc d_features_scale3_mirror"); // LEGACY: Rich dual-channel processing buffers (not used in intelligent solution) // check_cuda(cudaMalloc(&db.d_magnitude_features, sizeof(float) * B * MS), "alloc d_magnitude_features"); // check_cuda(cudaMalloc(&db.d_phase_features, sizeof(float) * B * MS), "alloc d_phase_features"); check_cuda(cudaMalloc(&db.d_multiscale_features, sizeof(float) * B * MS), "alloc d_multiscale_features"); check_cuda(cudaMalloc(&db.d_hidden, sizeof(float) * B * H), "alloc d_hidden"); check_cuda(cudaMalloc(&db.d_logits, sizeof(float) * B * C), "alloc d_logits"); check_cuda(cudaMalloc(&db.d_probs, sizeof(float) * B * C), "alloc d_probs"); check_cuda(cudaMalloc(&db.d_grad_logits, sizeof(float) * B * C), "alloc d_grad_logits"); check_cuda(cudaMalloc(&db.d_grad_hidden, sizeof(float) * B * H), "alloc d_grad_hidden"); check_cuda(cudaMalloc(&db.d_grad_multiscale, sizeof(float) * B * MS), "alloc d_grad_multiscale"); // Fungi buffers check_cuda(cudaMalloc(&db.d_A, sizeof(float) * S), "alloc d_A"); check_cuda(cudaMalloc(&db.d_P, sizeof(float) * S), "alloc d_P"); check_cuda(cudaMalloc(&db.d_grad_map, sizeof(float) * S), "alloc d_grad_map"); // C++ OPTIMIZATION: Persistent weight buffers (allocated once, updated in-place) check_cuda(cudaMalloc(&db.d_W1, sizeof(float) * H * MS), "alloc persistent d_W1"); check_cuda(cudaMalloc(&db.d_b1, sizeof(float) * H), "alloc persistent d_b1"); check_cuda(cudaMalloc(&db.d_W2, sizeof(float) * C * H), "alloc persistent d_W2"); check_cuda(cudaMalloc(&db.d_b2, sizeof(float) * C), "alloc persistent d_b2"); check_cuda(cudaMalloc(&db.d_gW1, sizeof(float) * H * MS), "alloc persistent d_gW1"); check_cuda(cudaMalloc(&db.d_gb1, sizeof(float) * H), "alloc persistent d_gb1"); check_cuda(cudaMalloc(&db.d_gW2, sizeof(float) * C * H), "alloc persistent d_gW2"); check_cuda(cudaMalloc(&db.d_gb2, sizeof(float) * C), "alloc persistent d_gb2"); check_cuda(cudaMalloc(&db.d_loss_scalar, sizeof(float)), "alloc persistent d_loss_scalar"); // CRITICAL: Bottleneck detection buffer - [4] metrics array check_cuda(cudaMalloc(&db.d_bottleneck_metrics, sizeof(float) * 4), "alloc bottleneck_metrics"); } void free_device_buffers(DeviceBuffers& db) { // Free batch-dependent buffers if (db.d_batch_in) cudaFree(db.d_batch_in); if (db.d_batch_lbl) cudaFree(db.d_batch_lbl); // Free multi-scale optical processing buffers if (db.d_field_scale1) cudaFree(db.d_field_scale1); if (db.d_freq_scale1) cudaFree(db.d_freq_scale1); if (db.d_features_scale1) cudaFree(db.d_features_scale1); if (db.d_field_scale2) cudaFree(db.d_field_scale2); if (db.d_freq_scale2) cudaFree(db.d_freq_scale2); if (db.d_features_scale2) cudaFree(db.d_features_scale2); if (db.d_field_scale3) cudaFree(db.d_field_scale3); if (db.d_freq_scale3) cudaFree(db.d_freq_scale3); if (db.d_features_scale3) cudaFree(db.d_features_scale3); // Free mirror architecture buffers if (db.d_features_scale1_mirror) cudaFree(db.d_features_scale1_mirror); if (db.d_features_scale2_mirror) cudaFree(db.d_features_scale2_mirror); if (db.d_features_scale3_mirror) cudaFree(db.d_features_scale3_mirror); if (db.d_multiscale_features) cudaFree(db.d_multiscale_features); if (db.d_hidden) cudaFree(db.d_hidden); if (db.d_logits) cudaFree(db.d_logits); if (db.d_probs) cudaFree(db.d_probs); if (db.d_grad_logits) cudaFree(db.d_grad_logits); if (db.d_grad_hidden) cudaFree(db.d_grad_hidden); if (db.d_grad_multiscale) cudaFree(db.d_grad_multiscale); // Free fungi buffers if (db.d_A) cudaFree(db.d_A); if (db.d_P) cudaFree(db.d_P); if (db.d_grad_map) cudaFree(db.d_grad_map); // Free persistent weight buffers if (db.d_W1) cudaFree(db.d_W1); if (db.d_b1) cudaFree(db.d_b1); if (db.d_W2) cudaFree(db.d_W2); if (db.d_b2) cudaFree(db.d_b2); if (db.d_gW1) cudaFree(db.d_gW1); if (db.d_gb1) cudaFree(db.d_gb1); if (db.d_gW2) cudaFree(db.d_gW2); if (db.d_gb2) cudaFree(db.d_gb2); if (db.d_loss_scalar) cudaFree(db.d_loss_scalar); // CRITICAL: Free bottleneck detection buffer if (db.d_bottleneck_metrics) cudaFree(db.d_bottleneck_metrics); // LEGACY: Free rich dual-channel buffers (not used in intelligent solution) // if (db.d_magnitude_features) cudaFree(db.d_magnitude_features); // if (db.d_phase_features) cudaFree(db.d_phase_features); } // --- Adam Updater (Host) --- static void adam_update(std::vector& P, std::vector& m, std::vector& v, const float* g_dev, size_t n, float lr, float wd, int t) { std::vector g(n); check_cuda(cudaMemcpy(g.data(), g_dev, sizeof(float) * n, cudaMemcpyDeviceToHost), "D2H grads for Adam"); float b1 = 0.9f, b2 = 0.999f, eps = 1e-8f; float b1t = 1.f - std::pow(b1, (float)t); float b2t = 1.f - std::pow(b2, (float)t); for (size_t i = 0; i < n; ++i) { m[i] = b1 * m[i] + (1 - b1) * g[i]; v[i] = b2 * v[i] + (1 - b2) * g[i] * g[i]; float mh = m[i] / b1t; float vh = v[i] / b2t; P[i] -= lr * (mh / (std::sqrt(vh) + eps) + wd * P[i]); } } // C++ OPTIMIZATION: Efficient GPU weight management void upload_params_to_gpu(const OpticalParams& params, DeviceBuffers& db) { const size_t MS = MULTISCALE_SIZE, H = HIDDEN_SIZE, C = NUM_CLASSES; // Upload weights to persistent GPU buffers check_cuda(cudaMemcpy(db.d_W1, params.W1.data(), sizeof(float) * H * MS, cudaMemcpyHostToDevice), "upload W1"); check_cuda(cudaMemcpy(db.d_b1, params.b1.data(), sizeof(float) * H, cudaMemcpyHostToDevice), "upload b1"); check_cuda(cudaMemcpy(db.d_W2, params.W2.data(), sizeof(float) * C * H, cudaMemcpyHostToDevice), "upload W2"); check_cuda(cudaMemcpy(db.d_b2, params.b2.data(), sizeof(float) * C, cudaMemcpyHostToDevice), "upload b2"); } void download_params_from_gpu(OpticalParams& params, const DeviceBuffers& db) { const size_t MS = MULTISCALE_SIZE, H = HIDDEN_SIZE, C = NUM_CLASSES; // Download updated weights from GPU check_cuda(cudaMemcpy(params.W1.data(), db.d_W1, sizeof(float) * H * MS, cudaMemcpyDeviceToHost), "download W1"); check_cuda(cudaMemcpy(params.b1.data(), db.d_b1, sizeof(float) * H, cudaMemcpyDeviceToHost), "download b1"); check_cuda(cudaMemcpy(params.W2.data(), db.d_W2, sizeof(float) * C * H, cudaMemcpyDeviceToHost), "download W2"); check_cuda(cudaMemcpy(params.b2.data(), db.d_b2, sizeof(float) * C, cudaMemcpyDeviceToHost), "download b2"); } // --- Training Step for Multi-Scale Two-Layer MLP --- // CHANGE LOG: Multi-scale optical processing for 90%+ accuracy // FORWARD: multi_scale_features -> W1*features+b1 -> ReLU -> W2*hidden+b2 -> logits // BACKWARD: Full backpropagation through both layers with multi-scale features float train_batch(const float* h_batch_in, const uint8_t* h_batch_lbl, int B, FungiSoA& fungi, OpticalParams& params, DeviceBuffers& db, FFTPlan& fft, float lr, float wd, int t_adam) { const int S = IMG_SIZE, H = HIDDEN_SIZE, C = NUM_CLASSES, MS = MULTISCALE_SIZE; check_cuda(cudaMemcpy(db.d_batch_in, h_batch_in, sizeof(float) * B * S, cudaMemcpyHostToDevice), "H2D input"); check_cuda(cudaMemcpy(db.d_batch_lbl, h_batch_lbl, sizeof(uint8_t) * B, cudaMemcpyHostToDevice), "H2D labels"); // Multi-scale optical processing for 90%+ accuracy fungi_build_masks_GPU(fungi, db.d_A, db.d_P); // DIAGNOSTIC: Analyze mask statistics every few epochs static int debug_counter = 0; if (debug_counter % 10 == 0) { // Every 10 batches fungi_analyze_mask_statistics(db.d_A, db.d_P, IMG_SIZE); } debug_counter++; // Scale 1: Full resolution 28x28 = 784 features k_modulate<<<(B * S + 255) / 256, 256>>>(db.d_batch_in, db.d_A, db.d_P, db.d_field_scale1, B * S); cufftExecC2C(fft.plan_fwd_scale1, db.d_field_scale1, db.d_freq_scale1, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_1_SIZE + 255) / 256, 256>>>(db.d_freq_scale1, db.d_features_scale1, B * SCALE_1_SIZE); // Scale 2: Half resolution 14x14 = 196 features k_downsample_2x2<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_batch_in, reinterpret_cast(db.d_field_scale2), IMG_H, IMG_W, B); k_modulate<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(reinterpret_cast(db.d_field_scale2), db.d_A, db.d_P, db.d_field_scale2, B * SCALE_2_SIZE); cufftExecC2C(fft.plan_fwd_scale2, db.d_field_scale2, db.d_freq_scale2, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_freq_scale2, db.d_features_scale2, B * SCALE_2_SIZE); // Scale 3: Quarter resolution 7x7 = 49 features (downsample from scale2) k_downsample_2x2<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_features_scale2, reinterpret_cast(db.d_field_scale3), SCALE_2, SCALE_2, B); k_modulate<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(reinterpret_cast(db.d_field_scale3), db.d_A, db.d_P, db.d_field_scale3, B * SCALE_3_SIZE); cufftExecC2C(fft.plan_fwd_scale3, db.d_field_scale3, db.d_freq_scale3, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_freq_scale3, db.d_features_scale3, B * SCALE_3_SIZE); // Mirror processing: create horizontally flipped versions for enhanced features k_flip_horizontal<<<(B * SCALE_1_SIZE + 255) / 256, 256>>>(db.d_features_scale1, db.d_features_scale1_mirror, SCALE_1, SCALE_1, B); k_flip_horizontal<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_features_scale2, db.d_features_scale2_mirror, SCALE_2, SCALE_2, B); k_flip_horizontal<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_features_scale3, db.d_features_scale3_mirror, SCALE_3, SCALE_3, B); // INTELLIGENT SOLUTION: Enhanced 6-scale mirror with optimized FFT kernel k_concatenate_6scale_mirror<<<(B * MS + 255) / 256, 256>>>( db.d_features_scale1, db.d_features_scale2, db.d_features_scale3, db.d_features_scale1_mirror, db.d_features_scale2_mirror, db.d_features_scale3_mirror, db.d_multiscale_features, B, SCALE_1_SIZE, SCALE_2_SIZE, SCALE_3_SIZE); // C++ OPTIMIZATION: Use persistent GPU buffers (NO malloc/free per batch!) // Forward pass: Layer 1 (with ReLU) - Enhanced 2058 features with better FFT extraction k_linear_relu_forward<<<(B*H+255)/256, 256>>>(db.d_multiscale_features, db.d_W1, db.d_b1, db.d_hidden, B, MS, H); // Forward pass: Layer 2 (linear) k_linear_forward_mlp<<<(B*C+255)/256, 256>>>(db.d_hidden, db.d_W2, db.d_b2, db.d_logits, B, H, C); // CRITICAL: Real-time bottleneck detection - analyze information flow static int bottleneck_counter = 0; if (bottleneck_counter % 5 == 0) { // Every 5 batches for performance cudaMemset(db.d_bottleneck_metrics, 0, sizeof(float) * 4); int max_threads = fmaxf(fmaxf(MS, H), C); k_bottleneck_detector<<<(max_threads + 255) / 256, 256>>>( db.d_multiscale_features, db.d_hidden, db.d_logits, db.d_bottleneck_metrics, B, MS, H, C); // Download metrics and report critical bottlenecks float h_metrics[4] = {0}; cudaMemcpy(h_metrics, db.d_bottleneck_metrics, sizeof(float) * 4, cudaMemcpyDeviceToHost); float dead_features_pct = (h_metrics[0] / MS) * 100.0f; // % dead input features float dead_neurons_pct = (h_metrics[1] / H) * 100.0f; // % dead hidden neurons float saturated_neurons_pct = (h_metrics[2] / H) * 100.0f; // % saturated hidden neurons float poor_discrimination_pct = (h_metrics[3] / C) * 100.0f; // % poor output discrimination // ALERT: Critical bottleneck detection if (dead_features_pct > 20.0f || dead_neurons_pct > 30.0f || saturated_neurons_pct > 30.0f || poor_discrimination_pct > 40.0f) { printf("🚨 CRITICAL BOTTLENECK DETECTED:\n"); printf(" 📉 Dead Features: %.1f%% | Dead Neurons: %.1f%% | Saturated: %.1f%% | Poor Discrim: %.1f%%\n", dead_features_pct, dead_neurons_pct, saturated_neurons_pct, poor_discrimination_pct); } } bottleneck_counter++; // Loss computation (using persistent buffer) cudaMemset(db.d_loss_scalar, 0, sizeof(float)); k_softmax_xent_loss_grad<<<(B + 255) / 256, 256>>>(db.d_logits, (const uint8_t*)db.d_batch_lbl, db.d_grad_logits, db.d_loss_scalar, B, C); // Backward pass: Layer 2 gradients (using persistent buffers) cudaMemset(db.d_gW2, 0, sizeof(float)*C*H); cudaMemset(db.d_gb2, 0, sizeof(float)*C); k_accum_linear_grads<<<(C+255)/256, 256>>>(db.d_hidden, db.d_grad_logits, db.d_gW2, db.d_gb2, B, H, C); // Backward pass: Hidden layer gradient k_linear_backward_input<<<(B*H+255)/256, 256>>>(db.d_grad_logits, db.d_W2, db.d_grad_hidden, B, H, C); // Backward pass: ReLU gradient k_relu_backward<<<(B*H+255)/256, 256>>>(db.d_grad_hidden, db.d_hidden, db.d_grad_hidden, B*H); // Backward pass: Layer 1 gradients (using persistent buffers with multi-scale) cudaMemset(db.d_gW1, 0, sizeof(float)*H*MS); cudaMemset(db.d_gb1, 0, sizeof(float)*H); k_accum_linear_grads<<<(H+255)/256, 256>>>(db.d_multiscale_features, db.d_grad_hidden, db.d_gW1, db.d_gb1, B, MS, H); // Backward pass: Multi-scale gradient for fungi (simplified - use scale 1 gradient) k_linear_backward_input<<<(B*MS+255)/256, 256>>>(db.d_grad_hidden, db.d_W1, db.d_grad_multiscale, B, MS, H); k_reduce_grad_map<<<(S + 255) / 256, 256>>>(db.d_grad_multiscale, B, S, db.d_grad_map); // Only use first S elements // Adam updates for all parameters (using persistent buffers with multi-scale) adam_update(params.W1, params.m_W1, params.v_W1, db.d_gW1, H * MS, lr, wd, t_adam); adam_update(params.b1, params.m_b1, params.v_b1, db.d_gb1, H, lr, 0.0f, t_adam); adam_update(params.W2, params.m_W2, params.v_W2, db.d_gW2, C * H, lr, wd, t_adam); adam_update(params.b2, params.m_b2, params.v_b2, db.d_gb2, C, lr, 0.0f, t_adam); // BUGFIX: Upload updated weights back to GPU after Adam upload_params_to_gpu(params, db); float h_loss; check_cuda(cudaMemcpy(&h_loss, db.d_loss_scalar, sizeof(float), cudaMemcpyDeviceToHost), "D2H loss"); return h_loss / B; } // --- Inference for Multi-Scale Two-Layer MLP --- void infer_batch(const float* h_batch_in, int B, const FungiSoA& fungi, const OpticalParams& params, DeviceBuffers& db, FFTPlan& fft, std::vector& out_predictions) { const int S = IMG_SIZE, H = HIDDEN_SIZE, C = NUM_CLASSES, MS = MULTISCALE_SIZE; check_cuda(cudaMemcpy(db.d_batch_in, h_batch_in, sizeof(float) * B * S, cudaMemcpyHostToDevice), "H2D infer input"); // Multi-scale optical processing for inference fungi_build_masks_GPU(const_cast(fungi), db.d_A, db.d_P); // Scale 1: Full resolution 28x28 = 784 features k_modulate<<<(B * S + 255) / 256, 256>>>(db.d_batch_in, db.d_A, db.d_P, db.d_field_scale1, B * S); cufftExecC2C(fft.plan_fwd_scale1, db.d_field_scale1, db.d_freq_scale1, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_1_SIZE + 255) / 256, 256>>>(db.d_freq_scale1, db.d_features_scale1, B * SCALE_1_SIZE); // Scale 2: Half resolution 14x14 = 196 features k_downsample_2x2<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_batch_in, reinterpret_cast(db.d_field_scale2), IMG_H, IMG_W, B); k_modulate<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(reinterpret_cast(db.d_field_scale2), db.d_A, db.d_P, db.d_field_scale2, B * SCALE_2_SIZE); cufftExecC2C(fft.plan_fwd_scale2, db.d_field_scale2, db.d_freq_scale2, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_freq_scale2, db.d_features_scale2, B * SCALE_2_SIZE); // Scale 3: Quarter resolution 7x7 = 49 features (downsample from scale2) k_downsample_2x2<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_features_scale2, reinterpret_cast(db.d_field_scale3), SCALE_2, SCALE_2, B); k_modulate<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(reinterpret_cast(db.d_field_scale3), db.d_A, db.d_P, db.d_field_scale3, B * SCALE_3_SIZE); cufftExecC2C(fft.plan_fwd_scale3, db.d_field_scale3, db.d_freq_scale3, CUFFT_FORWARD); k_intensity_magnitude_phase<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_freq_scale3, db.d_features_scale3, B * SCALE_3_SIZE); // Mirror processing: create horizontally flipped versions for enhanced features k_flip_horizontal<<<(B * SCALE_1_SIZE + 255) / 256, 256>>>(db.d_features_scale1, db.d_features_scale1_mirror, SCALE_1, SCALE_1, B); k_flip_horizontal<<<(B * SCALE_2_SIZE + 255) / 256, 256>>>(db.d_features_scale2, db.d_features_scale2_mirror, SCALE_2, SCALE_2, B); k_flip_horizontal<<<(B * SCALE_3_SIZE + 255) / 256, 256>>>(db.d_features_scale3, db.d_features_scale3_mirror, SCALE_3, SCALE_3, B); // INTELLIGENT SOLUTION: Enhanced 6-scale mirror with optimized FFT kernel (INFERENCE) k_concatenate_6scale_mirror<<<(B * MS + 255) / 256, 256>>>( db.d_features_scale1, db.d_features_scale2, db.d_features_scale3, db.d_features_scale1_mirror, db.d_features_scale2_mirror, db.d_features_scale3_mirror, db.d_multiscale_features, B, SCALE_1_SIZE, SCALE_2_SIZE, SCALE_3_SIZE); // C++ OPTIMIZATION: Use persistent GPU buffers for inference // Forward pass: Layer 1 (with ReLU) - Enhanced 2058 features with better FFT extraction k_linear_relu_forward<<<(B*H+255)/256, 256>>>(db.d_multiscale_features, db.d_W1, db.d_b1, db.d_hidden, B, MS, H); // Forward pass: Layer 2 (linear) k_linear_forward_mlp<<<(B*C+255)/256, 256>>>(db.d_hidden, db.d_W2, db.d_b2, db.d_logits, B, H, C); std::vector h_logits(B * C); check_cuda(cudaMemcpy(h_logits.data(), db.d_logits, sizeof(float) * B * C, cudaMemcpyDeviceToHost), "D2H logits"); out_predictions.resize(B); for (int b = 0; b < B; ++b) { int best_class = 0; float max_logit = h_logits[b * C]; for (int c = 1; c < C; ++c) { if (h_logits[b * C + c] > max_logit) { max_logit = h_logits[b * C + c]; best_class = c; } } out_predictions[b] = best_class; } } // --- Kernels --- __global__ void k_modulate(const float* x, const float* A, const float* P, cufftComplex* field, int N_pixels) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N_pixels) return; int pixel_idx = i % IMG_SIZE; float input_val = x[i]; float amp = A[pixel_idx]; float phase = P[pixel_idx]; field[i].x = input_val * amp * cosf(phase); field[i].y = input_val * amp * sinf(phase); } __global__ void k_intensity_log1p(const cufftComplex* freq, float* y, int N_elements) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N_elements) return; float intensity = freq[i].x * freq[i].x + freq[i].y * freq[i].y; y[i] = log1pf(intensity); } // BOTTLENECK FIX: Enhanced extraction with magnitude and phase information __global__ void k_intensity_magnitude_phase(const cufftComplex* freq, float* y, int N_elements) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N_elements) return; float real = freq[i].x; float imag = freq[i].y; // Preserve both magnitude and phase information float magnitude = sqrtf(real * real + imag * imag); float phase = atan2f(imag, real); // BREAKTHROUGH FIX: Instead of crushing to 1D, preserve rich information // Method 1: Enhanced representation with multiple components y[i] = log1pf(magnitude) + 0.5f * tanhf(phase) + 0.2f * (real / (fabsf(real) + 1e-6f)) + 0.1f * (imag / (fabsf(imag) + 1e-6f)); } // REVOLUTIONARY: Rich FFT extraction - DOUBLES information capacity __global__ void k_rich_fft_extraction(const cufftComplex* freq, float* magnitude_out, float* phase_out, int N_elements) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N_elements) return; float real = freq[i].x; float imag = freq[i].y; // Preserve magnitude with enhanced dynamic range float magnitude = sqrtf(real * real + imag * imag); magnitude_out[i] = log1pf(magnitude) + 0.1f * atan2f(magnitude, 1.0f); // Enhanced magnitude // Preserve phase with full resolution float phase = atan2f(imag, real); phase_out[i] = tanhf(2.0f * phase / 3.14159f); // Full phase preservation [-1,1] } // BREAKTHROUGH: Concatenate magnitude and phase channels into rich feature vector __global__ void k_concatenate_dual_channels(const float* magnitude_channel, const float* phase_channel, float* rich_features, int B, int channel_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int total_size = 2 * channel_size; // magnitude + phase = double size if (idx >= B * total_size) return; int batch_idx = idx / total_size; int feature_idx = idx % total_size; if (feature_idx < channel_size) { // First half: magnitude channel [0, channel_size) rich_features[idx] = magnitude_channel[batch_idx * channel_size + feature_idx]; } else { // Second half: phase channel [channel_size, 2*channel_size) int phase_idx = feature_idx - channel_size; rich_features[idx] = phase_channel[batch_idx * channel_size + phase_idx]; } } __global__ void k_linear_forward(const float* y, const float* W, const float* b, float* logits, int B, int S, int C) { int batch_class = blockIdx.x * blockDim.x + threadIdx.x; if (batch_class >= B * C) return; int batch_idx = batch_class / C; int class_idx = batch_class % C; float sum = b[class_idx]; for (int s = 0; s < S; ++s) { sum += W[class_idx * S + s] * y[batch_idx * S + s]; } logits[batch_class] = sum; } __global__ void k_softmax_xent_loss_grad(const float* logits, const uint8_t* labels, float* grad_logits, float* total_loss, int B, int C) { int b = blockIdx.x * blockDim.x + threadIdx.x; if (b >= B) return; const float* b_logits = logits + b * C; float max_val = -1e20f; for (int c = 0; c < C; ++c) { if (b_logits[c] > max_val) max_val = b_logits[c]; } float exp_sum = 0.f; float exp_vals[10]; for (int c = 0; c < C; ++c) { exp_vals[c] = expf(b_logits[c] - max_val); exp_sum += exp_vals[c]; } uint8_t true_label = labels[b]; float* b_grad = grad_logits + b * C; float loss = 0.f; for (int c = 0; c < C; ++c) { float prob = exp_vals[c] / exp_sum; b_grad[c] = prob - (c == true_label ? 1.f : 0.f); if (c == true_label) { loss = -logf(fmaxf(prob, 1e-9f)); } } atomicAdd(total_loss, loss); } __global__ void k_backprop_y(const float* grad_logits, const float* W, float* grad_y, int B, int S, int C) { int batch_pixel = blockIdx.x * blockDim.x + threadIdx.x; if (batch_pixel >= B * S) return; int batch_idx = batch_pixel / S; int pixel_idx = batch_pixel % S; float sum = 0.f; for (int c = 0; c < C; ++c) { sum += grad_logits[batch_idx * C + c] * W[c * S + pixel_idx]; } grad_y[batch_pixel] = sum; } __global__ void k_accum_grads_Wb(const float* y, const float* grad_logits, float* gW, float* gb, int B, int S, int C) { int class_idx = blockIdx.x * blockDim.x + threadIdx.x; if (class_idx >= C) return; float gb_sum = 0.f; for (int b = 0; b < B; ++b) { gb_sum += grad_logits[b * C + class_idx]; } gb[class_idx] = gb_sum; for (int s = 0; s < S; ++s) { float gw_sum = 0.f; for (int b = 0; b < B; ++b) { gw_sum += grad_logits[b * C + class_idx] * y[b * S + s]; } gW[class_idx * S + s] = gw_sum; } } __global__ void k_reduce_grad_map(const float* grad_y, int B, int S, float* grad_map) { int pixel = blockIdx.x * blockDim.x + threadIdx.x; if (pixel >= S) return; float acc = 0.f; for (int b = 0; b < B; ++b) { acc += fabsf(grad_y[b * S + pixel]); } grad_map[pixel] = acc / static_cast(B); } __global__ void k_sigmoid(const float* logits, float* probs, int N) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N) return; probs[i] = 1.f / (1.f + expf(-logits[i])); } void create_fft_plan(FFTPlan& fft, int batch) { // Scale 1: 28x28 FFT plans int n1[2] = {SCALE_1, SCALE_1}; cufftPlanMany(&fft.plan_fwd_scale1, 2, n1, nullptr, 1, SCALE_1_SIZE, nullptr, 1, SCALE_1_SIZE, CUFFT_C2C, batch); cufftPlanMany(&fft.plan_inv_scale1, 2, n1, nullptr, 1, SCALE_1_SIZE, nullptr, 1, SCALE_1_SIZE, CUFFT_C2C, batch); // Scale 2: 14x14 FFT plans int n2[2] = {SCALE_2, SCALE_2}; cufftPlanMany(&fft.plan_fwd_scale2, 2, n2, nullptr, 1, SCALE_2_SIZE, nullptr, 1, SCALE_2_SIZE, CUFFT_C2C, batch); cufftPlanMany(&fft.plan_inv_scale2, 2, n2, nullptr, 1, SCALE_2_SIZE, nullptr, 1, SCALE_2_SIZE, CUFFT_C2C, batch); // Scale 3: 7x7 FFT plans int n3[2] = {SCALE_3, SCALE_3}; cufftPlanMany(&fft.plan_fwd_scale3, 2, n3, nullptr, 1, SCALE_3_SIZE, nullptr, 1, SCALE_3_SIZE, CUFFT_C2C, batch); cufftPlanMany(&fft.plan_inv_scale3, 2, n3, nullptr, 1, SCALE_3_SIZE, nullptr, 1, SCALE_3_SIZE, CUFFT_C2C, batch); } void destroy_fft_plan(FFTPlan& fft) { if (fft.plan_fwd_scale1) cufftDestroy(fft.plan_fwd_scale1); if (fft.plan_inv_scale1) cufftDestroy(fft.plan_inv_scale1); if (fft.plan_fwd_scale2) cufftDestroy(fft.plan_fwd_scale2); if (fft.plan_inv_scale2) cufftDestroy(fft.plan_inv_scale2); if (fft.plan_fwd_scale3) cufftDestroy(fft.plan_fwd_scale3); if (fft.plan_inv_scale3) cufftDestroy(fft.plan_inv_scale3); } // CHANGE LOG: Updated initialization for 6-scale mirror two-layer MLP // ORIGINAL: Single layer initialization (IMG_SIZE=784) // NEW: Xavier/Glorot initialization for both layers (MULTISCALE_SIZE=2058) void init_params(OpticalParams& p, unsigned seed) { std::mt19937 gen(seed); // Xavier initialization: std = sqrt(2 / (fan_in + fan_out)) float std_W1 = std::sqrt(2.0f / (MULTISCALE_SIZE + HIDDEN_SIZE)); float std_W2 = std::sqrt(2.0f / (HIDDEN_SIZE + NUM_CLASSES)); std::normal_distribution dist_W1(0.f, std_W1); std::normal_distribution dist_W2(0.f, std_W2); // First layer: MULTISCALE_SIZE -> HIDDEN_SIZE size_t W1_size = HIDDEN_SIZE * MULTISCALE_SIZE; p.W1.resize(W1_size); p.b1.resize(HIDDEN_SIZE); p.m_W1.resize(W1_size); p.v_W1.resize(W1_size); p.m_b1.resize(HIDDEN_SIZE); p.v_b1.resize(HIDDEN_SIZE); for (size_t i = 0; i < W1_size; ++i) { p.W1[i] = dist_W1(gen); p.m_W1[i] = 0.f; p.v_W1[i] = 0.f; } for (size_t i = 0; i < HIDDEN_SIZE; ++i) { p.b1[i] = 0.f; p.m_b1[i] = 0.f; p.v_b1[i] = 0.f; } // Second layer: HIDDEN_SIZE -> NUM_CLASSES size_t W2_size = NUM_CLASSES * HIDDEN_SIZE; p.W2.resize(W2_size); p.b2.resize(NUM_CLASSES); p.m_W2.resize(W2_size); p.v_W2.resize(W2_size); p.m_b2.resize(NUM_CLASSES); p.v_b2.resize(NUM_CLASSES); for (size_t i = 0; i < W2_size; ++i) { p.W2[i] = dist_W2(gen); p.m_W2[i] = 0.f; p.v_W2[i] = 0.f; } for (size_t i = 0; i < NUM_CLASSES; ++i) { p.b2[i] = 0.f; p.m_b2[i] = 0.f; p.v_b2[i] = 0.f; } } // --- NEW KERNELS FOR TWO-LAYER MLP --- // Linear layer with ReLU activation: output = ReLU(W * input + b) __global__ void k_linear_relu_forward(const float* input, const float* W, const float* b, float* output, int B, int input_size, int output_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= B * output_size) return; int batch_idx = idx / output_size; int out_idx = idx % output_size; float sum = b[out_idx]; for (int i = 0; i < input_size; ++i) { sum += W[out_idx * input_size + i] * input[batch_idx * input_size + i]; } output[idx] = fmaxf(0.0f, sum); // ReLU activation } // Linear layer without activation: output = W * input + b __global__ void k_linear_forward_mlp(const float* input, const float* W, const float* b, float* output, int B, int input_size, int output_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= B * output_size) return; int batch_idx = idx / output_size; int out_idx = idx % output_size; float sum = b[out_idx]; for (int i = 0; i < input_size; ++i) { sum += W[out_idx * input_size + i] * input[batch_idx * input_size + i]; } output[idx] = sum; } // ReLU backward: grad_input = grad_output * (forward_output > 0) __global__ void k_relu_backward(const float* grad_output, const float* forward_output, float* grad_input, int N) { int i = blockIdx.x * blockDim.x + threadIdx.x; if (i >= N) return; grad_input[i] = grad_output[i] * (forward_output[i] > 0.0f ? 1.0f : 0.0f); } // Linear backward (input gradients): grad_input = W^T * grad_output __global__ void k_linear_backward_input(const float* grad_output, const float* W, float* grad_input, int B, int input_size, int output_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= B * input_size) return; int batch_idx = idx / input_size; int in_idx = idx % input_size; float sum = 0.0f; for (int o = 0; o < output_size; ++o) { sum += W[o * input_size + in_idx] * grad_output[batch_idx * output_size + o]; } grad_input[idx] = sum; } // Accumulate gradients for linear layer weights and biases __global__ void k_accum_linear_grads(const float* input, const float* grad_output, float* gW, float* gb, int B, int input_size, int output_size) { int out_idx = blockIdx.x * blockDim.x + threadIdx.x; if (out_idx >= output_size) return; // Accumulate bias gradient float gb_sum = 0.0f; for (int b = 0; b < B; ++b) { gb_sum += grad_output[b * output_size + out_idx]; } gb[out_idx] = gb_sum; // Accumulate weight gradients for (int in_idx = 0; in_idx < input_size; ++in_idx) { float gw_sum = 0.0f; for (int b = 0; b < B; ++b) { gw_sum += grad_output[b * output_size + out_idx] * input[b * input_size + in_idx]; } gW[out_idx * input_size + in_idx] = gw_sum; } } // --- NEW KERNELS FOR MULTI-SCALE OPTICAL PROCESSING --- // Downsample 28x28 to 14x14 using 2x2 average pooling __global__ void k_downsample_2x2(const float* input, float* output, int input_h, int input_w, int B) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int output_h = input_h / 2; int output_w = input_w / 2; int output_size = output_h * output_w; if (idx >= B * output_size) return; int batch_idx = idx / output_size; int out_pixel = idx % output_size; int out_y = out_pixel / output_w; int out_x = out_pixel % output_w; // Average 2x2 region float sum = 0.0f; for (int dy = 0; dy < 2; ++dy) { for (int dx = 0; dx < 2; ++dx) { int in_y = out_y * 2 + dy; int in_x = out_x * 2 + dx; int in_pixel = in_y * input_w + in_x; sum += input[batch_idx * (input_h * input_w) + in_pixel]; } } output[idx] = sum * 0.25f; // Average of 4 pixels } // Concatenate multi-scale features: [scale1 | scale2 | scale3] __global__ void k_concatenate_features(const float* scale1, const float* scale2, const float* scale3, float* multiscale, int B, int s1_size, int s2_size, int s3_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int total_size = s1_size + s2_size + s3_size; if (idx >= B * total_size) return; int batch_idx = idx / total_size; int feature_idx = idx % total_size; if (feature_idx < s1_size) { // Copy from scale1 multiscale[idx] = scale1[batch_idx * s1_size + feature_idx]; } else if (feature_idx < s1_size + s2_size) { // Copy from scale2 int s2_idx = feature_idx - s1_size; multiscale[idx] = scale2[batch_idx * s2_size + s2_idx]; } else { // Copy from scale3 int s3_idx = feature_idx - s1_size - s2_size; multiscale[idx] = scale3[batch_idx * s3_size + s3_idx]; } } // Memory-efficient horizontal flip: flip left-right in place __global__ void k_flip_horizontal(const float* input, float* output, int height, int width, int B) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int total_pixels = B * height * width; if (idx >= total_pixels) return; int batch_idx = idx / (height * width); int pixel_idx = idx % (height * width); int row = pixel_idx / width; int col = pixel_idx % width; // Flip column: new_col = width - 1 - col int flipped_col = width - 1 - col; int flipped_idx = batch_idx * (height * width) + row * width + flipped_col; output[idx] = input[flipped_idx]; } // Memory-efficient vertical flip: flip top-bottom in place __global__ void k_flip_vertical(const float* input, float* output, int height, int width, int B) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int total_pixels = B * height * width; if (idx >= total_pixels) return; int batch_idx = idx / (height * width); int pixel_idx = idx % (height * width); int row = pixel_idx / width; int col = pixel_idx % width; // Flip row: new_row = height - 1 - row int flipped_row = height - 1 - row; int flipped_idx = batch_idx * (height * width) + flipped_row * width + col; output[idx] = input[flipped_idx]; } // 6-scale mirror concatenation: [s1 | s2 | s3 | s1_mir | s2_mir | s3_mir] __global__ void k_concatenate_6scale_mirror(const float* s1, const float* s2, const float* s3, const float* s1_mir, const float* s2_mir, const float* s3_mir, float* multiscale, int B, int s1_size, int s2_size, int s3_size) { int idx = blockIdx.x * blockDim.x + threadIdx.x; int single_size = s1_size + s2_size + s3_size; // 1029 int total_size = 2 * single_size; // 2058 if (idx >= B * total_size) return; int batch_idx = idx / total_size; int feature_idx = idx % total_size; if (feature_idx < single_size) { // First half: normal features [s1 | s2 | s3] if (feature_idx < s1_size) { // Copy from scale1 multiscale[idx] = s1[batch_idx * s1_size + feature_idx]; } else if (feature_idx < s1_size + s2_size) { // Copy from scale2 int s2_idx = feature_idx - s1_size; multiscale[idx] = s2[batch_idx * s2_size + s2_idx]; } else { // Copy from scale3 int s3_idx = feature_idx - s1_size - s2_size; multiscale[idx] = s3[batch_idx * s3_size + s3_idx]; } } else { // Second half: mirrored features [s1_mir | s2_mir | s3_mir] int mirror_idx = feature_idx - single_size; if (mirror_idx < s1_size) { // Copy from mirrored scale1 multiscale[idx] = s1_mir[batch_idx * s1_size + mirror_idx]; } else if (mirror_idx < s1_size + s2_size) { // Copy from mirrored scale2 int s2_idx = mirror_idx - s1_size; multiscale[idx] = s2_mir[batch_idx * s2_size + s2_idx]; } else { // Copy from mirrored scale3 int s3_idx = mirror_idx - s1_size - s2_size; multiscale[idx] = s3_mir[batch_idx * s3_size + s3_idx]; } } } // ================= BOTTLENECK ANALYSIS KERNELS ================= // Analyze activation saturation (ReLU dead neurons) __global__ void k_analyze_activation_saturation(const float* activations, float* stats, int N) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= N) return; float val = activations[idx]; // Use atomic operations to gather statistics if (val <= 1e-6f) { atomicAdd(&stats[0], 1.0f); // Dead neurons (ReLU=0) } else if (val >= 0.99f) { atomicAdd(&stats[1], 1.0f); // Saturated neurons } atomicAdd(&stats[2], val); // Sum for mean atomicAdd(&stats[3], val*val); // Sum squares for variance } // Analyze gradient flow (vanishing/exploding gradients) __global__ void k_analyze_gradient_flow(const float* gradients, float* stats, int N) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx >= N) return; float grad = gradients[idx]; float abs_grad = fabsf(grad); if (abs_grad < 1e-6f) { atomicAdd(&stats[0], 1.0f); // Vanishing gradients } else if (abs_grad > 10.0f) { atomicAdd(&stats[1], 1.0f); // Exploding gradients } atomicAdd(&stats[2], abs_grad); // Sum for mean } // CRITICAL: Real-time bottleneck detection - identifies where information is lost __global__ void k_bottleneck_detector(const float* input_features, const float* hidden_act, const float* logits, float* bottleneck_metrics, int batch_size, int input_size, int hidden_size, int output_size) { int tid = blockIdx.x * blockDim.x + threadIdx.x; // Feature diversity analysis (input layer) - detect information collapse if (tid < input_size) { float feature_sum = 0.0f, feature_var = 0.0f; for (int b = 0; b < batch_size; b++) { float val = input_features[b * input_size + tid]; feature_sum += val; } float mean = feature_sum / batch_size; for (int b = 0; b < batch_size; b++) { float val = input_features[b * input_size + tid]; feature_var += (val - mean) * (val - mean); } feature_var /= batch_size; // Low variance = information loss (features all the same value) if (feature_var < 1e-4f) atomicAdd(&bottleneck_metrics[0], 1.0f); // Dead features count } // Hidden activation analysis - detect neural saturation if (tid < hidden_size) { float hidden_sum = 0.0f; for (int b = 0; b < batch_size; b++) { hidden_sum += hidden_act[b * hidden_size + tid]; } float hidden_mean = hidden_sum / batch_size; // Saturation detection (critical bottleneck indicators) if (hidden_mean < 0.01f) atomicAdd(&bottleneck_metrics[1], 1.0f); // Dead neurons if (hidden_mean > 0.99f) atomicAdd(&bottleneck_metrics[2], 1.0f); // Saturated neurons } // Logits analysis (output bottleneck) - detect poor class discrimination if (tid < output_size) { float logit_range = 0.0f; float min_logit = 1e10f, max_logit = -1e10f; for (int b = 0; b < batch_size; b++) { float val = logits[b * output_size + tid]; min_logit = fminf(min_logit, val); max_logit = fmaxf(max_logit, val); } logit_range = max_logit - min_logit; // Small range = poor discrimination ability (critical bottleneck) if (logit_range < 0.1f) atomicAdd(&bottleneck_metrics[3], 1.0f); // Poor discrimination count } }