mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 17:58:41 +00:00
todos fixed
This commit is contained in:
parent
99211861eb
commit
567ab3bfcd
|
|
@ -110,7 +110,7 @@ final class Kmeans
|
|||
* Generate the clusters of the points
|
||||
*
|
||||
* @param PointInterface[] $points Points to cluster
|
||||
* @param int<0, max> $clusters Amount of clusters
|
||||
* @param int<1, max> $clusters Amount of clusters
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
|
|
@ -140,8 +140,7 @@ final class Kmeans
|
|||
|
||||
foreach ($clusterCenters as $center) {
|
||||
for ($i = 0; $i < $coordinates; ++$i) {
|
||||
// @todo Invalid center coodinate value in like 5 % of the runs
|
||||
$center->setCoordinate($i, $center->getCoordinate($i) / ($center->group === 0 ? 1 : $center->group));
|
||||
$center->setCoordinate($i, $center->getCoordinate($i) / $center->group);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +148,7 @@ final class Kmeans
|
|||
foreach ($points as $point) {
|
||||
$min = $this->nearestClusterCenter($point, $clusterCenters)[0];
|
||||
|
||||
if ($min !== $point->group) {
|
||||
if ($clusters !== $point->group) {
|
||||
++$changed;
|
||||
$point->group = $min;
|
||||
}
|
||||
|
|
@ -208,29 +207,40 @@ final class Kmeans
|
|||
private function kpp(array $points, int $n) : array
|
||||
{
|
||||
$clusters = [clone $points[\array_rand($points, 1)]];
|
||||
$d = \array_fill(0, $n, 0.0);
|
||||
|
||||
$d = \array_fill(0, $n, 0.0);
|
||||
|
||||
for ($i = 1; $i < $n; ++$i) {
|
||||
$sum = 0;
|
||||
|
||||
foreach ($points as $key => $point) {
|
||||
$d[$key] = $this->nearestClusterCenter($point, \array_slice($clusters, 0, 5))[1];
|
||||
$d[$key] = $this->nearestClusterCenter($point, $clusters)[1];
|
||||
$sum += $d[$key];
|
||||
}
|
||||
|
||||
$sum *= \mt_rand(0, \mt_getrandmax()) / \mt_getrandmax();
|
||||
|
||||
$found = false;
|
||||
foreach ($d as $key => $di) {
|
||||
$sum -= $di;
|
||||
|
||||
if ($sum <= 0) {
|
||||
$clusters[$i] = clone $points[$key];
|
||||
// The in array check is important to avoid duplicate cluster centers
|
||||
if ($sum <= 0 && !\in_array($c = $points[$key], $clusters)) {
|
||||
$clusters[$i] = clone $c;
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
||||
while (!$found) {
|
||||
if (!\in_array($c = $points[\array_rand($points)], $clusters)) {
|
||||
$clusters[$i] = clone $c;
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($points as $point) {
|
||||
$point->group = ($this->nearestClusterCenter($point, $clusters)[0]);
|
||||
$point->group = $this->nearestClusterCenter($point, $clusters)[0];
|
||||
}
|
||||
|
||||
return $clusters;
|
||||
|
|
|
|||
|
|
@ -21,9 +21,159 @@ namespace phpOMS\Algorithm\Graph;
|
|||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo Implement
|
||||
*/
|
||||
final class MarkovChain
|
||||
{
|
||||
/**
|
||||
* Order of the markov chain
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private int $order = 1;
|
||||
|
||||
/**
|
||||
* Trained data
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private array $data = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int $order Order of the markov chain
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct(int $order = 1)
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create markov chain based on input
|
||||
*
|
||||
* @param array $values Training values
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function train(array $values) : void
|
||||
{
|
||||
$temp = [];
|
||||
$length = \count($values) - $this->order;
|
||||
|
||||
$unique = \array_unique($values);
|
||||
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$key = [];
|
||||
for ($j = 0; $j < $this->order; ++$j) {
|
||||
$key[] = $values[$i + $j];
|
||||
}
|
||||
|
||||
$keyString = \implode(' ', $key);
|
||||
|
||||
if (!isset($temp[$keyString])) {
|
||||
foreach ($unique as $value) {
|
||||
$temp[$keyString][$value] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
++$temp[$keyString][$values[$i + 1]];
|
||||
}
|
||||
|
||||
foreach ($temp as $key => $values) {
|
||||
$sum = \array_sum($values);
|
||||
foreach ($values as $idx => $value) {
|
||||
$this->data[$key][$idx] = $value / $sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set training data
|
||||
*
|
||||
* @param array<array<int, int>> $values Training values
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setTraining(array $values) : void
|
||||
{
|
||||
$this->data = $values;
|
||||
}
|
||||
|
||||
public function generate(int $length, array $start = null) : array
|
||||
{
|
||||
$orderKeys = \array_keys($this->data);
|
||||
$orderValues = \array_keys(\reset($this->data));
|
||||
|
||||
$output = $start ?? \explode(' ', $orderKeys[\array_rand($orderKeys)]);
|
||||
$key = $output;
|
||||
|
||||
for ($i = $this->order; $i < $length; ++$i) {
|
||||
$keyString = \implode(' ', $key);
|
||||
|
||||
$prob = \mt_rand(1, 100) / 100;
|
||||
$cProb = 0.0;
|
||||
$found = false;
|
||||
$val = null;
|
||||
|
||||
foreach (($this->data[$keyString] ?? []) as $val => $p) {
|
||||
$cProb += $p;
|
||||
|
||||
if ($prob <= $cProb) {
|
||||
$new = $val;
|
||||
$found = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find possible key
|
||||
if (!$found) {
|
||||
$new = $orderValues[\array_rand($orderValues)];
|
||||
}
|
||||
|
||||
$output[] = $new;
|
||||
$key[] = $new;
|
||||
|
||||
\array_shift($key);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function pathProbability(array $path) : float
|
||||
{
|
||||
$length = \count($path);
|
||||
if ($length <= $this->order) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$key = \array_slice($path, 0, $this->order);
|
||||
|
||||
$prob = 1.0;
|
||||
for ($i = $this->order; $i < $length; ++$i) {
|
||||
$prob *= $this->data[\implode($key)][$path[$i]] ?? 0.0;
|
||||
|
||||
$key[] = $path[$i];
|
||||
\array_shift($key);
|
||||
}
|
||||
|
||||
return $prob;
|
||||
}
|
||||
|
||||
public function stepProbability(array $state, mixed $next) : float
|
||||
{
|
||||
if (\count($state) !== $this->order) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return $this->data[\implode(' ', $state)][$next] ?? 0.0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ use phpOMS\Math\Solver\Root\Bisection;
|
|||
* @see https://en.wikipedia.org/wiki/Glicko_rating_system
|
||||
* @see http://www.glicko.net/glicko/glicko2.pdf
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo: implement
|
||||
*/
|
||||
final class Glicko2
|
||||
{
|
||||
|
|
|
|||
|
|
@ -695,11 +695,14 @@ class DataMapperFactory
|
|||
}
|
||||
|
||||
if ($count > $pageLimit) {
|
||||
if (!$hasNext) { // @todo: can be maybe removed?
|
||||
// @todo: can be maybe removed?
|
||||
/*
|
||||
if (!$hasNext) {
|
||||
\array_pop($data);
|
||||
$hasNext = true;
|
||||
--$count;
|
||||
}
|
||||
*/
|
||||
|
||||
if ($count > $pageLimit) {
|
||||
$hasPrevious = true;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ final class ReadMapper extends DataMapperAbstract
|
|||
// Get remaining objects (not available in memory cache) or remaining where clauses.
|
||||
//$dbData = $this->executeGetRaw($query);
|
||||
|
||||
$ids = [];
|
||||
foreach ($this->executeGetRawYield($query) as $row) {
|
||||
if ($row === []) {
|
||||
continue;
|
||||
|
|
@ -266,6 +267,15 @@ final class ReadMapper extends DataMapperAbstract
|
|||
$obj[$value] = $this->mapper::createBaseModel($row);
|
||||
|
||||
$obj[$value] = $this->populateAbstract($row, $obj[$value]);
|
||||
|
||||
$ids[] = $value;
|
||||
|
||||
// @todo: This is too slow, since it creates a query for every $row x relation type.
|
||||
// Pulling it out would be nice.
|
||||
// The problem with solving this is that in a many-to-many relationship a relation table is used
|
||||
// BUT the relation data is not available in the object itself meaning after retrieving the object
|
||||
// it cannot get assigned to the correct parent object.
|
||||
// Other relation types are easy because either the parent or child object contain the relation info.
|
||||
$this->loadHasManyRelations($obj[$value]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ use phpOMS\DataStorage\Database\Query\Where;
|
|||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo Karaka/phpOMS#33
|
||||
* Implement missing grammar & builder functions
|
||||
* Missing elements are e.g. sum, merge etc.
|
||||
*/
|
||||
class Grammar extends GrammarAbstract
|
||||
{
|
||||
|
|
|
|||
109
Math/Geometry/ConvexHull/GrahamScan.php
Normal file
109
Math/Geometry/ConvexHull/GrahamScan.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package phpOMS\Math\Geometry\ConvexHull
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Math\Geometry\ConvexHull;
|
||||
|
||||
/**
|
||||
* Andrew's monotone chain convex hull algorithm class.
|
||||
*
|
||||
* @package phpOMS\Math\Geometry\ConvexHull
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class GrahamScan
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create convex hull
|
||||
*
|
||||
* @param array<int, array{x:int|float, y:int|float}> $points Points (Point Cloud)
|
||||
*
|
||||
* @return array<int, array{x:int|float, y:int|float}>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function createConvexHull(array $points) : array
|
||||
{
|
||||
$count = \count($points);
|
||||
|
||||
if ($count < 3) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$min = 1;
|
||||
$points = \array_merge([null], $points);
|
||||
|
||||
for ($i = 2; $i < $count; ++$i) {
|
||||
if ($points[$i]['y'] < $points[$min]['y'] || ($points[$i]['y'] == $points[$min]['y'] && $points[$i]['x'] < $points[$min]['x'])) {
|
||||
$min = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$temp = $points[1];
|
||||
$points[1] = $points[$min];
|
||||
$points[$min] = $temp;
|
||||
|
||||
$c = $points[1];
|
||||
|
||||
$subpoints = \array_slice($points, 2, $count);
|
||||
\usort($subpoints, function (array $a, array $b) use ($c) : bool
|
||||
{
|
||||
return atan2($a['y'] - $c['y'], $a['x'] - $c['x']) < atan2( $b['y'] - $c['y'], $b['x'] - $c['x']);
|
||||
}
|
||||
);
|
||||
|
||||
$points = \array_merge([$points[0], $points[1]], $subpoints);
|
||||
$points[0] = $points[$count];
|
||||
|
||||
$size = 1;
|
||||
for ($i = 2; $i <= $count; ++$i) {
|
||||
while (self::ccw($points[$size - 1], $points[$size], $points[$i]) <= 0) {
|
||||
if ($size > 1) {
|
||||
--$size;
|
||||
} elseif ($i === $count) {
|
||||
break;
|
||||
} else {
|
||||
++$i;
|
||||
}
|
||||
}
|
||||
|
||||
$temp = $points[$i];
|
||||
$points[$size + 1] = $points[$i];
|
||||
$points[$i] = $points[$size + 1];
|
||||
++$size;
|
||||
}
|
||||
|
||||
$hull = [];
|
||||
for ($i = 1; $i <= $size; ++$i) {
|
||||
$hull[] = $points[$i];
|
||||
}
|
||||
|
||||
return $hull;
|
||||
}
|
||||
|
||||
public static function ccw(array $a, array $b, array $c)
|
||||
{
|
||||
return (($b['x'] - $a['x']) * ($c['y'] - $a['y']) - ($b['y'] - $a['y']) * ($c['x'] - $a['x']));
|
||||
}
|
||||
}
|
||||
|
|
@ -25,200 +25,210 @@ namespace phpOMS\Math\Optimization;
|
|||
*/
|
||||
class Simplex
|
||||
{
|
||||
private array $function = [];
|
||||
private int $m = 0;
|
||||
private int $n = 0;
|
||||
|
||||
private string $functionType = '';
|
||||
private array $A = [];
|
||||
|
||||
private int|float $functionLimit = 0.0;
|
||||
private array $b = [];
|
||||
|
||||
private array $constraints = [];
|
||||
private array $c = [];
|
||||
|
||||
private array $constraintsType = [];
|
||||
private int $v = 0;
|
||||
|
||||
private array $constraintsLimit = [];
|
||||
private array $Basic = [];
|
||||
|
||||
private array $slackForm = [];
|
||||
private array $Nonbasic = [];
|
||||
|
||||
private array $nonbasicSolution = [];
|
||||
|
||||
private array $basicSolution = [];
|
||||
|
||||
/**
|
||||
* Define the function to optimize
|
||||
*
|
||||
* @param array $function Function to optimize
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setFunction(array $function) : void
|
||||
private function pivot (int $x, int $y)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Add function constraint
|
||||
*
|
||||
* @param array $function Constraint function
|
||||
* @param string $type Constraint type
|
||||
* @param float $limit Constraint
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function addConstraint(array $function, string $type, float $limit) : void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Pivot element
|
||||
*
|
||||
* @param int $x X-Pivot
|
||||
* @param int $y Y-Pivot
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function pivot(int $x, int $y) : void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform simplex iteration
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function iterateSimplex() : void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize simplex algorithm
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function initialize() : bool
|
||||
{
|
||||
$k = -1;
|
||||
$minLimit = -1;
|
||||
|
||||
$m = \count($this->constraints);
|
||||
$n = \count($this->function);
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
if ($k === -1 || $this->constraintsLimit[$i] < $minLimit) {
|
||||
$k = $i;
|
||||
$minLimit = $this->constraintsLimit[$i];
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($j !== $y) {
|
||||
$this->A[$x][$j] /= -$this->A[$x][$y];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->constraintsLimit[$k] >= 0) {
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
$this->nonbasicSolution[$j] = $j;
|
||||
$this->b[$x] /= -$this->A[$x][$y];
|
||||
$this->A[$x][$y] = 1.0 / $this->A[$x][$y];
|
||||
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($i !== $x) {
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($j !== $y) {
|
||||
$this->A[$i][$j] += $this->A[$i][$y] * $this->A[$x][$j];
|
||||
}
|
||||
}
|
||||
|
||||
$this->b[$i] += $this->A[$i][$y] / $this->b[$x];
|
||||
$this->A[$i][$y] *= $this->A[$x][$y];
|
||||
}
|
||||
}
|
||||
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($j !== $y) {
|
||||
$this->c[$j] += $this->c[$y] * $this->A[$x][$j];
|
||||
}
|
||||
}
|
||||
|
||||
$this->v += $this->c[$y] * $this->b[$x];
|
||||
$this->c[$y] *= $this->A[$x][$y];
|
||||
|
||||
$temp = $this->Basic[$x];
|
||||
$this->Basic[$x] = $this->Nonbasic[$y];
|
||||
$this->Nonbasic[$y] = $temp;
|
||||
}
|
||||
|
||||
private function iterate() : int
|
||||
{
|
||||
$ind = -1;
|
||||
$best = -1;
|
||||
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($this->c[$j] > 0) {
|
||||
if ($best === -1 || $this->Nonbasic[$j] < $ind) {
|
||||
$ind = $this->Nonbasic[$j];
|
||||
$best = $j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ind === -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$maxConstraint = \INF;
|
||||
$bestConstraint = -1;
|
||||
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($this->A[$i][$best] < 0) {
|
||||
$currentConstraint = -$this->b[$i] / $this->A[$i][$best];
|
||||
if ($currentConstraint < $maxConstraint) {
|
||||
$maxConstraint = $currentConstraint;
|
||||
$bestConstraint = $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($maxConstraint === \INF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$this->pivot($bestConstraint, $best);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function initialize() : int
|
||||
{
|
||||
$k = -1;
|
||||
$minB = -1;
|
||||
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($k === -1 || $this->b[$i] < $minB) {
|
||||
$k = $i;
|
||||
$minB = $this->b[$i];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->b[$k] >= 0) {
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
$this->Nonbasic[$j] = $j;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
$this->basicSolution[$i] = $n + $i;
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
$this->Basic[$i] = $this->n + $i;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Auxiliary LP
|
||||
++$n;
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
$this->nonbasicSolution[$j] = $j;
|
||||
++$this->n;
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
$this->Nonbasic[$j] = $j;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
$this->basicSolution[$i] = $n + $i;
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
$this->Basic[$i] = $this->n + $i;
|
||||
}
|
||||
|
||||
$oldFunction = $this->function;
|
||||
$oldLimit = $this->functionLimit;
|
||||
|
||||
// Auxiliary function
|
||||
$this->function[$n - 1] = -1;
|
||||
$this->functionLimit = 0;
|
||||
|
||||
for ($j = 0; $j < $n - 1; ++$j) {
|
||||
$this->function[$j] = 0;
|
||||
$oldC = [];
|
||||
for ($j = 0; $j < $this->n - 1; ++$j) {
|
||||
$oldC[$j] = $this->c[$j];
|
||||
}
|
||||
|
||||
// Auxiliary constraints
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
$this->constraints[$i][$n - 1] = 1;
|
||||
$oldV = $this->v;
|
||||
|
||||
$this->c[$this->n - 1] = -1;
|
||||
for ($j = 0; $j < $this->n - 1; ++$j) {
|
||||
$this->c[$j] = 0;
|
||||
}
|
||||
|
||||
$this->pivot($k, $n - 1);
|
||||
$this->v = 0;
|
||||
|
||||
// Solve Auxiliary LP
|
||||
while ($this->iterateSimplex());
|
||||
|
||||
if ($this->functionLimit !== 0) {
|
||||
return false;
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
$this->A[$i][$this->n - 1] = 1;
|
||||
}
|
||||
|
||||
$zBasic = -1;
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
if ($this->basicSolution[$i] === $n - 1) {
|
||||
$zBasic = $i;
|
||||
$this->pivot($k, $this->n - 1);
|
||||
|
||||
while (!$this->iterate());
|
||||
|
||||
if ($this->v !== 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$basicZ = -1;
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($this->Basic[$i] === $this->n - 1) {
|
||||
$basicZ = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($zBasic === -1) {
|
||||
$this->pivot($zBasic, $n - 1);
|
||||
if ($basicZ !== -1) {
|
||||
$this->pivot($basicZ, $this->n - 1);
|
||||
}
|
||||
|
||||
$zNonBasic = -1;
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
if ($this->nonbasicSolution[$j] === $n - 1) {
|
||||
$zNonBasic = $j;
|
||||
$nonbasicZ = -1;
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($this->Nonbasic[$j] === $this->n - 1) {
|
||||
$nonbasicZ = $j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
$this->constraints[$i][$zNonBasic] = $this->constraints[$i][$n - 1];
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
$this->A[$i][$nonbasicZ] = $this->A[$i][$this->n - 1];
|
||||
}
|
||||
|
||||
$tmp = $this->nonbasicSolution[$n - 1];
|
||||
$this->nonbasicSolution[$n - 1] = $this->nonbasicSolution[$zNonBasic];
|
||||
$this->nonbasicSolution[$zNonBasic] = $tmp;
|
||||
$temp = $this->Nonbasic[$nonbasicZ];
|
||||
$this->Nonbasic[$nonbasicZ] = $this->Nonbasic[$this->n - 1];
|
||||
$this->Nonbasic[$this->n - 1] = $temp;
|
||||
|
||||
--$n;
|
||||
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
if ($this->nonbasicSolution[$j] > $n) {
|
||||
--$this->nonbasicSolution[$j];
|
||||
--$this->n;
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
if ($this->Nonbasic[$j] > $this->n) {
|
||||
--$this->Nonbasic[$j];
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
if ($this->basicSolution[$i] > $n) {
|
||||
--$this->basicSolution[$i];
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($this->Basic[$i] > $this->n) {
|
||||
--$this->Basic[$i];
|
||||
}
|
||||
}
|
||||
|
||||
$this->functionLimit = $oldLimit;
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
$this->function[$j] = 0;
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
$this->c[$j] = 0;
|
||||
}
|
||||
|
||||
for ($j = 0; $j < $n; ++$j) {
|
||||
$this->v = $oldV;
|
||||
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
$ok = false;
|
||||
|
||||
for ($jj = 0; $jj < $n; ++$jj) {
|
||||
if ($j === $this->nonbasicSolution[$jj]) {
|
||||
$this->function[$jj] += $oldFunction[$j];
|
||||
|
||||
for ($k = 0; $k < $this->n; ++$k) {
|
||||
if ($j = $this->Nonbasic[$k]) {
|
||||
$this->c[$k] += $oldC[$j];
|
||||
$ok = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -228,32 +238,52 @@ class Simplex
|
|||
continue;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $m; ++$i) {
|
||||
if ($j = $this->basicSolution[$i]) {
|
||||
for ($jj = 0; $jj < $n; ++$jj) {
|
||||
$this->function[$jj] += $oldFunction[$j] * $this->constraints[$i][$jj];
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
if ($j === $this->Basic[$i]) {
|
||||
for ($k = 0; $k < $this->n; ++$k) {
|
||||
$this->c[$k] = $oldC[$j] * $this->A[$i][$k];
|
||||
}
|
||||
|
||||
$this->functionLimit += $oldFunction[$j] * $this->constraintsLimit[$i];
|
||||
$this->v += $oldC[$j] * $this->b[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the optimization
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function solve() : array
|
||||
public function solve(array $A, array $b, array $c)
|
||||
{
|
||||
if (!$this->initialize()) {
|
||||
return [];
|
||||
$this->A = $A;
|
||||
$this->b = $b;
|
||||
$this->c = $c;
|
||||
|
||||
// @todo: createSlackForm() required?
|
||||
|
||||
$this->m = \count($A);
|
||||
$this->n = \count(\reset($A));
|
||||
|
||||
if ($this->initialize() === -1) {
|
||||
return [\array_fill(0, $this->m + $this->n, -2), \INF];
|
||||
}
|
||||
|
||||
$code = 0;
|
||||
while (!($code = $this->iterate()));
|
||||
|
||||
if ($code === -1) {
|
||||
return [\array_fill(0, $this->m + $this->n, -1), \INF];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
for ($j = 0; $j < $this->n; ++$j) {
|
||||
$result[$this->Nonbasic[$j]] = 0;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $this->m; ++$i) {
|
||||
$result[$this->Basic[$i]] = $this->b[$i];
|
||||
}
|
||||
|
||||
return [$result, $this->v];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ final class HttpResponse extends ResponseAbstract implements RenderableInterface
|
|||
}
|
||||
}
|
||||
|
||||
/** @var array{0:bool} $data */
|
||||
return $this->getRaw($data[0] ?? false);
|
||||
return $this->getRaw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ final class Rest
|
|||
\curl_setopt($curl, \CURLOPT_POST, 1);
|
||||
|
||||
// handle different content types
|
||||
$contentType = $requestHeaders['content-type'] ?? [];
|
||||
$contentType = $requestHeaders['Content-Type'] ?? [];
|
||||
if (empty($contentType) || \in_array(MimeType::M_POST, $contentType)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
\curl_setopt($curl, \CURLOPT_POSTFIELDS, \http_build_query($request->data));
|
||||
|
|
@ -94,7 +94,7 @@ final class Rest
|
|||
|
||||
// @todo: Replace boundary/ with the correct boundary= in the future.
|
||||
// Currently this cannot be done due to a bug. If we do it now the server cannot correclty populate php://input
|
||||
$headers['content-type'] = 'Content-Type: multipart/form-data; boundary/' . $boundary;
|
||||
$headers['Content-Type'] = 'Content-Type: multipart/form-data; boundary/' . $boundary;
|
||||
$headers['content-length'] = 'Content-Length: ' . \strlen($data);
|
||||
|
||||
\curl_setopt($curl, \CURLOPT_HTTPHEADER, $headers);
|
||||
|
|
@ -145,7 +145,7 @@ final class Rest
|
|||
\curl_close($curl);
|
||||
|
||||
$raw = \substr(\is_bool($result) ? '' : $result, $len === false ? 0 : $len);
|
||||
if (\stripos(\implode('', $response->header->get('content-type')), MimeType::M_JSON) !== false) {
|
||||
if (\stripos(\implode('', $response->header->get('Content-Type')), MimeType::M_JSON) !== false) {
|
||||
$temp = \json_decode($raw, true);
|
||||
if (!\is_array($temp)) {
|
||||
$temp = [];
|
||||
|
|
|
|||
|
|
@ -2277,7 +2277,7 @@ class Email implements MessageInterface
|
|||
'subject',
|
||||
'reply-to',
|
||||
'message-id',
|
||||
'content-type',
|
||||
'Content-Type',
|
||||
'mime-version',
|
||||
'x-mailer',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -545,7 +545,6 @@ abstract class ModuleAbstract
|
|||
mixed $obj
|
||||
) : void
|
||||
{
|
||||
// @todo: consider to set different status code? (also for other createInvalid() functions)
|
||||
$response->header->set('Content-Type', MimeType::M_JSON . '; charset=utf-8', true);
|
||||
$response->data[$request->uri->__toString()] = [
|
||||
'status' => NotificationLevel::WARNING,
|
||||
|
|
@ -719,8 +718,6 @@ abstract class ModuleAbstract
|
|||
*
|
||||
* @return void
|
||||
*
|
||||
* @todo find a way to offload this to the cli in a different process (same for other similar functions)
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function createModel(int $account, mixed $obj, string | \Closure $mapper, string $trigger, string $ip) : void
|
||||
|
|
|
|||
210
Stdlib/Tree/BinarySearchTree.php
Normal file
210
Stdlib/Tree/BinarySearchTree.php
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package phpOMS\Stdlib\Tree
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Stdlib\Tree;
|
||||
|
||||
/**
|
||||
* Binary search tree.
|
||||
*
|
||||
* @package phpOMS\Stdlib\Tree
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class BinarySearchTree
|
||||
{
|
||||
public ?Node $root = null;
|
||||
|
||||
public function __construct(Node $root = null)
|
||||
{
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
public function search(mixed $data) : ?Node
|
||||
{
|
||||
if ($this->root === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$comparison = $this->root->compare($data);
|
||||
|
||||
if ($comparison > 0) {
|
||||
return $this->root->left->search($data);
|
||||
} elseif ($comparison < 0) {
|
||||
return $this->root->right->search($data);
|
||||
}
|
||||
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
public function minimum() : ?Node
|
||||
{
|
||||
if ($this->root === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->root->left === null) {
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
return $this->root->left->minimum();
|
||||
}
|
||||
|
||||
public function maximum() : ?Node
|
||||
{
|
||||
if ($this->root === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->root->right === null) {
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
return $this->root->right->minimum();
|
||||
}
|
||||
|
||||
public function predecessor(Node $node) : ?Node
|
||||
{
|
||||
if ($node->left !== null) {
|
||||
return $node->left->maximum();
|
||||
}
|
||||
|
||||
$top = $node->parent;
|
||||
while ($top !== Null && $top->compare($node->data)) {
|
||||
$node = $top;
|
||||
$top = $top->parent;
|
||||
}
|
||||
|
||||
return $top;
|
||||
}
|
||||
|
||||
public function successor(Node $node) : ?Node
|
||||
{
|
||||
if ($node->right !== null) {
|
||||
return $node->right->minimum();
|
||||
}
|
||||
|
||||
$top = $node->parent;
|
||||
while ($top !== null && $top->compare($node->data)) {
|
||||
$node = $top;
|
||||
$top = $top->parent;
|
||||
}
|
||||
|
||||
return $top;
|
||||
}
|
||||
|
||||
public function insert(Node $node) : void
|
||||
{
|
||||
if ($this->root === null) {
|
||||
$new = new Node($node->key, $node->data);
|
||||
$new->parent = null;
|
||||
$new->tree = $this;
|
||||
|
||||
$this->root = $new;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$current = $this->root;
|
||||
while (true) {
|
||||
$comparison = $node->compare($current->data);
|
||||
|
||||
if ($comparison < 0) {
|
||||
if ($current->left === null) {
|
||||
$BST = new BinarySearchTree();
|
||||
$new = new Node($node->key, $node->data);
|
||||
$new->parent = $current;
|
||||
$new->tree = $BST;
|
||||
|
||||
$BST->root = $new;
|
||||
$current->left = $BST;
|
||||
} else {
|
||||
$current = $current->left->root;
|
||||
}
|
||||
} elseif ($comparison > 0) {
|
||||
if ($current->right === null) {
|
||||
$BST = new BinarySearchTree();
|
||||
$new = new Node($node->key, $node->data);
|
||||
$new->parent = $current;
|
||||
$new->tree = $BST;
|
||||
|
||||
$BST->root = $new;
|
||||
$current->right = $BST;
|
||||
} else {
|
||||
$current = $current->right->root;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Node &$node) : void
|
||||
{
|
||||
if ($node->left === null && $node->right === null) {
|
||||
if ($node->parent !== null) {
|
||||
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||
$node->parent->left = null;
|
||||
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node) === 0) {
|
||||
$node->parent->right = null;
|
||||
}
|
||||
}
|
||||
|
||||
$node = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$temp = null;
|
||||
if ($node->left === null) {
|
||||
$temp = $node->right->root;
|
||||
if ($node->parent !== null) {
|
||||
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||
$node->parent->left = $temp->tree;
|
||||
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node->data) === 0) {
|
||||
$node->parent->right = $temp->tree;
|
||||
}
|
||||
}
|
||||
|
||||
$temp->parent = $node->parent;
|
||||
|
||||
$node = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node->right === null) {
|
||||
$temp = $node->left->root;
|
||||
if ($node->parent !== null) {
|
||||
if ($node->parent->left !== null && $node->parent->left->root->compare($node->data) === 0) {
|
||||
$node->parent->left = $temp->tree;
|
||||
} elseif ($node->parent->right !== null && $node->parent->right->root->compare($node->data) === 0) {
|
||||
$node->parent->right = $temp->tree;
|
||||
}
|
||||
}
|
||||
|
||||
$temp->parent = $node->parent;
|
||||
|
||||
$node = null;
|
||||
|
||||
return;
|
||||
} else {
|
||||
$temp = $this->successor($node);
|
||||
$node->key = $temp->key;
|
||||
$node->data = $temp->data;
|
||||
|
||||
$this->delete($temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
Stdlib/Tree/Node.php
Normal file
49
Stdlib/Tree/Node.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package phpOMS\Stdlib\Tree
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Stdlib\Tree;
|
||||
|
||||
/**
|
||||
* Priority queue class.
|
||||
*
|
||||
* @package phpOMS\Stdlib\Tree
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Node
|
||||
{
|
||||
public string $key = '';
|
||||
|
||||
public mixed $data = null;
|
||||
|
||||
public ?BinarySearchTree $left = null;
|
||||
|
||||
public ?BinarySearchTree $right = null;
|
||||
|
||||
public ?self $parent = null;
|
||||
|
||||
public ?BinarySearchTree $tree = null;
|
||||
|
||||
public function __construct(string $key, mixed $data = null)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function compare(mixed $data) : int
|
||||
{
|
||||
return $this->data <=> $data;
|
||||
}
|
||||
}
|
||||
|
|
@ -458,8 +458,6 @@ abstract class MimeType extends Enum
|
|||
|
||||
public const M_EVY = 'application/x-envoy';
|
||||
|
||||
public const M_EXE = 'application/x-msdownload';
|
||||
|
||||
public const M_EXI = 'application/exi';
|
||||
|
||||
public const M_EXT = 'application/vnd.novadigm.ext';
|
||||
|
|
@ -2010,6 +2008,20 @@ abstract class MimeType extends Enum
|
|||
|
||||
public const M_123 = 'application/vnd.lotus-1-2-3';
|
||||
|
||||
public const M_PEXE = 'vnd.microsoft.portable-executable';
|
||||
|
||||
public const M_EXE = 'application/exe';
|
||||
|
||||
public const M_DEXE = 'application/dos-exe';
|
||||
|
||||
public const M_XEXE = 'application/x-winexe';
|
||||
|
||||
public const M_MDEXE = 'application/msdos-windows';
|
||||
|
||||
public const M_MSP = 'application/x-msdos-program';
|
||||
|
||||
public const M_XMDEXE = 'application/x-msdownload';
|
||||
|
||||
/**
|
||||
* Get mime from file extension
|
||||
*
|
||||
|
|
@ -2036,7 +2048,6 @@ abstract class MimeType extends Enum
|
|||
* @return null|string
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @todo continue implementation
|
||||
*/
|
||||
public static function mimeToExtension(string $mime) : ?string
|
||||
{
|
||||
|
|
@ -2046,6 +2057,10 @@ abstract class MimeType extends Enum
|
|||
case self::M_JPEG:
|
||||
case self::M_JPG:
|
||||
return 'jpg';
|
||||
case self::M_PNG:
|
||||
return 'png';
|
||||
case self::M_SVG:
|
||||
return 'svg';
|
||||
case self::M_BMP:
|
||||
return 'bmp';
|
||||
case self::M_GIF:
|
||||
|
|
@ -2053,6 +2068,51 @@ abstract class MimeType extends Enum
|
|||
case self::M_HTML:
|
||||
case self::M_HTM:
|
||||
return 'htm';
|
||||
case self::M_DOCX:
|
||||
return 'docx';
|
||||
case self::M_DOC:
|
||||
return 'doc';
|
||||
case self::M_ODT:
|
||||
return 'odt';
|
||||
case self::M_XLSX:
|
||||
return 'xlsx';
|
||||
case self::M_XLA:
|
||||
case self::M_XLS:
|
||||
return 'xls';
|
||||
case self::M_ODS:
|
||||
return 'ods';
|
||||
case self::M_PPTX:
|
||||
return 'pptx';
|
||||
case self::M_PPT:
|
||||
return 'ppt';
|
||||
case self::M_ODP:
|
||||
return 'odp';
|
||||
case self::M_CSV:
|
||||
return 'csv';
|
||||
case self::M_XML:
|
||||
return 'xml';
|
||||
case self::M_JSON:
|
||||
return 'json';
|
||||
case self::M_ZIP:
|
||||
return 'zip';
|
||||
case self::M_7Z:
|
||||
return '7z';
|
||||
case self::M_RAR:
|
||||
return 'rar';
|
||||
case self::M_TAR:
|
||||
return 'tar';
|
||||
case self::M_MP3:
|
||||
return 'mp3';
|
||||
case self::M_MP4:
|
||||
return 'mp4';
|
||||
case self::M_PEXE:
|
||||
case self::M_EXE:
|
||||
case self::M_DEXE:
|
||||
case self::M_XEXE:
|
||||
case self::M_MDEXE:
|
||||
case self::M_MSP:
|
||||
case self::M_XMDEXE:
|
||||
return 'exe';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ namespace phpOMS\tests\Algorithm\Clustering;
|
|||
use phpOMS\Algorithm\Clustering\Kmeans;
|
||||
use phpOMS\Algorithm\Clustering\Point;
|
||||
|
||||
include __DIR__ . '/../../Autoloader.php';
|
||||
|
||||
/**
|
||||
* @testdox phpOMS\tests\Algorithm\Clustering\KmeansTest: Clustering points/elements with the K-means algorithm
|
||||
*
|
||||
|
|
@ -34,6 +36,9 @@ final class KmeansTest extends \PHPUnit\Framework\TestCase
|
|||
$seed = \mt_rand(\PHP_INT_MIN, \PHP_INT_MAX);
|
||||
\mt_srand($seed);
|
||||
|
||||
// The following seed + putting the loop to 1 would fail the test
|
||||
//\mt_srand(1788576141);
|
||||
|
||||
$result = false;
|
||||
|
||||
// due to the random nature this can be false sometimes?!
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user