1<?php
2
3namespace Rubix\ML\Tests\NeuralNet\Layers;
4
5use Tensor\Matrix;
6use Rubix\ML\Deferred;
7use Rubix\ML\NeuralNet\Layers\Layer;
8use Rubix\ML\NeuralNet\Layers\Dense;
9use Rubix\ML\NeuralNet\Layers\Hidden;
10use Rubix\ML\NeuralNet\Initializers\He;
11use Rubix\ML\NeuralNet\Layers\Parametric;
12use Rubix\ML\NeuralNet\Optimizers\Stochastic;
13use Rubix\ML\NeuralNet\Initializers\Constant;
14use PHPUnit\Framework\TestCase;
15
16/**
17 * @group Layers
18 * @covers \Rubix\ML\NeuralNet\Layers\Dense
19 */
20class DenseTest extends TestCase
21{
22    protected const RANDOM_SEED = 0;
23
24    /**
25     * @var int
26     */
27    protected $fanIn;
28
29    /**
30     * @var \Tensor\Matrix
31     */
32    protected $input;
33
34    /**
35     * @var \Rubix\ML\Deferred
36     */
37    protected $prevGrad;
38
39    /**
40     * @var \Rubix\ML\NeuralNet\Optimizers\Optimizer
41     */
42    protected $optimizer;
43
44    /**
45     * @var \Rubix\ML\NeuralNet\Layers\Dense
46     */
47    protected $layer;
48
49    /**
50     * @before
51     */
52    protected function setUp() : void
53    {
54        $this->fanIn = 3;
55
56        $this->input = Matrix::quick([
57            [1.0, 2.5, -0.1],
58            [0.1, 0.0, 3.0],
59            [0.002, -6.0, -0.5],
60        ]);
61
62        $this->prevGrad = new Deferred(function () {
63            return Matrix::quick([
64                [0.50, 0.2, 0.01],
65                [0.25, 0.1, 0.89],
66            ]);
67        });
68
69        $this->optimizer = new Stochastic(0.001);
70
71        $this->layer = new Dense(2, 0.0, true, new He(), new Constant(0.0));
72
73        srand(self::RANDOM_SEED);
74    }
75
76    /**
77     * @test
78     */
79    public function build() : void
80    {
81        $this->assertInstanceOf(Dense::class, $this->layer);
82        $this->assertInstanceOf(Layer::class, $this->layer);
83        $this->assertInstanceOf(Hidden::class, $this->layer);
84        $this->assertInstanceOf(Parametric::class, $this->layer);
85    }
86
87    /**
88     * @test
89     */
90    public function initializeForwardBackInfer() : void
91    {
92        $this->layer->initialize($this->fanIn);
93
94        $this->assertEquals(2, $this->layer->width());
95
96        $expected = [
97            [0.1331636897703166, -2.659941938483866, 0.37781475642889195],
98            [0.8082829632098398, -2.9282037817258764, 0.21589538926944302],
99        ];
100
101        $forward = $this->layer->forward($this->input);
102
103        $this->assertInstanceOf(Matrix::class, $forward);
104        $this->assertEquals($expected, $forward->asArray());
105
106        $gradient = $this->layer->back($this->prevGrad, $this->optimizer)->compute();
107
108        $expected = [
109            [0.2513486032877107, 0.10053944131508427, 0.698223970571707],
110            [0.16407184592276702, 0.0656287383691068, 0.2102008334557029],
111            [0.44839890381544645, 0.1793595615261786, 0.7297101185916894],
112        ];
113
114        $this->assertInstanceOf(Matrix::class, $gradient);
115        $this->assertEquals($expected, $gradient->asArray());
116
117        $expected = [
118            [0.1314490977703166, -2.670373438483866, 0.376362656428892],
119            [0.8063645522098398, -2.9367382817258765, 0.20608923926944314],
120        ];
121
122        $infer = $this->layer->infer($this->input);
123
124        $this->assertInstanceOf(Matrix::class, $infer);
125        $this->assertEquals($expected, $infer->asArray());
126    }
127}
128