Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5f3c34e

Browse files
Add Matrix implementation, short README
1 parent 1202f57 commit 5f3c34e

File tree

3 files changed

+324
-3
lines changed

3 files changed

+324
-3
lines changed

‎README.md‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
Native matrix library
2+
-----------------
3+
4+
For a long time, PHP developers dreamed of being able to overload operators. And now, finally, this moment has come.
5+
Thanks to the PHP7.4 and the [lisachenko/z-engine](https://github.com/lisachenko/z-engine) package, we can overload the
6+
operators of comparison, addition, multiplication, casting and much more!
7+
8+
This library is first ever userland PHP extension, that implements operator overloading for the `Matrix` class.
9+
10+
[![GitHub release](https://img.shields.io/github/release/lisachenko/native-matrix.svg)](https://github.com/lisachenko/native-matrix/releases/latest)
11+
[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg)](https://php.net/)
12+
[![License](https://img.shields.io/packagist/l/lisachenko/native-matrix.svg)](https://packagist.org/packages/lisachenko/native-matrix)
13+
14+
15+
Pre-requisites and initialization
16+
--------------
17+
18+
As this library depends on `FFI`, it requires PHP>=7.4 and `FFI` extension to be enabled. Also, current limitations of
19+
[lisachenko/z-engine](https://github.com/lisachenko/z-engine) are also applied (x64, NTS)
20+
21+
To install this library, simply add it via `composer`:
22+
```bash
23+
composer require lisachenko/native-types
24+
```
25+
26+
Now you can test it with following example:
27+
```php
28+
<?php
29+
declare(strict_types=1);
30+
31+
use Native\Type\Matrix;
32+
33+
$first = new Matrix([[10, 20, 30]]);
34+
$second = new Matrix([[2, 4, 6]]);
35+
$value = $first * 2 + $second; // Matrix([[22, 44, 66]])
36+
```
37+
38+
Supported features:
39+
- [x] Matrices addition (`$matrixA + $matrixB`)
40+
- [x] Matrices subtraction (`$matrixA - $matrixB`)
41+
- [x] Matrices multiplication (`$matrixA * $matrixB`)
42+
- [x] Matrices division (`$matrixA / $matrixB`)
43+
- [x] Matrices division (`$matrixA / $matrixB`)
44+
- [x] Matrices equality check (`$matrixA == $matrixB`)
45+
46+
For the future versions, I would like to implement native SSE/AVX assembly methods to improve the performance of calculation.
47+
48+

‎composer.json‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "lisachenko/native-matrix",
3-
"description": "PHP Library that provides an implementation of userland Matrix, powered with overloaded operators",
2+
"name": "lisachenko/native-types",
3+
"description": "PHP Library that provides an implementation of userland types, powered with overloaded operators",
44
"type": "library",
55
"license": "MIT",
66
"authors": [
@@ -10,6 +10,12 @@
1010
}
1111
],
1212
"require": {
13-
"lisachenko/z-engine": "^0.6.0"
13+
"lisachenko/z-engine": "^0.6.0",
14+
"php": "~7.4"
15+
},
16+
"autoload": {
17+
"psr-4" : {
18+
"ZEngine\\Type\\" : "src/"
19+
}
1420
}
1521
}

‎src/Matrix.php‎

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<?php
2+
/**
3+
* Native matrix library
4+
*
5+
* @copyright Copyright 2019, Lisachenko Alexander <lisachenko.it@gmail.com>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
declare(strict_types = 1);
11+
12+
namespace Native\Type;
13+
14+
use InvalidArgumentException;
15+
use ZEngine\ClassExtension\ObjectCompareValuesInterface;
16+
use ZEngine\ClassExtension\ObjectCreateInterface;
17+
use ZEngine\ClassExtension\ObjectCreateTrait;
18+
use ZEngine\ClassExtension\ObjectDoOperationInterface;
19+
use ZEngine\System\OpCode;
20+
use function count, is_numeric;
21+
22+
/**
23+
* Simple class Matrix powered by custom operator handlers
24+
*/
25+
class Matrix implements ObjectCreateInterface, ObjectDoOperationInterface, ObjectCompareValuesInterface
26+
{
27+
use ObjectCreateTrait;
28+
29+
private array $matrix = [];
30+
private int $rows = 0;
31+
private int $columns = 0;
32+
33+
/**
34+
* Matrix constructor.
35+
*
36+
* @param array $matrix
37+
*/
38+
public function __construct(array $matrix)
39+
{
40+
$this->matrix = $matrix;
41+
$this->rows = count($matrix);
42+
$this->columns = count($matrix[0]);
43+
}
44+
45+
public function getRows(): int
46+
{
47+
return $this->rows;
48+
}
49+
50+
public function getColumns(): int
51+
{
52+
return $this->columns;
53+
}
54+
55+
public function isSquare(): bool
56+
{
57+
return $this->columns === $this->rows;
58+
}
59+
60+
/**
61+
* Performs multiplication of two matrices
62+
*
63+
* @param Matrix $multiplier
64+
*
65+
* @todo: Implement SSE/AVX support for multiplication
66+
*
67+
* @return $this Product of two matrices
68+
*/
69+
public function multiply(self $multiplier): self
70+
{
71+
if ($this->columns !== $multiplier->rows) {
72+
throw new InvalidArgumentException('Inconsistent matrix supplied');
73+
}
74+
75+
$totalColumns = $multiplier->columns;
76+
$result = [];
77+
foreach ($this->matrix as $row => $rowItems) {
78+
for ($column = 0; $column < $totalColumns; ++$column) {
79+
$columnItems = array_column($multiplier->matrix, $column);
80+
$cellValue = 0;
81+
foreach ($rowItems as $key => $value) {
82+
$cellValue += $value * $columnItems[$key];
83+
}
84+
85+
$result[$row][$column] = $cellValue;
86+
}
87+
}
88+
89+
return new static($result);
90+
}
91+
92+
/**
93+
* Performs division by scalar value
94+
*
95+
* @param int|float $value Divider
96+
*
97+
* @return $this
98+
*/
99+
public function divideByScalar($value): self
100+
{
101+
if (!is_numeric($value)) {
102+
throw new \InvalidArgumentException("Divide accepts only numeric values");
103+
}
104+
$result = [];
105+
for ($i = 0; $i < $this->rows; ++$i) {
106+
for ($j = 0; $j < $this->columns; ++$j) {
107+
$result[$i][$j] = $this->matrix[$i][$j] / $value;
108+
}
109+
}
110+
111+
return new static($result);
112+
}
113+
114+
/**
115+
* Performs multiplication by scalar value
116+
*
117+
* @param int|float $value Multiplier
118+
*
119+
* @return $this
120+
*/
121+
public function multiplyByScalar($value): self
122+
{
123+
if (!is_numeric($value)) {
124+
throw new \InvalidArgumentException("Multiply accepts only numeric values");
125+
}
126+
$result = [];
127+
for ($i = 0; $i < $this->rows; ++$i) {
128+
for ($j = 0; $j < $this->columns; ++$j) {
129+
$result[$i][$j] = $this->matrix[$i][$j] * $value;
130+
}
131+
}
132+
133+
return new static($result);
134+
}
135+
136+
/**
137+
* Performs addition of two matrices
138+
*
139+
* @param Matrix $value
140+
*
141+
* @return $this Sum of two matrices
142+
* @todo: Implement SSE/AVX support for addition
143+
*/
144+
public function sum(self $value): self
145+
{
146+
$result = [];
147+
for ($i = 0; $i < $this->rows; ++$i) {
148+
for ($k = 0; $k < $this->columns; ++$k) {
149+
$result[$i][$k] = $this->matrix[$i][$k] + $value->matrix[$i][$k];
150+
}
151+
}
152+
153+
return new static($result);
154+
}
155+
156+
/**
157+
* Performs subtraction of two matrices
158+
*
159+
* @param Matrix $value
160+
*
161+
* @todo: Implement SSE/AVX support for addition
162+
*
163+
* @return $this Subtraction of two matrices
164+
*/
165+
public function subtract(self $value): self
166+
{
167+
$result = [];
168+
for ($i = 0; $i < $this->rows; ++$i) {
169+
for ($k = 0; $k < $this->columns; ++$k) {
170+
$result[$i][$k] = $this->matrix[$i][$k] - $value->matrix[$i][$k];
171+
}
172+
}
173+
174+
return new static($result);
175+
}
176+
177+
/**
178+
* Checks if the given matrix equals to another one
179+
*
180+
* @param Matrix $another Another matrix
181+
*
182+
* @return bool
183+
*/
184+
public function equals(Matrix $another): bool
185+
{
186+
if ($another->rows !== $this->rows || $another->columns !== $this->columns) {
187+
return false;
188+
}
189+
for ($i = 0; $i < $this->rows; ++$i) {
190+
for ($k = 0; $k < $this->columns; ++$k) {
191+
$equals = $this->matrix[$i][$k] === $another->matrix[$i][$k];
192+
if (!$equals) {
193+
return false;
194+
}
195+
}
196+
}
197+
198+
return true;
199+
}
200+
201+
/**
202+
* Performs operation on given object
203+
*
204+
* @param int $opCode Operation code
205+
* @param Matrix|int|float $left left side of operation
206+
* @param Matrix|int|float $right Right side of operation
207+
*
208+
* @return Matrix Result of operation value
209+
*/
210+
public static function __doOperation(int $opCode, $left, $right): Matrix
211+
{
212+
$isLeftMatrix = $left instanceof Matrix;
213+
$isRightMatrix = $right instanceof Matrix;
214+
$isLeftNumeric = is_numeric($left);
215+
$isRightNumeric = is_numeric($right);
216+
217+
switch ($opCode) {
218+
case OpCode::ADD:
219+
if ($isLeftMatrix && $isRightMatrix) {
220+
return $left->sum($right);
221+
}
222+
break;
223+
case OpCode::SUB:
224+
if ($isLeftMatrix && $isRightMatrix) {
225+
return $left->subtract($right);
226+
}
227+
break;
228+
case OpCode::MUL:
229+
if ($isLeftMatrix && $isRightMatrix) {
230+
return $left->multiply($right);
231+
} elseif ($isLeftMatrix && $isRightNumeric) {
232+
return $left->multiplyByScalar($right);
233+
} elseif ($isLeftNumeric && $isRightMatrix) {
234+
return $right->multiplyByScalar($left);
235+
}
236+
break;
237+
case OpCode::DIV:
238+
if ($isLeftMatrix && $isRightNumeric) {
239+
return $left->divideByScalar($right);
240+
}
241+
break;
242+
}
243+
244+
throw new \LogicException('Unsupported ' . OpCode::name($opCode). ' operation or invalid arguments');
245+
}
246+
247+
/**
248+
* Performs comparison of given object with another value
249+
*
250+
* @param mixed $one First side of operation
251+
* @param mixed $another Another side of operation
252+
*
253+
* @return int Result of comparison: 1 is greater, -1 is less, 0 is equal
254+
*/
255+
public static function __compare($one, $another): int
256+
{
257+
if (!($one instanceof Matrix) || !($another instanceof Matrix)) {
258+
throw new \InvalidArgumentException('Matrix can be compared only with another matrix');
259+
}
260+
261+
if ($one->equals($another)) {
262+
return 0;
263+
}
264+
265+
return -2;
266+
}
267+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /