Entropy.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. namespace MathPHP\InformationTheory;
  3. use MathPHP\Functions\Map;
  4. use MathPHP\Exception;
  5. /**
  6. * Functions dealing with information entropy in the field of statistical field of information thoery.
  7. *
  8. * - Entropy:
  9. * - Shannon entropy (bits)
  10. * - Shannon entropy (nats)
  11. * - Shannon entropy (harts)
  12. * - Cross entropy
  13. *
  14. * In information theory, entropy is the expected value (average) of the information contained in each message.
  15. *
  16. * https://en.wikipedia.org/wiki/Entropy_(information_theory)
  17. */
  18. class Entropy
  19. {
  20. private const ONE_TOLERANCE = 0.010001;
  21. /**
  22. * Shannon entropy (bit entropy)
  23. * The average minimum number of bits needed to encode a string of symbols, based on the probability of the symbols.
  24. * https://en.wikipedia.org/wiki/Entropy_(information_theory)
  25. *
  26. * H = -∑ pᵢlog₂(pᵢ)
  27. *
  28. * H is in shannons, or bits.
  29. *
  30. * @param array<int|float> $p probability distribution
  31. *
  32. * @return float average minimum number of bits
  33. *
  34. * @throws Exception\BadDataException if probability distribution p does not add up to 1
  35. */
  36. public static function shannonEntropy(array $p): float
  37. {
  38. // Probability distribution must add up to 1.0
  39. if (\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) {
  40. throw new Exception\BadDataException('Probability distribution p must add up to 1; p adds up to: ' . \array_sum($p));
  41. }
  42. // Defensive measure against taking the log of 0 which would be -∞
  43. $p = \array_map(
  44. function ($pᵢ) {
  45. return $pᵢ == 0 ? 1e-15 : $pᵢ;
  46. },
  47. $p
  48. );
  49. // ∑ pᵢlog₂(pᵢ)
  50. $∑pᵢlog₂⟮pᵢ⟯ = \array_sum(\array_map(
  51. function ($pᵢ) {
  52. return $pᵢ * \log($pᵢ, 2);
  53. },
  54. $p
  55. ));
  56. return -$∑pᵢlog₂⟮pᵢ⟯;
  57. }
  58. /**
  59. * Shannon nat entropy (nat entropy)
  60. * The average minimum number of nats needed to encode a string of symbols, based on the probability of the symbols.
  61. * https://en.wikipedia.org/wiki/Entropy_(information_theory)
  62. *
  63. * H = -∑ pᵢln(pᵢ)
  64. *
  65. * H is in units of nats.
  66. * 1 nat = 1/ln(2) shannons or bits.
  67. * https://en.wikipedia.org/wiki/Nat_(unit)
  68. *
  69. * @param array<int|float> $p probability distribution
  70. *
  71. * @return float average minimum number of nats
  72. *
  73. * @throws Exception\BadDataException if probability distribution p does not add up to 1
  74. */
  75. public static function shannonNatEntropy(array $p)
  76. {
  77. // Probability distribution must add up to 1.0
  78. if (\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) {
  79. throw new Exception\BadDataException('Probability distribution p must add up to 1; p adds up to: ' . \array_sum($p));
  80. }
  81. // Defensive measure against taking the log of 0 which would be -∞
  82. $p = \array_map(
  83. function ($pᵢ) {
  84. return $pᵢ == 0 ? 1e-15 : $pᵢ;
  85. },
  86. $p
  87. );
  88. // ∑ pᵢln(pᵢ)
  89. $∑pᵢln⟮pᵢ⟯ = \array_sum(\array_map(
  90. function ($pᵢ) {
  91. return $pᵢ * \log($pᵢ);
  92. },
  93. $p
  94. ));
  95. return -$∑pᵢln⟮pᵢ⟯;
  96. }
  97. /**
  98. * Shannon hartley entropy (hartley entropy)
  99. * The average minimum number of hartleys needed to encode a string of symbols, based on the probability of the symbols.
  100. * https://en.wikipedia.org/wiki/Entropy_(information_theory)
  101. *
  102. * H = -∑ pᵢlog₁₀(pᵢ)
  103. *
  104. * H is in units of hartleys, or harts.
  105. * 1 hartley = log₂(10) bit = ln(10) nat, or approximately 3.322 Sh, or 2.303 nat.
  106. * https://en.wikipedia.org/wiki/Hartley_(unit)
  107. *
  108. * @param array<int|float> $p probability distribution
  109. *
  110. * @return float average minimum number of hartleys
  111. *
  112. * @throws Exception\BadDataException if probability distribution p does not add up to 1
  113. */
  114. public static function shannonHartleyEntropy(array $p)
  115. {
  116. // Probability distribution must add up to 1.0
  117. if (\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) {
  118. throw new Exception\BadDataException('Probability distribution p must add up to 1; p adds up to: ' . \array_sum($p));
  119. }
  120. // Defensive measure against taking the log of 0 which would be -∞
  121. $p = \array_map(
  122. function ($pᵢ) {
  123. return $pᵢ == 0 ? 1e-15 : $pᵢ;
  124. },
  125. $p
  126. );
  127. // ∑ pᵢlog₁₀(pᵢ)
  128. $∑pᵢlog₁₀⟮pᵢ⟯ = \array_sum(\array_map(
  129. function ($pᵢ) {
  130. return $pᵢ * \log10($pᵢ);
  131. },
  132. $p
  133. ));
  134. return -$∑pᵢlog₁₀⟮pᵢ⟯;
  135. }
  136. /**
  137. * Cross entropy
  138. * The cross entropy between two probability distributions p and q over the same underlying set of events
  139. * measures the average number of bits needed to identify an event drawn from the set, if a coding scheme
  140. * is used that is optimized for an "unnatural" probability distribution q, rather than the "true" distribution p.
  141. * https://en.wikipedia.org/wiki/Cross_entropy
  142. *
  143. * H(p,q) = -∑ p(x) log₂ q(x)
  144. *
  145. * @param array<int|float> $p distribution p
  146. * @param array<int|float> $q distribution q
  147. *
  148. * @return float entropy between distributions
  149. *
  150. * @throws Exception\BadDataException if p and q do not have the same number of elements
  151. * @throws Exception\BadDataException if p and q are not probability distributions that add up to 1
  152. */
  153. public static function crossEntropy(array $p, array $q)
  154. {
  155. // Arrays must have the same number of elements
  156. if (\count($p) !== \count($q)) {
  157. throw new Exception\BadDataException('p and q must have the same number of elements');
  158. }
  159. // Probability distributions must add up to 1.0
  160. if ((\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) || (\abs(\array_sum($q) - 1) > self::ONE_TOLERANCE)) {
  161. throw new Exception\BadDataException('Distributions p and q must add up to 1');
  162. }
  163. // Defensive measure against taking the log of 0 which would be -∞
  164. $q = \array_map(
  165. function ($qᵢ) {
  166. return $qᵢ == 0 ? 1e-15 : $qᵢ;
  167. },
  168. $q
  169. );
  170. // ∑ p(x) log₂ q(x)
  171. $∑plog₂⟮q⟯ = \array_sum(\array_map(
  172. function ($pᵢ, $qᵢ) {
  173. return $pᵢ * \log($qᵢ, 2);
  174. },
  175. $p,
  176. $q
  177. ));
  178. return -$∑plog₂⟮q⟯;
  179. }
  180. /**
  181. * Joint entropy (bits)
  182. * A measure of the uncertainty associated with a set of variables.
  183. * https://en.wikipedia.org/wiki/Joint_entropy
  184. *
  185. * H(X,Y) = -∑ ∑ P(x,y)log₂[P(x,y)]
  186. * x y
  187. *
  188. * Where x and y are particular values of random variables X and Y, respectively,
  189. * and P(x,y) is the joint probability of these values occurring together.
  190. * H is in shannons, or bits.
  191. *
  192. * Joint entropy is basically just shannonEntropy but the probability distribution input
  193. * represents the probability of two variables happening at the same time.
  194. *
  195. * @param array<int|float> $P⟮x、y⟯ probability distribution of x and y occuring together
  196. *
  197. * @return float uncertainty
  198. *
  199. * @throws Exception\BadDataException if probability distribution $P⟮x、y⟯ does not add up to 1
  200. */
  201. public static function jointEntropy(array $P⟮x、y⟯)
  202. {
  203. return self::shannonEntropy($P⟮x、y⟯);
  204. }
  205. /**
  206. * Rényi entropy
  207. * Rényi entropy generalizes the Hartley entropy, the Shannon entropy, the collision entropy and the min entropy
  208. * https://en.wikipedia.org/wiki/R%C3%A9nyi_entropy
  209. * 1
  210. * Hₐ(X) = ----- log₂(∑ pᵢᵃ)
  211. * 1 - α
  212. *
  213. * α ≥ 0; α ≠ 1
  214. *
  215. * H is in shannons, or bits.
  216. *
  217. * @param array<int|float> $p probability distribution
  218. * @param int|float $α order α
  219. *
  220. * @return float
  221. *
  222. * @throws Exception\BadDataException if probability distribution p does not add up to 1
  223. * @throws Exception\OutOfBoundsException if α < 0 or α = 1
  224. */
  225. public static function renyiEntropy(array $p, $α)
  226. {
  227. // Probability distribution must add up to 1.0
  228. if (\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) {
  229. throw new Exception\BadDataException('Probability distribution p must add up to 1; p adds up to: ' . \array_sum($p));
  230. }
  231. // α ≥ 0; α ≠ 1
  232. if ($α < 0 || $α == 1) {
  233. throw new Exception\OutOfBoundsException("α must be ≥ 0 and ≠ 1 ");
  234. }
  235. // (1 / 1 - α) log (∑ pᵢᵃ)
  236. $Hₐ⟮X⟯ = (1 / (1 - $α)) * \log(\array_sum(Map\Single::pow($p, $α)), 2);
  237. return $Hₐ⟮X⟯;
  238. }
  239. /**
  240. * Perplexity
  241. * a measurement of how well a probability distribution or probability model predicts a sample.
  242. * It may be used to compare probability models.
  243. * A low perplexity indicates the probability distribution is good at predicting the sample.
  244. * https://en.wikipedia.org/wiki/Perplexity
  245. *
  246. * perplexity = 2ᴴ⁽ᵖ⁾ = 2^(-∑ pᵢlog₂(pᵢ))
  247. * where H(p) = entropy
  248. *
  249. * Perplexity is in shannons, or bits.
  250. *
  251. * @param array<int|float> $p probability distribution
  252. *
  253. * @return float perplexity
  254. *
  255. * @throws Exception\BadDataException if probability distribution p does not add up to 1
  256. */
  257. public static function perplexity(array $p)
  258. {
  259. // Probability distribution must add up to 1.0
  260. if (\abs(\array_sum($p) - 1) > self::ONE_TOLERANCE) {
  261. throw new Exception\BadDataException('Probability distribution p must add up to 1; p adds up to: ' . \array_sum($p));
  262. }
  263. // ∑ pᵢlog₂(pᵢ)
  264. $H⟮p⟯ = self::shannonEntropy($p);
  265. return 2 ** $H⟮p⟯;
  266. }
  267. }