1+ package sk.ai.net.nn
2+
3+ import sk.ai.net.Shape
4+ import sk.ai.net.impl.DoublesTensor
5+ import kotlin.test.Test
6+ import kotlin.test.assertEquals
7+ import kotlin.test.assertContentEquals
8+ import kotlin.test.assertNotEquals
9+
10+ class DropoutTest {
11+ @Test
12+ fun `dropout in training mode with p=0_5` () {
13+ val tensor = DoublesTensor (
14+ Shape (2 , 3 ),
15+ doubleArrayOf(1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 )
16+ )
17+ val dropout = Dropout (p = 0.5 )
18+ dropout.train() // Ensure training mode
19+
20+ val result = dropout.forward(tensor) as DoublesTensor
21+
22+ // Check that shape is preserved
23+ assertEquals(Shape (2 , 3 ), result.shape)
24+
25+ // Check that some elements are zeroed out (this is probabilistic, but with p=0.5 it's very likely)
26+ // and that non-zero elements are scaled by 1/(1-p) = 2
27+ var hasZeros = false
28+ var hasScaledValues = false
29+
30+ for (i in 0 until tensor.size) {
31+ if (result.elements[i] == 0.0 ) {
32+ hasZeros = true
33+ } else if (result.elements[i] == tensor.elements[i] * 2.0 ) {
34+ hasScaledValues = true
35+ }
36+ }
37+
38+ // Assert that we have both zeros and scaled values
39+ // Note: This is a probabilistic test, so there's a very small chance it could fail
40+ // even if the implementation is correct
41+ kotlin.test.assertTrue(hasZeros, " Dropout should zero out some elements" )
42+ kotlin.test.assertTrue(hasScaledValues, " Dropout should scale non-zero elements by 1/(1-p)" )
43+ }
44+
45+ @Test
46+ fun `dropout in evaluation mode` () {
47+ val tensor = DoublesTensor (
48+ Shape (2 , 3 ),
49+ doubleArrayOf(1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 )
50+ )
51+ val dropout = Dropout (p = 0.5 )
52+ dropout.eval() // Set to evaluation mode
53+
54+ val result = dropout.forward(tensor) as DoublesTensor
55+
56+ // In evaluation mode, dropout should return the input unchanged
57+ assertEquals(Shape (2 , 3 ), result.shape)
58+ assertContentEquals(tensor.elements, result.elements)
59+ }
60+
61+ @Test
62+ fun `dropout with p=0` () {
63+ val tensor = DoublesTensor (
64+ Shape (2 , 3 ),
65+ doubleArrayOf(1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 )
66+ )
67+ val dropout = Dropout (p = 0.0 )
68+ dropout.train() // Ensure training mode
69+
70+ val result = dropout.forward(tensor) as DoublesTensor
71+
72+ // With p=0, dropout should return the input unchanged
73+ assertEquals(Shape (2 , 3 ), result.shape)
74+ assertContentEquals(tensor.elements, result.elements)
75+ }
76+
77+ @Test
78+ fun `dropout with p=1` () {
79+ val tensor = DoublesTensor (
80+ Shape (2 , 3 ),
81+ doubleArrayOf(1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 )
82+ )
83+ val dropout = Dropout (p = 1.0 )
84+ dropout.train() // Ensure training mode
85+
86+ val result = dropout.forward(tensor) as DoublesTensor
87+
88+ // With p=1, dropout should zero out all elements
89+ assertEquals(Shape (2 , 3 ), result.shape)
90+ assertContentEquals(DoubleArray (tensor.size) { 0.0 }, result.elements)
91+ }
92+
93+ @Test
94+ fun `dropout preserves tensor shape` () {
95+ val shapes = listOf (
96+ Shape (1 , 10 ),
97+ Shape (5 , 5 ),
98+ Shape (2 , 3 , 4 ),
99+ Shape (1 , 2 , 3 , 4 )
100+ )
101+
102+ for (shape in shapes) {
103+ val tensor = DoublesTensor (shape, DoubleArray (shape.volume) { 1.0 })
104+ val dropout = Dropout (p = 0.5 )
105+ dropout.train()
106+
107+ val result = dropout.forward(tensor) as DoublesTensor
108+
109+ assertEquals(shape, result.shape, " Dropout should preserve tensor shape" )
110+ }
111+ }
112+ }
0 commit comments