/** * Generalized Hypergeometric Function * * https://en.wikipedia.org/wiki/Generalized_hypergeometric_function * * ∞ * ____ * \ ∏ap⁽ⁿ⁾ * zⁿ * pFq(a₁,a₂,...ap;b₁,b₂,...,bq;z)= > ------------ * / ∏bq⁽ⁿ⁾ * n! * ‾‾‾‾ * n=0 * * Where a⁽ⁿ⁾ is the Pochhammer Function or Rising Factorial * * We are evaluating this as a series: * * (a + n - 1) * z * ∏n = ∏n₋₁ * ----------------- * (b + n - 1) * n * * n (a + n - 1) * z * ₁F₁ = ₁F₁n₋₁ + ∏ ----------------- = ₁F₁n₋₁ + ∏n * 1 (b + n - 1) * n * * @param int $p the number of parameters in the numerator * @param int $q the number of parameters in the denominator * @param array $params a collection of the a, b, and z parameters * * @return number * * @throws BadParameterException if the number of parameters is incorrect */ public static function generalizedHypergeometric(int $p, int $q, ...$params) { $n = count($params); if ($n !== $p + $q + 1) { $expected_num_params = $p + $q + 1; throw new Exception\BadParameterException("Number of parameters is incorrect. Expected {$expected_num_params}; got {$n}"); } $a = array_slice($params, 0, $p); $b = array_slice($params, $p, $q); $z = $params[$n - 1]; $tol = 1.0E-8; $n = 1; $sum = 0; $product = 1; do { $sum += $product; $a_sum = array_product(Single::add($a, $n - 1)); $b_sum = array_product(Single::add($b, $n - 1)); $product *= $a_sum * $z / $b_sum / $n; $n++; } while ($product / $sum > $tol); return $sum; }