Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
PageRank
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
2 / 2
14
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
8
 calculateRanks
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Business\Marketing
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Business\Marketing;
16
17/**
18 * PageRank algorithm
19 *
20 * @package phpOMS\Business\Marketing
21 * @license OMS License 2.0
22 * @link    https://jingga.app
23 * @since   1.0.0
24 */
25final class PageRank
26{
27    /**
28     * Damping value
29     *
30     * @var float
31     * @since 1.0.0
32     */
33    private float $damping = 0.85;
34
35    /**
36     * Page rank
37     *
38     * @var array<mixed, float>
39     * @since 1.0.0
40     */
41    private array $pageRanks = [];
42
43    /**
44     * Relation array
45     *
46     * Array of elements where every element has an array of incoming links/relations
47     *
48     * @var array[]
49     * @since 1.0.0
50     */
51    private array $relations = [];
52
53    /**
54     * Amount of outgoing links from an element
55     *
56     * @var int[]
57     * @since 1.0.0
58     */
59    private array $outgoing = [];
60
61    /**
62     * Constructor.
63     *
64     * @param array[] $relations Relations between elements (keys => link from, array => link to)
65     * @param bool    $isUnique  Only consider unique relations
66     * @param float   $damping   Damping value
67     *
68     * @since 1.0.0
69     */
70    public function __construct(array $relations, bool $isUnique = true, float $damping = 0.85)
71    {
72        $this->damping = $damping;
73
74        foreach ($relations as $key => $relation) {
75            $this->outgoing[$key] = \count($relation);
76
77            if (!isset($this->relations[$key])) {
78                $this->relations[$key] = [];
79            }
80
81            foreach ($relation as $linkTo) {
82                if (!isset($this->relations[$linkTo])) {
83                    $this->relations[$linkTo] = [];
84                }
85
86                if (!isset($this->outgoing[$linkTo])) {
87                    $this->outgoing[$linkTo] = 0;
88                }
89
90                if (!$isUnique || !\in_array($key, $this->relations[$linkTo])) {
91                    $this->relations[$linkTo][] = $key;
92                }
93            }
94        }
95    }
96
97    /**
98     * Calcualte the rank based on a start rank for the different elements
99     *
100     * A different start rank for different elements might make sense if the elements are not uniform from the very beginning
101     *
102     * @param int                      $iterations Algorithm iterations
103     * @param null|array<mixed, float> $startRank  Start rank for an element
104     *
105     * @return array
106     *
107     * @since 1.0.0
108     */
109    public function calculateRanks(int $iterations = 20, array $startRank = null) : array
110    {
111        if ($startRank !== null) {
112            $this->pageRanks = $startRank;
113        } else {
114            foreach ($this->relations as $key => $relation) {
115                $this->pageRanks[$key] = 0.0;
116            }
117        }
118
119        for ($i = 0; $i < $iterations; ++$i) {
120            foreach ($this->relations as $key => $relation) {
121                $PR = 0.0;
122
123                foreach ($relation as $linkFrom) {
124                    $PR += $this->pageRanks[$linkFrom] / $this->outgoing[$linkFrom];
125                }
126
127                $this->pageRanks[$key] = 1 - $this->damping + $this->damping * $PR;
128            }
129        }
130
131        return $this->pageRanks;
132    }
133}