@@ -6804,6 +6804,112 @@ BOOST_AUTO_TEST_CASE(test_case_globalavgpool2d_fixed_point)
68046804 BOOST_TEST (output[1 ].getValue () == ValueType (3 , 0 ).getValue ());
68056805}
68066806
6807+ BOOST_AUTO_TEST_CASE (test_case_avgpool2d_backward)
6808+ {
6809+ // 4x4 single-channel, 2x2 window stride 2. Each output cell distributes
6810+ // its delta uniformly across the 4 input cells in its window: grad = d/4.
6811+ tinymind::AvgPool2D<double , 4 , 4 , 1 , 2 , 2 , 2 , 2 > pool;
6812+ double outputDeltas[4 ] = {4.0 , 8.0 , 12.0 , 16.0 };
6813+ double inputDeltas[16 ];
6814+ pool.backward (outputDeltas, inputDeltas);
6815+
6816+ // Top-left window (output 0): grad = 1.0 across 4 cells.
6817+ BOOST_TEST (std::fabs (inputDeltas[0 ] - 1.0 ) < 1e-9 );
6818+ BOOST_TEST (std::fabs (inputDeltas[1 ] - 1.0 ) < 1e-9 );
6819+ BOOST_TEST (std::fabs (inputDeltas[4 ] - 1.0 ) < 1e-9 );
6820+ BOOST_TEST (std::fabs (inputDeltas[5 ] - 1.0 ) < 1e-9 );
6821+ // Top-right window (output 1): grad = 2.0
6822+ BOOST_TEST (std::fabs (inputDeltas[2 ] - 2.0 ) < 1e-9 );
6823+ BOOST_TEST (std::fabs (inputDeltas[3 ] - 2.0 ) < 1e-9 );
6824+ BOOST_TEST (std::fabs (inputDeltas[6 ] - 2.0 ) < 1e-9 );
6825+ BOOST_TEST (std::fabs (inputDeltas[7 ] - 2.0 ) < 1e-9 );
6826+ // Bot-right window (output 3): grad = 4.0
6827+ BOOST_TEST (std::fabs (inputDeltas[10 ] - 4.0 ) < 1e-9 );
6828+ BOOST_TEST (std::fabs (inputDeltas[15 ] - 4.0 ) < 1e-9 );
6829+ }
6830+
6831+ BOOST_AUTO_TEST_CASE (test_case_avgpool2d_backward_fixed_point)
6832+ {
6833+ // Same input pattern as above in Q8.8: confirms divisor() is correct on
6834+ // the backward path, not just forward.
6835+ typedef tinymind::QValue<8 , 8 , true , tinymind::RoundUpPolicy> ValueType;
6836+ tinymind::AvgPool2D<ValueType, 4 , 4 , 1 , 2 , 2 , 2 , 2 > pool;
6837+ ValueType outputDeltas[4 ] = {ValueType (4 , 0 ), ValueType (8 , 0 ), ValueType (12 , 0 ), ValueType (16 , 0 )};
6838+ ValueType inputDeltas[16 ];
6839+ pool.backward (outputDeltas, inputDeltas);
6840+
6841+ BOOST_TEST (inputDeltas[0 ].getValue () == ValueType (1 , 0 ).getValue ());
6842+ BOOST_TEST (inputDeltas[5 ].getValue () == ValueType (1 , 0 ).getValue ());
6843+ BOOST_TEST (inputDeltas[3 ].getValue () == ValueType (2 , 0 ).getValue ());
6844+ BOOST_TEST (inputDeltas[10 ].getValue () == ValueType (4 , 0 ).getValue ());
6845+ BOOST_TEST (inputDeltas[15 ].getValue () == ValueType (4 , 0 ).getValue ());
6846+ }
6847+
6848+ BOOST_AUTO_TEST_CASE (test_case_globalavgpool2d_backward)
6849+ {
6850+ // GAP backward distributes each output delta uniformly across its
6851+ // spatial extent (H*W positions per channel).
6852+ tinymind::GlobalAvgPool2D<double , 3 , 3 , 2 > gap;
6853+ double outputDeltas[2 ] = {9.0 , 18.0 }; // chosen so per-cell grad is 1.0 and 2.0
6854+ double inputDeltas[18 ];
6855+ gap.backward (outputDeltas, inputDeltas);
6856+
6857+ for (size_t i = 0 ; i < 9 ; ++i)
6858+ {
6859+ BOOST_TEST (std::fabs (inputDeltas[i * 2 + 0 ] - 1.0 ) < 1e-9 );
6860+ BOOST_TEST (std::fabs (inputDeltas[i * 2 + 1 ] - 2.0 ) < 1e-9 );
6861+ }
6862+ }
6863+
6864+ BOOST_AUTO_TEST_CASE (test_case_globalavgpool2d_backward_fixed_point)
6865+ {
6866+ typedef tinymind::QValue<8 , 8 , true , tinymind::RoundUpPolicy> ValueType;
6867+ tinymind::GlobalAvgPool2D<ValueType, 3 , 3 , 2 > gap;
6868+ ValueType outputDeltas[2 ] = {ValueType (9 , 0 ), ValueType (18 , 0 )};
6869+ ValueType inputDeltas[18 ];
6870+ gap.backward (outputDeltas, inputDeltas);
6871+
6872+ for (size_t i = 0 ; i < 9 ; ++i)
6873+ {
6874+ BOOST_TEST (inputDeltas[i * 2 + 0 ].getValue () == ValueType (1 , 0 ).getValue ());
6875+ BOOST_TEST (inputDeltas[i * 2 + 1 ].getValue () == ValueType (2 , 0 ).getValue ());
6876+ }
6877+ }
6878+
6879+ BOOST_AUTO_TEST_CASE (test_case_maxpool2d_backward_fixed_point)
6880+ {
6881+ // Argmax-routed gradients: only the position holding each max receives
6882+ // the upstream delta. Other positions stay zero.
6883+ typedef tinymind::QValue<8 , 8 , true , tinymind::RoundUpPolicy> ValueType;
6884+ tinymind::MaxPool2D<ValueType, 4 , 4 , 1 , 2 , 2 , 2 , 2 > pool;
6885+ ValueType input[16 ];
6886+ const int values[16 ] = {
6887+ 1 , 3 , 2 , 4 ,
6888+ 5 , 7 , 6 , 8 ,
6889+ 9 , 11 , 10 , 12 ,
6890+ 13 , 15 , 14 , 16
6891+ };
6892+ for (size_t i = 0 ; i < 16 ; ++i) input[i] = ValueType (values[i], 0 );
6893+
6894+ ValueType output[4 ];
6895+ pool.forward (input, output); // populates argmax indices
6896+
6897+ ValueType outputDeltas[4 ] = {ValueType (1 , 0 ), ValueType (1 , 0 ), ValueType (1 , 0 ), ValueType (1 , 0 )};
6898+ ValueType inputDeltas[16 ];
6899+ pool.backward (outputDeltas, inputDeltas);
6900+
6901+ typename ValueType::FullWidthValueType sum = 0 ;
6902+ size_t hits = 0 ;
6903+ for (size_t i = 0 ; i < 16 ; ++i)
6904+ {
6905+ sum += inputDeltas[i].getValue ();
6906+ if (inputDeltas[i].getValue () != 0 ) ++hits;
6907+ }
6908+ // Exactly four argmax positions, each receiving raw=256 (= 1.0).
6909+ BOOST_TEST (hits == 4u );
6910+ BOOST_TEST (sum == 4 * ValueType (1 , 0 ).getValue ());
6911+ }
6912+
68076913// ============================================================
68086914// Benchmark harness tests
68096915// ============================================================
0 commit comments