|
| 1 | +import { factory } from '../../utils/factory.js' |
| 2 | + |
| 3 | +const name = 'zeta' |
| 4 | +const dependencies = ['typed', 'config', 'multiply', 'pow', 'divide', 'factorial', 'equal', 'smallerEq', 'isNegative', 'gamma', 'sin', 'subtract', 'add', '?Complex', '?BigNumber', 'pi'] |
| 5 | + |
| 6 | +export const createZeta = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, multiply, pow, divide, factorial, equal, smallerEq, isNegative, gamma, sin, subtract, add, Complex, BigNumber, pi }) => { |
| 7 | + /** |
| 8 | + * Compute the Riemann Zeta function of a value using an infinite series for |
| 9 | + * all of the complex plane using Riemann's Functional equation. |
| 10 | + * |
| 11 | + * Based off the paper by Xavier Gourdon and Pascal Sebah |
| 12 | + * ( http://numbers.computation.free.fr/Constants/Miscellaneous/zetaevaluations.pdf ) |
| 13 | + * |
| 14 | + * Implementation and slight modification by Anik Patel |
| 15 | + * |
| 16 | + * Note: the implementation is accurate up to about 6 digits. |
| 17 | + * |
| 18 | + * Syntax: |
| 19 | + * |
| 20 | + * math.zeta(n) |
| 21 | + * |
| 22 | + * Examples: |
| 23 | + * |
| 24 | + * math.zeta(5) // returns 1.0369277551433895 |
| 25 | + * math.zeta(-0.5) // returns -0.2078862249773449 |
| 26 | + * math.zeta(math.i) // returns 0.0033002236853253153 - 0.4181554491413212i |
| 27 | + * |
| 28 | + * |
| 29 | + * @param {number | Complex | BigNumber} s A Real, Complex or BigNumber parameter to the Riemann Zeta Function |
| 30 | + * @return {number | Complex | BigNumber} The Riemann Zeta of `s` |
| 31 | + */ |
| 32 | + return typed(name, { |
| 33 | + number: (s) => zetaNumeric(s, value => value, () => 20), |
| 34 | + BigNumber: (s) => zetaNumeric( |
| 35 | + s, |
| 36 | + value => new BigNumber(value), |
| 37 | + () => { |
| 38 | + // epsilon is for example 1e-12. Extract the positive exponent 12 from that |
| 39 | + return Math.abs(Math.log10(config.epsilon)) |
| 40 | + } |
| 41 | + ), |
| 42 | + Complex: zetaComplex |
| 43 | + }) |
| 44 | + |
| 45 | + /** |
| 46 | + * @param {number | BigNumber} s |
| 47 | + * @param {(value: number) => number | BigNumber} createValue |
| 48 | + * @param {(value: number | BigNumber | Complex) => number} determineDigits |
| 49 | + * @returns {number | BigNumber} |
| 50 | + */ |
| 51 | + function zetaNumeric (s, createValue, determineDigits) { |
| 52 | + if (equal(s, 0)) { |
| 53 | + return createValue(-0.5) |
| 54 | + } |
| 55 | + if (equal(s, 1)) { |
| 56 | + return createValue(NaN) |
| 57 | + } |
| 58 | + if (!isFinite(s)) { |
| 59 | + return isNegative(s) ? createValue(NaN) : createValue(1) |
| 60 | + } |
| 61 | + |
| 62 | + return zeta(s, createValue, determineDigits, s => s) |
| 63 | + } |
| 64 | + |
| 65 | + /** |
| 66 | + * @param {Complex} s |
| 67 | + * @returns {Complex} |
| 68 | + */ |
| 69 | + function zetaComplex (s) { |
| 70 | + if (s.re === 0 && s.im === 0) { |
| 71 | + return new Complex(-0.5) |
| 72 | + } |
| 73 | + if (s.re === 1) { |
| 74 | + return new Complex(NaN, NaN) |
| 75 | + } |
| 76 | + if (s.re === Infinity && s.im === 0) { |
| 77 | + return new Complex(1) |
| 78 | + } |
| 79 | + if (s.im === Infinity || s.re === -Infinity) { |
| 80 | + return new Complex(NaN, NaN) |
| 81 | + } |
| 82 | + |
| 83 | + return zeta(s, value => value, s => Math.round(1.3 * 15 + 0.9 * Math.abs(s.im)), s => s.re) |
| 84 | + } |
| 85 | + |
| 86 | + /** |
| 87 | + * @param {number | BigNumber | Complex} s |
| 88 | + * @param {(value: number) => number | BigNumber | Complex} createValue |
| 89 | + * @param {(value: number | BigNumber | Complex) => number} determineDigits |
| 90 | + * @param {(value: number | BigNumber | Complex) => number} getRe |
| 91 | + * @returns {*|number} |
| 92 | + */ |
| 93 | + function zeta (s, createValue, determineDigits, getRe) { |
| 94 | + const n = determineDigits(s) |
| 95 | + if (getRe(s) > -(n - 1) / 2) { |
| 96 | + return f(s, createValue(n), createValue) |
| 97 | + } else { |
| 98 | + // Function Equation for reflection to x < 1 |
| 99 | + let c = multiply(pow(2, s), pow(createValue(pi), subtract(s, 1))) |
| 100 | + c = multiply(c, (sin(multiply(divide(createValue(pi), 2), s)))) |
| 101 | + c = multiply(c, gamma(subtract(1, s))) |
| 102 | + return multiply(c, zeta(subtract(1, s), createValue, determineDigits, getRe)) |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * Calculate a portion of the sum |
| 108 | + * @param {number | BigNumber} k a positive integer |
| 109 | + * @param {number | BigNumber} n a positive integer |
| 110 | + * @return {number} the portion of the sum |
| 111 | + **/ |
| 112 | + function d (k, n) { |
| 113 | + let S = k |
| 114 | + for (let j = k; smallerEq(j, n); j = add(j, 1)) { |
| 115 | + const factor = divide( |
| 116 | + multiply(factorial(add(n, subtract(j, 1))), pow(4, j)), |
| 117 | + multiply(factorial(subtract(n, j)), factorial(multiply(2, j))) |
| 118 | + ) |
| 119 | + S = add(S, factor) |
| 120 | + } |
| 121 | + |
| 122 | + return multiply(n, S) |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Calculate the positive Riemann Zeta function |
| 127 | + * @param {number} s a real or complex number with s.re > 1 |
| 128 | + * @param {number} n a positive integer |
| 129 | + * @param {(number) => number | BigNumber | Complex} createValue |
| 130 | + * @return {number} Riemann Zeta of s |
| 131 | + **/ |
| 132 | + function f (s, n, createValue) { |
| 133 | + const c = divide(1, multiply(d(createValue(0), n), subtract(1, pow(2, subtract(1, s))))) |
| 134 | + let S = createValue(0) |
| 135 | + for (let k = createValue(1); smallerEq(k, n); k = add(k, 1)) { |
| 136 | + S = add(S, divide(multiply((-1) ** (k - 1), d(k, n)), pow(k, s))) |
| 137 | + } |
| 138 | + return multiply(c, S) |
| 139 | + } |
| 140 | +}) |
0 commit comments