Release of v3.2.5

Add [AllowDynamicProperties] in the base view class for J5. Move the _prepareDocument  above the display call in the base view class. Remove all backward compatibility issues, so JCB will not need the [Backward Compatibility] plugin to run. Added new import powers for custom import of spreadsheets. Move the setDocument and _prepareDocument above the display in the site view and custom admin view. Update the trashhelper layout to work in Joomla 5. Add AllowDynamicProperties (Joomla 4+5) to view class to allow Custom Dynamic Get methods to work without issues. Fix Save failed issue in dynamicGet. #1148. Move all [TEXT, EDITOR, TEXTAREA] fields from [NOT NULL] to [NULL]. Add the DateHelper class and improve the date methods. Add simple SessionHelper class. Add first classes for the new import engine. Improve the [VDM Registry] to be Joomla Registry Compatible. Move all registries to the [VDM Registry] class. Fix Checked Out to be null and not 0. (#1194). Fix created_by, modified_by, checked_out fields in the compiler of the SQL. (#1194). Update all core date fields in table class. (#1188). Update created_by, modified_by, checked_out fields in table class. Implementation of the decentralized Super-Power CORE repository network. (#1190). Fix the noticeboard to display Llewellyn's Joomla Social feed.
This commit is contained in:
2025-02-14 22:59:16 +02:00
parent 714cb5588a
commit f8b0a5df54
1474 changed files with 133744 additions and 48350 deletions

View File

@@ -0,0 +1,124 @@
name: main
on: [ push, pull_request ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version:
- '7.1'
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
include:
- php-version: 'nightly'
experimental: true
name: PHP ${{ matrix.php-version }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Delete composer lock file
id: composer-lock
if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' || matrix.php-version == '8.2' || matrix.php-version == 'nightly' }}
run: |
rm composer.lock
echo "::set-output name=flags::--ignore-platform-reqs"
- name: Install dependencies
run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
- name: Setup problem matchers for PHP
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
- name: Setup problem matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Test with PHPUnit
run: ./vendor/bin/phpunit
phpcs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
coverage: none
tools: cs2pr
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Code style with PHP_CodeSniffer
run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr --graceful-warnings --colorize
coverage:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
coverage: pcov
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Coverage
run: |
./vendor/bin/phpunit --coverage-text

View File

@@ -0,0 +1,215 @@
PHPMatrix
==========
---
PHP Class for handling Matrices
[![Build Status](https://github.com/MarkBaker/PHPMatrix/workflows/main/badge.svg)](https://github.com/MarkBaker/PHPMatrix/actions)
[![Total Downloads](https://img.shields.io/packagist/dt/markbaker/matrix)](https://packagist.org/packages/markbaker/matrix)
[![Latest Stable Version](https://img.shields.io/github/v/release/MarkBaker/PHPMatrix)](https://packagist.org/packages/markbaker/matrix)
[![License](https://img.shields.io/github/license/MarkBaker/PHPMatrix)](https://packagist.org/packages/markbaker/matrix)
[![Matrix Transform](https://imgs.xkcd.com/comics/matrix_transform.png)](https://xkcd.com/184/)
Matrix Transform
---
This library currently provides the following operations:
- addition
- direct sum
- subtraction
- multiplication
- division (using [A].[B]<sup>-1</sup>)
- division by
- division into
together with functions for
- adjoint
- antidiagonal
- cofactors
- determinant
- diagonal
- identity
- inverse
- minors
- trace
- transpose
- solve
Given Matrices A and B, calculate X for A.X = B
and classes for
- Decomposition
- LU Decomposition with partial row pivoting,
such that [P].[A] = [L].[U] and [A] = [P]<sup>|</sup>.[L].[U]
- QR Decomposition
such that [A] = [Q].[R]
## TO DO
- power() function
- Decomposition
- Cholesky Decomposition
- EigenValue Decomposition
- EigenValues
- EigenVectors
---
# Installation
```shell
composer require markbaker/matrix:^3.0
```
# Important BC Note
If you've previously been using procedural calls to functions and operations using this library, then from version 3.0 you should use [MarkBaker/PHPMatrixFunctions](https://github.com/MarkBaker/PHPMatrixFunctions) instead (available on packagist as [markbaker/matrix-functions](https://packagist.org/packages/markbaker/matrix-functions)).
You'll need to replace `markbaker/matrix`in your `composer.json` file with the new library, but otherwise there should be no difference in the namespacing, or in the way that you have called the Matrix functions in the past, so no actual code changes are required.
```shell
composer require markbaker/matrix-functions:^1.0
```
You should not reference this library (`markbaker/matrix`) in your `composer.json`, composer wil take care of that for you.
# Usage
To create a new Matrix object, provide an array as the constructor argument
```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
[ 9, 6, 7, 12],
[ 4, 15, 14, 1],
];
$matrix = new Matrix\Matrix($grid);
```
The `Builder` class provides helper methods for creating specific matrices, specifically an identity matrix of a specified size; or a matrix of a specified dimensions, with every cell containing a set value.
```php
$matrix = Matrix\Builder::createFilledMatrix(1, 5, 3);
```
Will create a matrix of 5 rows and 3 columns, filled with a `1` in every cell; while
```php
$matrix = Matrix\Builder::createIdentityMatrix(3);
```
will create a 3x3 identity matrix.
Matrix objects are immutable: whenever you call a method or pass a grid to a function that returns a matrix value, a new Matrix object will be returned, and the original will remain unchanged. This also allows you to chain multiple methods as you would for a fluent interface (as long as they are methods that will return a Matrix result).
## Performing Mathematical Operations
To perform mathematical operations with Matrices, you can call the appropriate method against a matrix value, passing other values as arguments
```php
$matrix1 = new Matrix\Matrix([
[2, 7, 6],
[9, 5, 1],
[4, 3, 8],
]);
$matrix2 = new Matrix\Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
var_dump($matrix1->multiply($matrix2)->toArray());
```
or pass all values to the appropriate static method
```php
$matrix1 = new Matrix\Matrix([
[2, 7, 6],
[9, 5, 1],
[4, 3, 8],
]);
$matrix2 = new Matrix\Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
var_dump(Matrix\Operations::multiply($matrix1, $matrix2)->toArray());
```
You can pass in the arguments as Matrix objects, or as arrays.
If you want to perform the same operation against multiple values (e.g. to add three or more matrices), then you can pass multiple arguments to any of the operations.
## Using functions
When calling any of the available functions for a matrix value, you can either call the relevant method for the Matrix object
```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
[ 9, 6, 7, 12],
[ 4, 15, 14, 1],
];
$matrix = new Matrix\Matrix($grid);
echo $matrix->trace();
```
or you can call the static method, passing the Matrix object or array as an argument
```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
[ 9, 6, 7, 12],
[ 4, 15, 14, 1],
];
$matrix = new Matrix\Matrix($grid);
echo Matrix\Functions::trace($matrix);
```
```php
$grid = [
[16, 3, 2, 13],
[ 5, 10, 11, 8],
[ 9, 6, 7, 12],
[ 4, 15, 14, 1],
];
echo Matrix\Functions::trace($grid);
```
## Decomposition
The library also provides classes for matrix decomposition. You can access these using
```php
$grid = [
[1, 2],
[3, 4],
];
$matrix = new Matrix\Matrix($grid);
$decomposition = new Matrix\Decomposition\QR($matrix);
$Q = $decomposition->getQ();
$R = $decomposition->getR();
```
or alternatively us the `Decomposition` factory, identifying which form of decomposition you want to use
```php
$grid = [
[1, 2],
[3, 4],
];
$matrix = new Matrix\Matrix($grid);
$decomposition = Matrix\Decomposition\Decomposition::decomposition(Matrix\Decomposition\Decomposition::QR, $matrix);
$Q = $decomposition->getQ();
$R = $decomposition->getR();
```

View File

@@ -1,53 +0,0 @@
<?php
namespace Matrix;
/**
*
* Autoloader for Matrix classes
*
* @package Matrix
* @copyright Copyright (c) 2014 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
class Autoloader
{
/**
* Register the Autoloader with SPL
*
*/
public static function Register()
{
if (function_exists('__autoload')) {
// Register any existing autoloader function with SPL, so we don't get any clashes
spl_autoload_register('__autoload');
}
// Register ourselves with SPL
return spl_autoload_register(['Matrix\\Autoloader', 'Load']);
}
/**
* Autoload a class identified by name
*
* @param string $pClassName Name of the object to load
*/
public static function Load($pClassName)
{
if ((class_exists($pClassName, false)) || (strpos($pClassName, 'Matrix\\') !== 0)) {
// Either already loaded, or not a Matrix class request
return false;
}
$pClassFilePath = __DIR__ . DIRECTORY_SEPARATOR .
'src' . DIRECTORY_SEPARATOR .
str_replace(['Matrix\\', '\\'], ['', '/'], $pClassName) .
'.php';
if ((file_exists($pClassFilePath) === false) || (is_readable($pClassFilePath) === false)) {
// Can't load
return false;
}
require($pClassFilePath);
}
}

View File

@@ -1,38 +0,0 @@
<?php
include_once __DIR__ . '/Autoloader.php';
\Matrix\Autoloader::Register();
abstract class FilesystemRegexFilter extends RecursiveRegexIterator
{
protected $regex;
public function __construct(RecursiveIterator $it, $regex)
{
$this->regex = $regex;
parent::__construct($it, $regex);
}
}
class FilenameFilter extends FilesystemRegexFilter
{
// Filter files against the regex
public function accept()
{
return (!$this->isFile() || preg_match($this->regex, $this->getFilename()));
}
}
$srcFolder = __DIR__ . DIRECTORY_SEPARATOR . 'src';
$srcDirectory = new RecursiveDirectoryIterator($srcFolder);
$filteredFileList = new FilenameFilter($srcDirectory, '/(?:php)$/i');
$filteredFileList = new FilenameFilter($filteredFileList, '/^(?!.*(Matrix|Exception)\.php).*$/i');
foreach (new RecursiveIteratorIterator($filteredFileList) as $file) {
if ($file->isFile()) {
include_once $file;
}
}

View File

@@ -21,13 +21,13 @@ class Builder
* Create a new matrix of specified dimensions, and filled with a specified value
* If the column argument isn't provided, then a square matrix will be created
*
* @param mixed $value
* @param mixed $fillValue
* @param int $rows
* @param int|null $columns
* @return Matrix
* @throws Exception
*/
public static function createFilledMatrix($value, $rows, $columns = null)
public static function createFilledMatrix($fillValue, $rows, $columns = null)
{
if ($columns === null) {
$columns = $rows;
@@ -43,7 +43,7 @@ class Builder
array_fill(
0,
$columns,
$value
$fillValue
)
)
);
@@ -57,9 +57,9 @@ class Builder
* @return Matrix
* @throws Exception
*/
public static function createIdentityMatrix($dimensions)
public static function createIdentityMatrix($dimensions, $fillValue = null)
{
$grid = static::createFilledMatrix(null, $dimensions)->toArray();
$grid = static::createFilledMatrix($fillValue, $dimensions)->toArray();
for ($x = 0; $x < $dimensions; ++$x) {
$grid[$x][$x] = 1;

View File

@@ -0,0 +1,27 @@
<?php
namespace Matrix\Decomposition;
use Matrix\Exception;
use Matrix\Matrix;
class Decomposition
{
const LU = 'LU';
const QR = 'QR';
/**
* @throws Exception
*/
public static function decomposition($type, Matrix $matrix)
{
switch (strtoupper($type)) {
case self::LU:
return new LU($matrix);
case self::QR:
return new QR($matrix);
default:
throw new Exception('Invalid Decomposition');
}
}
}

View File

@@ -0,0 +1,260 @@
<?php
namespace Matrix\Decomposition;
use Matrix\Exception;
use Matrix\Matrix;
class LU
{
private $luMatrix;
private $rows;
private $columns;
private $pivot = [];
public function __construct(Matrix $matrix)
{
$this->luMatrix = $matrix->toArray();
$this->rows = $matrix->rows;
$this->columns = $matrix->columns;
$this->buildPivot();
}
/**
* Get lower triangular factor.
*
* @return Matrix Lower triangular factor
*/
public function getL(): Matrix
{
$lower = [];
$columns = min($this->rows, $this->columns);
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $columns; ++$column) {
if ($row > $column) {
$lower[$row][$column] = $this->luMatrix[$row][$column];
} elseif ($row === $column) {
$lower[$row][$column] = 1.0;
} else {
$lower[$row][$column] = 0.0;
}
}
}
return new Matrix($lower);
}
/**
* Get upper triangular factor.
*
* @return Matrix Upper triangular factor
*/
public function getU(): Matrix
{
$upper = [];
$rows = min($this->rows, $this->columns);
for ($row = 0; $row < $rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
if ($row <= $column) {
$upper[$row][$column] = $this->luMatrix[$row][$column];
} else {
$upper[$row][$column] = 0.0;
}
}
}
return new Matrix($upper);
}
/**
* Return pivot permutation vector.
*
* @return Matrix Pivot matrix
*/
public function getP(): Matrix
{
$pMatrix = [];
$pivots = $this->pivot;
$pivotCount = count($pivots);
foreach ($pivots as $row => $pivot) {
$pMatrix[$row] = array_fill(0, $pivotCount, 0);
$pMatrix[$row][$pivot] = 1;
}
return new Matrix($pMatrix);
}
/**
* Return pivot permutation vector.
*
* @return array Pivot vector
*/
public function getPivot(): array
{
return $this->pivot;
}
/**
* Is the matrix nonsingular?
*
* @return bool true if U, and hence A, is nonsingular
*/
public function isNonsingular(): bool
{
for ($diagonal = 0; $diagonal < $this->columns; ++$diagonal) {
if ($this->luMatrix[$diagonal][$diagonal] === 0.0) {
return false;
}
}
return true;
}
private function buildPivot(): void
{
for ($row = 0; $row < $this->rows; ++$row) {
$this->pivot[$row] = $row;
}
for ($column = 0; $column < $this->columns; ++$column) {
$luColumn = $this->localisedReferenceColumn($column);
$this->applyTransformations($column, $luColumn);
$pivot = $this->findPivot($column, $luColumn);
if ($pivot !== $column) {
$this->pivotExchange($pivot, $column);
}
$this->computeMultipliers($column);
unset($luColumn);
}
}
private function localisedReferenceColumn($column): array
{
$luColumn = [];
for ($row = 0; $row < $this->rows; ++$row) {
$luColumn[$row] = &$this->luMatrix[$row][$column];
}
return $luColumn;
}
private function applyTransformations($column, array $luColumn): void
{
for ($row = 0; $row < $this->rows; ++$row) {
$luRow = $this->luMatrix[$row];
// Most of the time is spent in the following dot product.
$kmax = min($row, $column);
$sValue = 0.0;
for ($kValue = 0; $kValue < $kmax; ++$kValue) {
$sValue += $luRow[$kValue] * $luColumn[$kValue];
}
$luRow[$column] = $luColumn[$row] -= $sValue;
}
}
private function findPivot($column, array $luColumn): int
{
$pivot = $column;
for ($row = $column + 1; $row < $this->rows; ++$row) {
if (abs($luColumn[$row]) > abs($luColumn[$pivot])) {
$pivot = $row;
}
}
return $pivot;
}
private function pivotExchange($pivot, $column): void
{
for ($kValue = 0; $kValue < $this->columns; ++$kValue) {
$tValue = $this->luMatrix[$pivot][$kValue];
$this->luMatrix[$pivot][$kValue] = $this->luMatrix[$column][$kValue];
$this->luMatrix[$column][$kValue] = $tValue;
}
$lValue = $this->pivot[$pivot];
$this->pivot[$pivot] = $this->pivot[$column];
$this->pivot[$column] = $lValue;
}
private function computeMultipliers($diagonal): void
{
if (($diagonal < $this->rows) && ($this->luMatrix[$diagonal][$diagonal] != 0.0)) {
for ($row = $diagonal + 1; $row < $this->rows; ++$row) {
$this->luMatrix[$row][$diagonal] /= $this->luMatrix[$diagonal][$diagonal];
}
}
}
private function pivotB(Matrix $B): array
{
$X = [];
foreach ($this->pivot as $rowId) {
$row = $B->getRows($rowId + 1)->toArray();
$X[] = array_pop($row);
}
return $X;
}
/**
* Solve A*X = B.
*
* @param Matrix $B a Matrix with as many rows as A and any number of columns
*
* @throws Exception
*
* @return Matrix X so that L*U*X = B(piv,:)
*/
public function solve(Matrix $B): Matrix
{
if ($B->rows !== $this->rows) {
throw new Exception('Matrix row dimensions are not equal');
}
if ($this->rows !== $this->columns) {
throw new Exception('LU solve() only works on square matrices');
}
if (!$this->isNonsingular()) {
throw new Exception('Can only perform operation on singular matrix');
}
// Copy right hand side with pivoting
$nx = $B->columns;
$X = $this->pivotB($B);
// Solve L*Y = B(piv,:)
for ($k = 0; $k < $this->columns; ++$k) {
for ($i = $k + 1; $i < $this->columns; ++$i) {
for ($j = 0; $j < $nx; ++$j) {
$X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
}
}
}
// Solve U*X = Y;
for ($k = $this->columns - 1; $k >= 0; --$k) {
for ($j = 0; $j < $nx; ++$j) {
$X[$k][$j] /= $this->luMatrix[$k][$k];
}
for ($i = 0; $i < $k; ++$i) {
for ($j = 0; $j < $nx; ++$j) {
$X[$i][$j] -= $X[$k][$j] * $this->luMatrix[$i][$k];
}
}
}
return new Matrix($X);
}
}

View File

@@ -0,0 +1,191 @@
<?php
namespace Matrix\Decomposition;
use Matrix\Exception;
use Matrix\Matrix;
class QR
{
private $qrMatrix;
private $rows;
private $columns;
private $rDiagonal = [];
public function __construct(Matrix $matrix)
{
$this->qrMatrix = $matrix->toArray();
$this->rows = $matrix->rows;
$this->columns = $matrix->columns;
$this->decompose();
}
public function getHouseholdVectors(): Matrix
{
$householdVectors = [];
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
if ($row >= $column) {
$householdVectors[$row][$column] = $this->qrMatrix[$row][$column];
} else {
$householdVectors[$row][$column] = 0.0;
}
}
}
return new Matrix($householdVectors);
}
public function getQ(): Matrix
{
$qGrid = [];
$rowCount = $this->rows;
for ($k = $this->columns - 1; $k >= 0; --$k) {
for ($i = 0; $i < $this->rows; ++$i) {
$qGrid[$i][$k] = 0.0;
}
$qGrid[$k][$k] = 1.0;
if ($this->columns > $this->rows) {
$qGrid = array_slice($qGrid, 0, $this->rows);
}
for ($j = $k; $j < $this->columns; ++$j) {
if (isset($this->qrMatrix[$k], $this->qrMatrix[$k][$k]) && $this->qrMatrix[$k][$k] != 0.0) {
$s = 0.0;
for ($i = $k; $i < $this->rows; ++$i) {
$s += $this->qrMatrix[$i][$k] * $qGrid[$i][$j];
}
$s = -$s / $this->qrMatrix[$k][$k];
for ($i = $k; $i < $this->rows; ++$i) {
$qGrid[$i][$j] += $s * $this->qrMatrix[$i][$k];
}
}
}
}
array_walk(
$qGrid,
function (&$row) use ($rowCount) {
$row = array_reverse($row);
$row = array_slice($row, 0, $rowCount);
}
);
return new Matrix($qGrid);
}
public function getR(): Matrix
{
$rGrid = [];
for ($row = 0; $row < $this->columns; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
if ($row < $column) {
$rGrid[$row][$column] = $this->qrMatrix[$row][$column] ?? 0.0;
} elseif ($row === $column) {
$rGrid[$row][$column] = $this->rDiagonal[$row] ?? 0.0;
} else {
$rGrid[$row][$column] = 0.0;
}
}
}
if ($this->columns > $this->rows) {
$rGrid = array_slice($rGrid, 0, $this->rows);
}
return new Matrix($rGrid);
}
private function hypo($a, $b): float
{
if (abs($a) > abs($b)) {
$r = $b / $a;
$r = abs($a) * sqrt(1 + $r * $r);
} elseif ($b != 0.0) {
$r = $a / $b;
$r = abs($b) * sqrt(1 + $r * $r);
} else {
$r = 0.0;
}
return $r;
}
/**
* QR Decomposition computed by Householder reflections.
*/
private function decompose(): void
{
for ($k = 0; $k < $this->columns; ++$k) {
// Compute 2-norm of k-th column without under/overflow.
$norm = 0.0;
for ($i = $k; $i < $this->rows; ++$i) {
$norm = $this->hypo($norm, $this->qrMatrix[$i][$k]);
}
if ($norm != 0.0) {
// Form k-th Householder vector.
if ($this->qrMatrix[$k][$k] < 0.0) {
$norm = -$norm;
}
for ($i = $k; $i < $this->rows; ++$i) {
$this->qrMatrix[$i][$k] /= $norm;
}
$this->qrMatrix[$k][$k] += 1.0;
// Apply transformation to remaining columns.
for ($j = $k + 1; $j < $this->columns; ++$j) {
$s = 0.0;
for ($i = $k; $i < $this->rows; ++$i) {
$s += $this->qrMatrix[$i][$k] * $this->qrMatrix[$i][$j];
}
$s = -$s / $this->qrMatrix[$k][$k];
for ($i = $k; $i < $this->rows; ++$i) {
$this->qrMatrix[$i][$j] += $s * $this->qrMatrix[$i][$k];
}
}
}
$this->rDiagonal[$k] = -$norm;
}
}
public function isFullRank(): bool
{
for ($j = 0; $j < $this->columns; ++$j) {
if ($this->rDiagonal[$j] == 0.0) {
return false;
}
}
return true;
}
/**
* Least squares solution of A*X = B.
*
* @param Matrix $B a Matrix with as many rows as A and any number of columns
*
* @throws Exception
*
* @return Matrix matrix that minimizes the two norm of Q*R*X-B
*/
public function solve(Matrix $B): Matrix
{
if ($B->rows !== $this->rows) {
throw new Exception('Matrix row dimensions are not equal');
}
if (!$this->isFullRank()) {
throw new Exception('Can only perform this operation on a full-rank matrix');
}
// Compute Y = transpose(Q)*B
$Y = $this->getQ()->transpose()
->multiply($B);
// Solve R*X = Y;
return $this->getR()->inverse()
->multiply($Y);
}
}

View File

@@ -0,0 +1,13 @@
<?php
/**
* Exception.
*
* @copyright Copyright (c) 2013-2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
class Div0Exception extends Exception
{
}

View File

@@ -4,6 +4,25 @@ namespace Matrix;
class Functions
{
/**
* Validates an array of matrix, converting an array to a matrix if required.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
private static function validateMatrix($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return $matrix;
}
/**
* Calculate the adjoint of the matrix
*
@@ -25,12 +44,14 @@ class Functions
* The adjugate has sometimes been called the "adjoint", but today the "adjoint" of a matrix normally refers
* to its corresponding adjoint operator, which is its conjugate transpose.
*
* @param Matrix $matrix The matrix whose adjoint we wish to calculate
* @param Matrix|array $matrix The matrix whose adjoint we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function adjoint(Matrix $matrix)
public static function adjoint($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Adjoint can only be calculated for a square matrix');
}
@@ -67,13 +88,15 @@ class Functions
/**
* Return the cofactors of this matrix
*
* @param Matrix $matrix The matrix whose cofactors we wish to calculate
* @param Matrix|array $matrix The matrix whose cofactors we wish to calculate
* @return Matrix
*
* @throws Exception
*/
public static function cofactors(Matrix $matrix)
public static function cofactors($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Cofactors can only be calculated for a square matrix');
}
@@ -141,12 +164,14 @@ class Functions
/**
* Return the determinant of this matrix
*
* @param Matrix $matrix The matrix whose determinant we wish to calculate
* @param Matrix|array $matrix The matrix whose determinant we wish to calculate
* @return float
* @throws Exception
**/
public static function determinant(Matrix $matrix)
public static function determinant($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Determinant can only be calculated for a square matrix');
}
@@ -157,12 +182,14 @@ class Functions
/**
* Return the diagonal of this matrix
*
* @param Matrix $matrix The matrix whose diagonal we wish to calculate
* @param Matrix|array $matrix The matrix whose diagonal we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function diagonal(Matrix $matrix)
public static function diagonal($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Diagonal can only be extracted from a square matrix');
}
@@ -181,12 +208,14 @@ class Functions
/**
* Return the antidiagonal of this matrix
*
* @param Matrix $matrix The matrix whose antidiagonal we wish to calculate
* @param Matrix|array $matrix The matrix whose antidiagonal we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function antidiagonal(Matrix $matrix)
public static function antidiagonal($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Anti-Diagonal can only be extracted from a square matrix');
}
@@ -207,12 +236,14 @@ class Functions
* The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix
* with ones on the main diagonal and zeros elsewhere
*
* @param Matrix $matrix The matrix whose identity we wish to calculate
* @param Matrix|array $matrix The matrix whose identity we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function identity(Matrix $matrix)
public static function identity($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Identity can only be created for a square matrix');
}
@@ -225,19 +256,21 @@ class Functions
/**
* Return the inverse of this matrix
*
* @param Matrix $matrix The matrix whose inverse we wish to calculate
* @param Matrix|array $matrix The matrix whose inverse we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function inverse(Matrix $matrix)
public static function inverse($matrix, string $type = 'inverse')
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Inverse can only be calculated for a square matrix');
throw new Exception(ucfirst($type) . ' can only be calculated for a square matrix');
}
$determinant = self::getDeterminant($matrix);
if ($determinant == 0.0) {
throw new Exception('Inverse can only be calculated for a matrix with a non-zero determinant');
throw new Div0Exception(ucfirst($type) . ' can only be calculated for a matrix with a non-zero determinant');
}
if ($matrix->rows == 1) {
@@ -281,12 +314,14 @@ class Functions
* calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of
* square matrices.
*
* @param Matrix $matrix The matrix whose minors we wish to calculate
* @param Matrix|array $matrix The matrix whose minors we wish to calculate
* @return Matrix
* @throws Exception
**/
public static function minors(Matrix $matrix)
public static function minors($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Minors can only be calculated for a square matrix');
}
@@ -299,12 +334,14 @@ class Functions
* The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right)
* of the matrix
*
* @param Matrix $matrix The matrix whose trace we wish to calculate
* @param Matrix|array $matrix The matrix whose trace we wish to calculate
* @return float
* @throws Exception
**/
public static function trace(Matrix $matrix)
public static function trace($matrix)
{
$matrix = self::validateMatrix($matrix);
if (!$matrix->isSquare()) {
throw new Exception('Trace can only be extracted from a square matrix');
}
@@ -321,11 +358,13 @@ class Functions
/**
* Return the transpose of this matrix
*
* @param Matrix $matrix The matrix whose transpose we wish to calculate
* @param Matrix|\a $matrix The matrix whose transpose we wish to calculate
* @return Matrix
**/
public static function transpose(Matrix $matrix)
public static function transpose($matrix)
{
$matrix = self::validateMatrix($matrix);
$array = array_values(array_merge([null], $matrix->toArray()));
$grid = call_user_func_array(
'array_map',

View File

@@ -10,6 +10,10 @@
namespace Matrix;
use Generator;
use Matrix\Decomposition\LU;
use Matrix\Decomposition\QR;
/**
* Matrix object.
*
@@ -24,7 +28,6 @@ namespace Matrix;
* @method Matrix diagonal()
* @method Matrix identity()
* @method Matrix inverse()
* @method Matrix pseudoInverse()
* @method Matrix minors()
* @method float trace()
* @method Matrix transpose()
@@ -33,6 +36,7 @@ namespace Matrix;
* @method Matrix multiply(...$matrices)
* @method Matrix divideby(...$matrices)
* @method Matrix divideinto(...$matrices)
* @method Matrix directsum(...$matrices)
*/
class Matrix
{
@@ -55,7 +59,7 @@ class Matrix
*
* @param array $grid
*/
protected function buildFromArray(array $grid)
protected function buildFromArray(array $grid): void
{
$this->rows = count($grid);
$columns = array_reduce(
@@ -86,7 +90,7 @@ class Matrix
* @return int
* @throws Exception
*/
public static function validateRow($row)
public static function validateRow(int $row): int
{
if ((!is_numeric($row)) || (intval($row) < 1)) {
throw new Exception('Invalid Row');
@@ -102,7 +106,7 @@ class Matrix
* @return int
* @throws Exception
*/
public static function validateColumn($column)
public static function validateColumn(int $column): int
{
if ((!is_numeric($column)) || (intval($column) < 1)) {
throw new Exception('Invalid Column');
@@ -118,7 +122,7 @@ class Matrix
* @return int
* @throws Exception
*/
protected function validateRowInRange($row)
protected function validateRowInRange(int $row): int
{
$row = static::validateRow($row);
if ($row > $this->rows) {
@@ -135,7 +139,7 @@ class Matrix
* @return int
* @throws Exception
*/
protected function validateColumnInRange($column)
protected function validateColumnInRange(int $column): int
{
$column = static::validateColumn($column);
if ($column > $this->columns) {
@@ -157,7 +161,7 @@ class Matrix
* @return static
* @throws Exception
*/
public function getRows($row, $rowCount = 1)
public function getRows(int $row, int $rowCount = 1): Matrix
{
$row = $this->validateRowInRange($row);
if ($rowCount === 0) {
@@ -179,7 +183,7 @@ class Matrix
* @return Matrix
* @throws Exception
*/
public function getColumns($column, $columnCount = 1)
public function getColumns(int $column, int $columnCount = 1): Matrix
{
$column = $this->validateColumnInRange($column);
if ($columnCount < 1) {
@@ -207,7 +211,7 @@ class Matrix
* @return static
* @throws Exception
*/
public function dropRows($row, $rowCount = 1)
public function dropRows(int $row, int $rowCount = 1): Matrix
{
$this->validateRowInRange($row);
if ($rowCount === 0) {
@@ -233,7 +237,7 @@ class Matrix
* @return static
* @throws Exception
*/
public function dropColumns($column, $columnCount = 1)
public function dropColumns(int $column, int $columnCount = 1): Matrix
{
$this->validateColumnInRange($column);
if ($columnCount < 1) {
@@ -260,7 +264,7 @@ class Matrix
* @return mixed
* @throws Exception
*/
public function getValue($row, $column)
public function getValue(int $row, int $column)
{
$row = $this->validateRowInRange($row);
$column = $this->validateColumnInRange($column);
@@ -270,11 +274,11 @@ class Matrix
/**
* Returns a Generator that will yield each row of the matrix in turn as a vector matrix
* or the value of each cell if the matrix is a vector
* or the value of each cell if the matrix is a column vector
*
* @return \Generator|Matrix[]|mixed[]
* @return Generator|Matrix[]|mixed[]
*/
public function rows()
public function rows(): Generator
{
foreach ($this->grid as $i => $row) {
yield $i + 1 => ($this->columns == 1)
@@ -285,11 +289,11 @@ class Matrix
/**
* Returns a Generator that will yield each column of the matrix in turn as a vector matrix
* or the value of each cell if the matrix is a vector
* or the value of each cell if the matrix is a row vector
*
* @return \Generator|Matrix[]|mixed[]
* @return Generator|Matrix[]|mixed[]
*/
public function columns()
public function columns(): Generator
{
for ($i = 0; $i < $this->columns; ++$i) {
yield $i + 1 => ($this->rows == 1)
@@ -304,9 +308,9 @@ class Matrix
*
* @return bool
*/
public function isSquare()
public function isSquare(): bool
{
return $this->rows == $this->columns;
return $this->rows === $this->columns;
}
/**
@@ -315,9 +319,9 @@ class Matrix
*
* @return bool
*/
public function isVector()
public function isVector(): bool
{
return $this->rows == 1 || $this->columns == 1;
return $this->rows === 1 || $this->columns === 1;
}
/**
@@ -325,11 +329,29 @@ class Matrix
*
* @return array
*/
public function toArray()
public function toArray(): array
{
return $this->grid;
}
/**
* Solve A*X = B.
*
* @param Matrix $B Right hand side
*
* @throws Exception
*
* @return Matrix ... Solution if A is square, least squares solution otherwise
*/
public function solve(Matrix $B): Matrix
{
if ($this->columns === $this->rows) {
return (new LU($this))->solve($B);
}
return (new QR($this))->solve($B);
}
protected static $getters = [
'rows',
'columns',
@@ -342,7 +364,7 @@ class Matrix
* @return mixed
* @throws Exception
*/
public function __get($propertyName)
public function __get(string $propertyName)
{
$propertyName = strtolower($propertyName);
@@ -355,8 +377,8 @@ class Matrix
}
protected static $functions = [
'antidiagonal',
'adjoint',
'antidiagonal',
'cofactors',
'determinant',
'diagonal',
@@ -384,16 +406,17 @@ class Matrix
* @return Matrix|float
* @throws Exception
*/
public function __call($functionName, $arguments)
public function __call(string $functionName, $arguments)
{
$functionName = strtolower(str_replace('_', '', $functionName));
if (in_array($functionName, self::$functions) || in_array($functionName, self::$operations)) {
$functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
if (is_callable($functionName)) {
$arguments = array_values(array_merge([$this], $arguments));
return call_user_func_array($functionName, $arguments);
}
// Test for function calls
if (in_array($functionName, self::$functions, true)) {
return Functions::$functionName($this, ...$arguments);
}
// Test for operation calls
if (in_array($functionName, self::$operations, true)) {
return Operations::$functionName($this, ...$arguments);
}
throw new Exception('Function or Operation does not exist');
}

View File

@@ -0,0 +1,157 @@
<?php
namespace Matrix;
use Matrix\Operators\Addition;
use Matrix\Operators\DirectSum;
use Matrix\Operators\Division;
use Matrix\Operators\Multiplication;
use Matrix\Operators\Subtraction;
class Operations
{
public static function add(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('Addition operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Addition arguments must be Matrix or array');
}
$result = new Addition($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
public static function directsum(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('DirectSum operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('DirectSum arguments must be Matrix or array');
}
$result = new DirectSum($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
public static function divideby(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('Division operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Division arguments must be Matrix or array');
}
$result = new Division($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
public static function divideinto(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('Division operation requires at least 2 arguments');
}
$matrix = array_pop($matrixValues);
$matrixValues = array_reverse($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Division arguments must be Matrix or array');
}
$result = new Division($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
public static function multiply(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('Multiplication operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Multiplication arguments must be Matrix or array');
}
$result = new Multiplication($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
public static function subtract(...$matrixValues): Matrix
{
if (count($matrixValues) < 2) {
throw new Exception('Subtraction operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Subtraction arguments must be Matrix or array');
}
$result = new Subtraction($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}
}

View File

@@ -14,7 +14,7 @@ class Addition extends Operator
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple additions to be chained
**/
public function execute($value)
public function execute($value): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
@@ -35,7 +35,7 @@ class Addition extends Operator
* @param mixed $value The numeric value to add to the current base value
* @return $this The operation object, allowing multiple additions to be chained
**/
protected function addScalar($value)
protected function addScalar($value): Operator
{
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
@@ -53,7 +53,7 @@ class Addition extends Operator
* @return $this The operation object, allowing multiple additions to be chained
* @throws Exception If the provided argument is not appropriate for the operation
**/
protected function addMatrix(Matrix $value)
protected function addMatrix(Matrix $value): Operator
{
$this->validateMatchingDimensions($value);

View File

@@ -14,7 +14,7 @@ class DirectSum extends Operator
* @return $this The operation object, allowing multiple additions to be chained
* @throws Exception If the provided argument is not appropriate for the operation
*/
public function execute($value)
public function execute($value): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
@@ -33,7 +33,7 @@ class DirectSum extends Operator
* @param Matrix $value The numeric value to concatenate/direct sum with the current base value
* @return $this The operation object, allowing multiple additions to be chained
**/
private function directSumMatrix($value)
private function directSumMatrix($value): Operator
{
$originalColumnCount = count($this->matrix[0]);
$originalRowCount = count($this->matrix);

View File

@@ -2,9 +2,10 @@
namespace Matrix\Operators;
use Matrix\Div0Exception;
use Matrix\Exception;
use \Matrix\Matrix;
use \Matrix\Functions;
use Matrix\Exception;
class Division extends Multiplication
{
@@ -15,22 +16,18 @@ class Division extends Multiplication
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple divisions to be chained
**/
public function execute($value)
public function execute($value, string $type = 'division'): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
}
if (is_object($value) && ($value instanceof Matrix)) {
try {
$value = Functions::inverse($value);
} catch (Exception $e) {
throw new Exception('Division can only be calculated using a matrix with a non-zero determinant');
}
$value = Functions::inverse($value, $type);
return $this->multiplyMatrix($value);
return $this->multiplyMatrix($value, $type);
} elseif (is_numeric($value)) {
return $this->multiplyScalar(1 / $value);
return $this->multiplyScalar(1 / $value, $type);
}
throw new Exception('Invalid argument for division');

View File

@@ -5,6 +5,7 @@ namespace Matrix\Operators;
use Matrix\Matrix;
use \Matrix\Builder;
use Matrix\Exception;
use Throwable;
class Multiplication extends Operator
{
@@ -15,19 +16,19 @@ class Multiplication extends Operator
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple multiplications to be chained
**/
public function execute($value)
public function execute($value, string $type = 'multiplication'): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
}
if (is_object($value) && ($value instanceof Matrix)) {
return $this->multiplyMatrix($value);
return $this->multiplyMatrix($value, $type);
} elseif (is_numeric($value)) {
return $this->multiplyScalar($value);
return $this->multiplyScalar($value, $type);
}
throw new Exception('Invalid argument for multiplication');
throw new Exception("Invalid argument for $type");
}
/**
@@ -36,12 +37,16 @@ class Multiplication extends Operator
* @param mixed $value The numeric value to multiply with the current base value
* @return $this The operation object, allowing multiple mutiplications to be chained
**/
protected function multiplyScalar($value)
protected function multiplyScalar($value, string $type = 'multiplication'): Operator
{
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
$this->matrix[$row][$column] *= $value;
try {
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
$this->matrix[$row][$column] *= $value;
}
}
} catch (Throwable $e) {
throw new Exception("Invalid argument for $type");
}
return $this;
@@ -54,7 +59,7 @@ class Multiplication extends Operator
* @return $this The operation object, allowing multiple mutiplications to be chained
* @throws Exception If the provided argument is not appropriate for the operation
**/
protected function multiplyMatrix(Matrix $value)
protected function multiplyMatrix(Matrix $value, string $type = 'multiplication'): Operator
{
$this->validateReflectingDimensions($value);
@@ -62,13 +67,17 @@ class Multiplication extends Operator
$newColumns = $value->columns;
$matrix = Builder::createFilledMatrix(0, $newRows, $newColumns)
->toArray();
for ($row = 0; $row < $newRows; ++$row) {
for ($column = 0; $column < $newColumns; ++$column) {
$columnData = $value->getColumns($column + 1)->toArray();
foreach ($this->matrix[$row] as $key => $valueData) {
$matrix[$row][$column] += $valueData * $columnData[$key][0];
try {
for ($row = 0; $row < $newRows; ++$row) {
for ($column = 0; $column < $newColumns; ++$column) {
$columnData = $value->getColumns($column + 1)->toArray();
foreach ($this->matrix[$row] as $key => $valueData) {
$matrix[$row][$column] += $valueData * $columnData[$key][0];
}
}
}
} catch (Throwable $e) {
throw new Exception("Invalid argument for $type");
}
$this->matrix = $matrix;

View File

@@ -46,7 +46,7 @@ abstract class Operator
* @param Matrix $matrix The second Matrix object on which the operation will be performed
* @throws Exception
*/
protected function validateMatchingDimensions(Matrix $matrix)
protected function validateMatchingDimensions(Matrix $matrix): void
{
if (($this->rows != $matrix->rows) || ($this->columns != $matrix->columns)) {
throw new Exception('Matrices have mismatched dimensions');
@@ -59,7 +59,7 @@ abstract class Operator
* @param Matrix $matrix The second Matrix object on which the operation will be performed
* @throws Exception
*/
protected function validateReflectingDimensions(Matrix $matrix)
protected function validateReflectingDimensions(Matrix $matrix): void
{
if ($this->columns != $matrix->rows) {
throw new Exception('Matrices have mismatched dimensions');
@@ -71,7 +71,7 @@ abstract class Operator
*
* @return Matrix
*/
public function result()
public function result(): Matrix
{
return new Matrix($this->matrix);
}

View File

@@ -14,7 +14,7 @@ class Subtraction extends Operator
* @throws Exception If the provided argument is not appropriate for the operation
* @return $this The operation object, allowing multiple subtractions to be chained
**/
public function execute($value)
public function execute($value): Operator
{
if (is_array($value)) {
$value = new Matrix($value);
@@ -35,7 +35,7 @@ class Subtraction extends Operator
* @param mixed $value The numeric value to subtracted from the current base value
* @return $this The operation object, allowing multiple additions to be chained
**/
protected function subtractScalar($value)
protected function subtractScalar($value): Operator
{
for ($row = 0; $row < $this->rows; ++$row) {
for ($column = 0; $column < $this->columns; ++$column) {
@@ -53,7 +53,7 @@ class Subtraction extends Operator
* @return $this The operation object, allowing multiple subtractions to be chained
* @throws Exception If the provided argument is not appropriate for the operation
**/
protected function subtractMatrix(Matrix $value)
protected function subtractMatrix(Matrix $value): Operator
{
$this->validateMatchingDimensions($value);

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix adjoint() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the adjoint of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function adjoint($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::adjoint($matrix);
}

View File

@@ -1,29 +0,0 @@
<?php
/**
*
* Function code for the matrix antidiagonal() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the antidiagonal of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function antidiagonal($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::antidiagonal($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix cofactors() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the cofactors of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function cofactors($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::cofactors($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix determinant() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the determinant of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return float Matrix determinant
* @throws Exception If argument isn't a valid matrix or array.
*/
function determinant($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::determinant($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix diagonal() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the diagonal of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function diagonal($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::diagonal($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix identity() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the identity of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The identity matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function identity($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::identity($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix inverse() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the inverse of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function inverse($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::inverse($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix minors() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the minors of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The new matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function minors($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::minors($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix trace() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the trace of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return float The trace of the matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function trace($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::trace($matrix);
}

View File

@@ -1,30 +0,0 @@
<?php
/**
*
* Function code for the matrix transpose() function
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
/**
* Returns the transpose of a matrix or an array.
*
* @param Matrix|array $matrix Matrix or an array to treat as a matrix.
* @return Matrix The transposed matrix
* @throws Exception If argument isn't a valid matrix or array.
*/
function transpose($matrix)
{
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Must be Matrix or array');
}
return Functions::transpose($matrix);
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix addition operation
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\Addition;
/**
* Adds two or more matrices
*
* @param array<int, mixed> $matrixValues The matrices to add
* @return Matrix
* @throws Exception
*/
function add(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('Addition operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Addition arguments must be Matrix or array');
}
$result = new Addition($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix direct sum operation
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\DirectSum;
/**
* Adds two or more matrices
*
* @param array<int, mixed> $matrixValues The matrices to add
* @return Matrix
* @throws Exception
*/
function directsum(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('DirectSum operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('DirectSum arguments must be Matrix or array');
}
$result = new DirectSum($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix division operation
*
* @copyright Copyright (c) 2013-2018 Mark Baker (https://github.com/MarkBaker/PHPComplex)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\Division;
/**
* Divides two or more matrix numbers
*
* @param array<int, mixed> $matrixValues The matrices to divide
* @return Matrix
* @throws Exception
*/
function divideby(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('Division operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Division arguments must be Matrix or array');
}
$result = new Division($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix division operation
*
* @copyright Copyright (c) 2013-2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\Division;
/**
* Divides two or more matrix numbers
*
* @param array<int, mixed> $matrixValues The numbers to divide
* @return Matrix
* @throws Exception
*/
function divideinto(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('Division operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Division arguments must be Matrix or array');
}
$result = new Division($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix multiplication operation
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\Multiplication;
/**
* Multiplies two or more matrices
*
* @param array<int, mixed> $matrixValues The matrices to multiply
* @return Matrix
* @throws Exception
*/
function multiply(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('Multiplication operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Multiplication arguments must be Matrix or array');
}
$result = new Multiplication($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -1,44 +0,0 @@
<?php
/**
*
* Function code for the matrix subtraction operation
*
* @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
* @license https://opensource.org/licenses/MIT MIT
*/
namespace Matrix;
use Matrix\Operators\Subtraction;
/**
* Subtracts two or more matrices
*
* @param array<int, mixed> $matrixValues The matrices to subtract
* @return Matrix
* @throws Exception
*/
function subtract(...$matrixValues)
{
if (count($matrixValues) < 2) {
throw new Exception('Subtraction operation requires at least 2 arguments');
}
$matrix = array_shift($matrixValues);
if (is_array($matrix)) {
$matrix = new Matrix($matrix);
}
if (!$matrix instanceof Matrix) {
throw new Exception('Subtraction arguments must be Matrix or array');
}
$result = new Subtraction($matrix);
foreach ($matrixValues as $matrix) {
$result->execute($matrix);
}
return $result->result();
}

View File

@@ -0,0 +1,52 @@
{
"name": "markbaker/matrix",
"type": "library",
"description": "PHP Class for working with matrices",
"keywords": ["matrix", "vector", "mathematics"],
"homepage": "https://github.com/MarkBaker/PHPMatrix",
"license": "MIT",
"authors": [
{
"name": "Mark Baker",
"email": "mark@demon-angel.eu"
}
],
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"phpdocumentor/phpdocumentor": "2.*",
"phpmd/phpmd": "2.*",
"sebastian/phpcpd": "^4.0",
"phploc/phploc": "^4.0",
"squizlabs/php_codesniffer": "^3.7",
"phpcompatibility/php-compatibility": "^9.3",
"dealerdirect/phpcodesniffer-composer-installer": "dev-master"
},
"autoload": {
"psr-4": {
"Matrix\\": "classes/src/"
}
},
"autoload-dev": {
"psr-4": {
"MatrixTest\\": "unitTests/classes/src/"
}
},
"scripts": {
"style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full classes/src/ unitTests/classes/src -n",
"test": "phpunit -c phpunit.xml.dist",
"mess": "phpmd classes/src/ xml codesize,unusedcode,design,naming -n",
"lines": "phploc classes/src/ -n",
"cpd": "phpcpd classes/src/ -n",
"versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full classes/src/ --runtime-set testVersion 7.2- -n",
"coverage": "phpunit -c phpunit.xml.dist --coverage-text --coverage-html ./build/coverage"
},
"minimum-stability": "dev",
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Matrix\Matrix;
use Matrix\Decomposition\QR;
include __DIR__ . '/../vendor/autoload.php';
$grid = [
[0, 1],
[-1, 0],
];
$targetGrid = [
[-1],
[2],
];
$matrix = new Matrix($grid);
$target = new Matrix($targetGrid);
$decomposition = new QR($matrix);
$X = $decomposition->solve($target);
echo 'X', PHP_EOL;
var_export($X->toArray());
echo PHP_EOL;
$resolve = $matrix->multiply($X);
echo 'Resolve', PHP_EOL;
var_export($resolve->toArray());
echo PHP_EOL;

View File

@@ -0,0 +1,17 @@
{
"timeout": 1,
"source": {
"directories": [
"classes\/src"
]
},
"logs": {
"text": "build/infection/text.log",
"summary": "build/infection/summary.log",
"debug": "build/infection/debug.log",
"perMutator": "build/infection/perMutator.md"
},
"mutators": {
"@default": true
}
}

View File

@@ -0,0 +1,25 @@
The MIT License (MIT)
=====================
Copyright © `2018` `Mark Baker`
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,6 @@
parameters:
ignoreErrors:
- '#Property [A-Za-z\\]+::\$[A-Za-z]+ has no typehint specified#'
- '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has no return typehint specified#'
- '#Method [A-Za-z\\]+::[A-Za-z]+\(\) has parameter \$[A-Za-z0-9]+ with no typehint specified#'
checkMissingIterableValueType: false