Completed
Push — develop ( 3e4dc3...633974 )
by Arkadiusz
02:40
created

Matrix::getDeterminant()   D

Complexity

Conditions 9
Paths 5

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 29
rs 4.909
cc 9
eloc 19
nc 5
nop 0
1
<?php
2
3
declare (strict_types = 1);
4
5
namespace Phpml\Math;
6
7
use Phpml\Exception\InvalidArgumentException;
8
use Phpml\Exception\MatrixException;
9
10
class Matrix
11
{
12
    /**
13
     * @var array
14
     */
15
    private $matrix;
16
17
    /**
18
     * @var int
19
     */
20
    private $rows;
21
22
    /**
23
     * @var int
24
     */
25
    private $columns;
26
27
    /**
28
     * @var float
29
     */
30
    private $determinant;
31
32
    /**
33
     * @param array $matrix
34
     * @param bool  $validate
35
     *
36
     * @throws InvalidArgumentException
37
     */
38
    public function __construct(array $matrix, bool $validate = true)
39
    {
40
        $this->rows = count($matrix);
41
        $this->columns = count($matrix[0]);
42
43
        if ($validate) {
44
            for ($i = 0; $i < $this->rows; ++$i) {
45
                if (count($matrix[$i]) !== $this->columns) {
46
                    throw InvalidArgumentException::matrixDimensionsDidNotMatch();
47
                }
48
            }
49
        }
50
51
        $this->matrix = $matrix;
52
    }
53
54
    /**
55
     * @return array
56
     */
57
    public function toArray()
58
    {
59
        return $this->matrix;
60
    }
61
62
    /**
63
     * @return int
64
     */
65
    public function getRows()
66
    {
67
        return $this->rows;
68
    }
69
70
    /**
71
     * @return int
72
     */
73
    public function getColumns()
74
    {
75
        return $this->columns;
76
    }
77
78
    /**
79
     * @param $column
80
     *
81
     * @return array
82
     *
83
     * @throws MatrixException
84
     */
85
    public function getColumnValues($column)
86
    {
87
        if ($column >= $this->columns) {
88
            throw MatrixException::columnOutOfRange();
89
        }
90
91
        $values = [];
92
        for ($i = 0; $i < $this->rows; ++$i) {
93
            $values[] = $this->matrix[$i][$column];
94
        }
95
96
        return $values;
97
    }
98
99
    /**
100
     * @return float|int
101
     * 
102
     * @throws MatrixException
103
     */
104
    public function getDeterminant()
105
    {
106
        if ($this->determinant) {
107
            return $this->determinant;
108
        }
109
110
        if (!$this->isSquare()) {
111
            throw MatrixException::notSquareMatrix();
112
        }
113
114
        $determinant = 0;
115
        if ($this->rows == 1 && $this->columns == 1) {
116
            $determinant = $this->matrix[0][0];
117
        } elseif ($this->rows == 2 && $this->columns == 2) {
118
            $determinant = $this->matrix[0][0] * $this->matrix[1][1] -
119
                $this->matrix[0][1] * $this->matrix[1][0];
120
        } else {
121
            for ($j = 0; $j < $this->columns; ++$j) {
122
                $subMatrix = $this->crossOut(0, $j);
123
                if (fmod($j, 2) == 0) {
124
                    $determinant += $this->matrix[0][$j] * $subMatrix->getDeterminant();
125
                } else {
126
                    $determinant -= $this->matrix[0][$j] * $subMatrix->getDeterminant();
127
                }
128
            }
129
        }
130
131
        return $this->determinant = $determinant;
132
    }
133
134
    /**
135
     * @return bool
136
     */
137
    public function isSquare()
138
    {
139
        return $this->columns === $this->rows;
140
    }
141
142
    /**
143
     * @return Matrix
144
     */
145 View Code Duplication
    public function transpose()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
    {
147
        $newMatrix = [];
148
        for ($i = 0; $i < $this->rows; ++$i) {
149
            for ($j = 0; $j < $this->columns; ++$j) {
150
                $newMatrix[$j][$i] = $this->matrix[$i][$j];
151
            }
152
        }
153
154
        return new self($newMatrix, false);
155
    }
156
157
    /**
158
     * @param Matrix $matrix
159
     *
160
     * @return Matrix
161
     *
162
     * @throws InvalidArgumentException
163
     */
164
    public function multiply(Matrix $matrix)
165
    {
166
        if ($this->columns != $matrix->getRows()) {
167
            throw InvalidArgumentException::inconsistentMatrixSupplied();
168
        }
169
170
        $product = [];
171
        $multiplier = $matrix->toArray();
172
        for ($i = 0; $i < $this->rows; ++$i) {
173
            for ($j = 0; $j < $matrix->getColumns(); ++$j) {
174
                $product[$i][$j] = 0;
175
                for ($k = 0; $k < $this->columns; ++$k) {
176
                    $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j];
177
                }
178
            }
179
        }
180
181
        return new self($product, false);
182
    }
183
184
    /**
185
     * @param $value
186
     *
187
     * @return Matrix
188
     */
189 View Code Duplication
    public function divideByScalar($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
190
    {
191
        $newMatrix = array();
192
        for ($i = 0; $i < $this->rows; ++$i) {
193
            for ($j = 0; $j < $this->columns; ++$j) {
194
                $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value;
195
            }
196
        }
197
198
        return new self($newMatrix, false);
199
    }
200
201
    /**
202
     * @return Matrix
203
     *
204
     * @throws MatrixException
205
     */
206
    public function inverse()
207
    {
208
        if (!$this->isSquare()) {
209
            throw MatrixException::notSquareMatrix();
210
        }
211
212
        $newMatrix = array();
213
        for ($i = 0; $i < $this->rows; ++$i) {
214
            for ($j = 0; $j < $this->columns; ++$j) {
215
                $subMatrix = $this->crossOut($i, $j);
216
                if (fmod($i + $j, 2) == 0) {
217
                    $newMatrix[$i][$j] = ($subMatrix->getDeterminant());
218
                } else {
219
                    $newMatrix[$i][$j] = -($subMatrix->getDeterminant());
220
                }
221
            }
222
        }
223
224
        $cofactorMatrix = new self($newMatrix, false);
225
226
        return $cofactorMatrix->transpose()->divideByScalar($this->getDeterminant());
227
    }
228
229
    /**
230
     * @param int $row
231
     * @param int $column
232
     *
233
     * @return Matrix
234
     */
235
    public function crossOut(int $row, int $column)
236
    {
237
        $newMatrix = [];
238
        $r = 0;
239
        for ($i = 0; $i < $this->rows; ++$i) {
240
            $c = 0;
241
            if ($row != $i) {
242
                for ($j = 0; $j < $this->columns; ++$j) {
243
                    if ($column != $j) {
244
                        $newMatrix[$r][$c] = $this->matrix[$i][$j];
245
                        ++$c;
246
                    }
247
                }
248
                ++$r;
249
            }
250
        }
251
252
        return new self($newMatrix, false);
253
    }
254
}
255