@numero-uno-jpn/rstats 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.ja.md +36 -20
  3. package/README.md +34 -18
  4. package/THIRD_PARTY_NOTICES.md +25 -5
  5. package/dist/chunks/chunk-2JCIB5H2.js +2 -0
  6. package/dist/chunks/chunk-7JVSDGQQ.js +2 -0
  7. package/dist/chunks/chunk-7NK4SA6B.js +2 -0
  8. package/dist/chunks/chunk-AJYQNFXK.js +2 -0
  9. package/dist/chunks/chunk-DRNMIEYH.js +2 -0
  10. package/dist/chunks/chunk-NAYDS7CL.js +2 -0
  11. package/dist/chunks/chunk-NOCEAM44.js +2 -0
  12. package/dist/chunks/chunk-QNAIK4PY.js +2 -0
  13. package/dist/chunks/chunk-TPC6MTYC.js +2 -0
  14. package/dist/chunks/chunk-UGUQYWRQ.js +2 -0
  15. package/dist/chunks/chunk-XM2TVD63.js +2 -0
  16. package/dist/chunks/chunk-Y4IZV7XK.js +2 -0
  17. package/dist/chunks/chunk-ZWJ42QN6.js +2 -0
  18. package/dist/descriptive/basic.d.ts +73 -0
  19. package/dist/descriptive/basic.js +256 -0
  20. package/dist/descriptive/cov.d.ts +39 -0
  21. package/dist/descriptive/cov.js +105 -0
  22. package/dist/descriptive/hist.d.ts +64 -0
  23. package/dist/descriptive/hist.js +304 -0
  24. package/dist/descriptive/moments.d.ts +63 -0
  25. package/dist/descriptive/moments.js +158 -0
  26. package/dist/descriptive/quantile.d.ts +50 -0
  27. package/dist/descriptive/quantile.js +131 -0
  28. package/dist/descriptive/rank.d.ts +23 -0
  29. package/dist/descriptive/rank.js +72 -0
  30. package/dist/descriptive/table.d.ts +54 -0
  31. package/dist/descriptive/table.js +160 -0
  32. package/dist/descriptive.js +2 -0
  33. package/dist/distributions/arcsine.d.ts +24 -0
  34. package/dist/distributions/arcsine.js +73 -0
  35. package/dist/distributions/invgamma.d.ts +29 -0
  36. package/dist/distributions/invgamma.js +85 -0
  37. package/dist/distributions/kumaraswamy.d.ts +17 -0
  38. package/dist/distributions/kumaraswamy.js +96 -0
  39. package/dist/distributions/laplace.d.ts +23 -0
  40. package/dist/distributions/laplace.js +69 -0
  41. package/dist/distributions/multinomial.d.ts +24 -0
  42. package/dist/distributions/multinomial.js +70 -0
  43. package/dist/distributions/nbeta.js +28 -10
  44. package/dist/distributions/nchisq.js +20 -3
  45. package/dist/distributions/pareto.d.ts +23 -0
  46. package/dist/distributions/pareto.js +76 -0
  47. package/dist/distributions/summary.d.ts +25 -0
  48. package/dist/distributions/summary.js +289 -0
  49. package/dist/distributions/triangular.d.ts +25 -0
  50. package/dist/distributions/triangular.js +87 -0
  51. package/dist/distributions.js +2 -0
  52. package/dist/entries/descriptive.d.ts +7 -0
  53. package/dist/entries/descriptive.js +8 -0
  54. package/dist/entries/distributions.d.ts +41 -0
  55. package/dist/entries/distributions.js +41 -0
  56. package/dist/entries/linalg.d.ts +11 -0
  57. package/dist/entries/linalg.js +11 -0
  58. package/dist/entries/numerics.d.ts +8 -0
  59. package/dist/entries/numerics.js +6 -0
  60. package/dist/entries/special.d.ts +9 -0
  61. package/dist/entries/special.js +9 -0
  62. package/dist/entries/tests.d.ts +21 -0
  63. package/dist/entries/tests.js +20 -0
  64. package/dist/htest/ansariTest.d.ts +13 -0
  65. package/dist/htest/ansariTest.js +80 -0
  66. package/dist/htest/aov.d.ts +85 -0
  67. package/dist/htest/aov.js +178 -0
  68. package/dist/htest/binomTest.d.ts +36 -0
  69. package/dist/htest/binomTest.js +121 -0
  70. package/dist/htest/cor.d.ts +14 -0
  71. package/dist/htest/cor.js +4 -3
  72. package/dist/htest/core/ansari.d.ts +18 -0
  73. package/dist/htest/core/ansari.js +121 -0
  74. package/dist/htest/core/kolmogorov.d.ts +26 -0
  75. package/dist/htest/core/kolmogorov.js +221 -0
  76. package/dist/htest/core/mantelhaen.d.ts +26 -0
  77. package/dist/htest/core/mantelhaen.js +145 -0
  78. package/dist/htest/core/shapiro.d.ts +24 -0
  79. package/dist/htest/core/shapiro.js +142 -0
  80. package/dist/htest/core/smirnov.d.ts +20 -0
  81. package/dist/htest/core/smirnov.js +75 -0
  82. package/dist/htest/fisherTest.js +134 -120
  83. package/dist/htest/groups.d.ts +20 -0
  84. package/dist/htest/groups.js +62 -0
  85. package/dist/htest/ksTest.d.ts +31 -0
  86. package/dist/htest/ksTest.js +106 -0
  87. package/dist/htest/mantelhaenTest.d.ts +26 -0
  88. package/dist/htest/mantelhaenTest.js +196 -0
  89. package/dist/htest/mcnemarTest.d.ts +26 -0
  90. package/dist/htest/mcnemarTest.js +99 -0
  91. package/dist/htest/onewayTest.d.ts +26 -0
  92. package/dist/htest/onewayTest.js +97 -0
  93. package/dist/htest/pairwise.d.ts +64 -0
  94. package/dist/htest/pairwise.js +152 -0
  95. package/dist/htest/poissonTest.d.ts +35 -0
  96. package/dist/htest/poissonTest.js +170 -0
  97. package/dist/htest/propTest.d.ts +37 -0
  98. package/dist/htest/propTest.js +164 -0
  99. package/dist/htest/rankTests.d.ts +52 -0
  100. package/dist/htest/rankTests.js +234 -0
  101. package/dist/htest/types.d.ts +2 -0
  102. package/dist/htest/types.js +6 -0
  103. package/dist/htest/varTest.d.ts +48 -0
  104. package/dist/htest/varTest.js +118 -0
  105. package/dist/htest/wilcoxTest.js +136 -174
  106. package/dist/index.d.ts +6 -50
  107. package/dist/index.js +8 -50
  108. package/dist/integrate/quadpack.d.ts +29 -0
  109. package/dist/integrate/quadpack.js +525 -0
  110. package/dist/internal/seq.d.ts +5 -0
  111. package/dist/internal/seq.js +14 -0
  112. package/dist/internal/uniroot.js +10 -71
  113. package/dist/linalg/lu.d.ts +45 -0
  114. package/dist/linalg/lu.js +88 -0
  115. package/dist/linalg/ops.d.ts +90 -0
  116. package/dist/linalg/ops.js +320 -0
  117. package/dist/linalg.js +2 -0
  118. package/dist/models/lm.d.ts +98 -0
  119. package/dist/models/lm.js +187 -0
  120. package/dist/numerics/deriv.d.ts +37 -0
  121. package/dist/numerics/deriv.js +150 -0
  122. package/dist/numerics/integrate.d.ts +91 -0
  123. package/dist/numerics/integrate.js +264 -0
  124. package/dist/numerics/interp.d.ts +103 -0
  125. package/dist/numerics/interp.js +581 -0
  126. package/dist/numerics/ode.d.ts +23 -0
  127. package/dist/numerics/ode.js +59 -0
  128. package/dist/numerics.js +2 -0
  129. package/dist/optim/localmin.d.ts +16 -0
  130. package/dist/optim/localmin.js +97 -0
  131. package/dist/random/beta.js +1 -1
  132. package/dist/random/binomial.js +1 -1
  133. package/dist/random/continuous.js +1 -1
  134. package/dist/random/extra.d.ts +33 -0
  135. package/dist/random/extra.js +62 -0
  136. package/dist/random/gamma.js +1 -1
  137. package/dist/random/hyper.js +1 -1
  138. package/dist/random/mt.js +0 -1
  139. package/dist/random/poisson.js +0 -1
  140. package/dist/random/rank.js +1 -1
  141. package/dist/random/rng.js +1 -1
  142. package/dist/random/sample.js +1 -1
  143. package/dist/rstats.js +2 -2
  144. package/dist/special/besselIK.d.ts +38 -0
  145. package/dist/special/besselIK.js +315 -0
  146. package/dist/special/besselJY.d.ts +32 -0
  147. package/dist/special/besselJY.js +508 -0
  148. package/dist/special/besselSeries.d.ts +8 -0
  149. package/dist/special/besselSeries.js +24 -0
  150. package/dist/special/beta.js +22 -5
  151. package/dist/special/choose.d.ts +75 -0
  152. package/dist/special/choose.js +233 -0
  153. package/dist/special/erf.d.ts +28 -2
  154. package/dist/special/erf.js +37 -10
  155. package/dist/special/extra.d.ts +47 -0
  156. package/dist/special/extra.js +220 -0
  157. package/dist/special/igam.d.ts +4 -0
  158. package/dist/special/igam.js +57 -2
  159. package/dist/special/incbet.js +77 -22
  160. package/dist/special/lgammaDiff.d.ts +7 -0
  161. package/dist/special/lgammaDiff.js +16 -3
  162. package/dist/special/psigamma.d.ts +48 -0
  163. package/dist/special/psigamma.js +318 -0
  164. package/dist/special/rCompatible.d.ts +8 -0
  165. package/dist/special/rCompatible.js +1 -0
  166. package/dist/special.js +2 -0
  167. package/dist/tests.js +2 -0
  168. package/package.json +26 -1
@@ -0,0 +1,187 @@
1
+ // Linear models with a matrix interface: R's lm (with summary.lm's table), confint and predict.
2
+ //
3
+ // Written for rStats.js (no ported code). The fit is the QR decomposition of the model matrix (a column of ones
4
+ // first unless `intercept` is false, rows scaled by sqrt(weights)) with R's LINPACK pivoting (src/linalg/qr.ts,
5
+ // tolerance 1e-7): columns that are linear combinations of earlier ones are moved to the end and get NaN
6
+ // coefficients ("aliased"), as in R. The summary follows summary.lm: residual variance rss / (n - rank),
7
+ // covariance of the estimates sigma^2 (R'R)^-1 from the triangular factor, t tests on n - rank degrees of freedom,
8
+ // R^2 from the (weighted) mean-corrected model sum of squares when there is an intercept, and the overall F test.
9
+ // Rows of weight 0 are left out of the fit and of the counts of observations, but get fitted values (and residuals)
10
+ // from the coefficients, as in R.
11
+ import { pf1 } from "../distributions/f.js";
12
+ import { pt1, qt1 } from "../distributions/t.js";
13
+ import { chol2inv } from "../linalg/chol.js";
14
+ import { qr, qrCoef, qrResid } from "../linalg/qr.js";
15
+ /**
16
+ * Fit a linear model by least squares. Equivalent to R's `lm(y ~ x)` (or `y ~ x - 1` without intercept, with
17
+ * `weights` for weighted least squares) together with `summary()`; the predictors are given as a matrix instead of a
18
+ * formula. Rows with a missing value in `x`, `y` or the weights are dropped.
19
+ *
20
+ * @param x Predictors: a matrix (array of rows, one column per predictor) or a vector (one predictor).
21
+ * @param y Response.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const fit = lm([1, 2, 3, 4, 5], [2.1, 3.9, 6.2, 7.8, 10.1]);
26
+ * fit.coefficients; // [intercept, slope]
27
+ * fit.rSquared;
28
+ * ```
29
+ */
30
+ export function lm(x, y, options = {}) {
31
+ const intercept = options.intercept ?? true;
32
+ const xr = x.length > 0 && typeof x[0] !== "number" ? x.map((r) => Array.from(r)) : Array.from(x, (v) => [v]);
33
+ const ys = Array.from(y);
34
+ if (xr.length !== ys.length)
35
+ throw new RangeError("variable lengths differ (found for 'x')");
36
+ const wsAll = options.weights === undefined ? undefined : Array.from(options.weights);
37
+ if (wsAll !== undefined && wsAll.length !== ys.length)
38
+ throw new RangeError("variable lengths differ (found for '(weights)')");
39
+ if (wsAll?.some((w) => w < 0))
40
+ throw new RangeError("missing or negative weights not allowed");
41
+ const keep = ys.map((v, i) => !Number.isNaN(v) && xr[i].every((t) => !Number.isNaN(t)) && (wsAll === undefined || !Number.isNaN(wsAll[i])));
42
+ const Xall = xr.filter((_, i) => keep[i]).map((r) => (intercept ? [1, ...r] : r));
43
+ const Yall = ys.filter((_, i) => keep[i]);
44
+ const Wall = wsAll?.filter((_, i) => keep[i]);
45
+ if (Yall.length === 0)
46
+ throw new RangeError("0 (non-NA) cases");
47
+ // the fit uses the rows of non-zero weight only
48
+ const fitRow = Yall.map((_, i) => Wall === undefined || Wall[i] !== 0);
49
+ const X = Xall.filter((_, i) => fitRow[i]);
50
+ const Y = Yall.filter((_, i) => fitRow[i]);
51
+ const W = Wall?.filter((_, i) => fitRow[i]);
52
+ const n = Y.length;
53
+ if (n === 0)
54
+ throw new RangeError("all weights are zero");
55
+ const sw = W?.map(Math.sqrt);
56
+ const Xw = sw === undefined ? X : X.map((r, i) => r.map((v) => v * sw[i]));
57
+ const Yw = sw === undefined ? Y : Y.map((v, i) => v * sw[i]);
58
+ const q = qr(Xw);
59
+ const rank = q.rank;
60
+ const coefficients = qrCoef(q, Yw);
61
+ // residuals from Q (the part of Q'y beyond the rank), so a saturated model has residuals of exactly 0, as in R
62
+ const rw = qrResid(q, Yw);
63
+ const fitResiduals = sw === undefined ? rw : rw.map((v, i) => v / sw[i]);
64
+ // rows of weight 0: fitted values x'b (aliased coefficients count as 0), residuals y - fitted
65
+ const residuals = [];
66
+ const fittedValues = [];
67
+ let k = 0;
68
+ Yall.forEach((v, i) => {
69
+ if (fitRow[i]) {
70
+ residuals.push(fitResiduals[k]);
71
+ fittedValues.push(v - fitResiduals[k]);
72
+ k++;
73
+ }
74
+ else {
75
+ const fv = Xall[i].reduce((s, t, j) => (Number.isNaN(coefficients[j]) ? s : s + t * coefficients[j]), 0);
76
+ fittedValues.push(fv);
77
+ residuals.push(v - fv);
78
+ }
79
+ });
80
+ const fitted = fittedValues.filter((_, i) => fitRow[i]);
81
+ const dfResidual = n - rank;
82
+ const w = W ?? new Array(n).fill(1);
83
+ let rss = 0;
84
+ for (let i = 0; i < n; i++)
85
+ rss += w[i] * fitResiduals[i] ** 2;
86
+ const resvar = rss / dfResidual;
87
+ const sigma = Math.sqrt(resvar);
88
+ // (R'R)^-1 of the leading rank x rank block, in pivot order
89
+ const Rr = q.qr.slice(0, rank).map((row) => row.slice(0, rank));
90
+ const covUnscaled = rank > 0 ? chol2inv(Rr) : [];
91
+ const piv = q.pivot.slice(0, rank);
92
+ const order = piv.map((col, k) => [col, k]).sort((a, b) => a[0] - b[0]);
93
+ const estimate = order.map(([col]) => coefficients[col]);
94
+ const stdError = order.map(([, k]) => Math.sqrt(covUnscaled[k][k] * resvar));
95
+ const tValue = estimate.map((e, i) => e / stdError[i]);
96
+ const pValue = tValue.map((t) => 2 * pt1(-Math.abs(t), dfResidual, true, false));
97
+ const result = {
98
+ coefficients,
99
+ residuals,
100
+ fittedValues,
101
+ rank,
102
+ dfResidual,
103
+ coefTable: { estimate, stdError, tValue, pValue },
104
+ sigma,
105
+ rSquared: 0,
106
+ adjRSquared: 0,
107
+ intercept,
108
+ qr: q,
109
+ };
110
+ const dfInt = intercept ? 1 : 0;
111
+ const numdf = rank - dfInt;
112
+ // nothing beyond the intercept: R^2 = 0 and no F test, as in summary.lm
113
+ if (numdf === 0)
114
+ return result;
115
+ let mss = 0;
116
+ if (intercept) {
117
+ const sumw = w.reduce((a, b) => a + b, 0);
118
+ let m = 0;
119
+ for (let i = 0; i < n; i++)
120
+ m += (w[i] * fitted[i]) / sumw;
121
+ for (let i = 0; i < n; i++)
122
+ mss += w[i] * (fitted[i] - m) ** 2;
123
+ }
124
+ else
125
+ for (let i = 0; i < n; i++)
126
+ mss += w[i] * fitted[i] ** 2;
127
+ result.rSquared = mss / (mss + rss);
128
+ result.adjRSquared = 1 - (1 - result.rSquared) * ((n - dfInt) / dfResidual);
129
+ const fValue = mss / numdf / resvar;
130
+ result.fStatistic = { value: fValue, numdf, dendf: dfResidual };
131
+ result.fPValue = pf1(fValue, numdf, dfResidual, false, false);
132
+ return result;
133
+ }
134
+ /**
135
+ * Confidence intervals for the coefficients of a linear model (NaN for aliased columns). Equivalent to R's
136
+ * `confint()` for an `lm` fit.
137
+ */
138
+ export function confint(fit, options = {}) {
139
+ const level = options.level ?? 0.95;
140
+ const t = qt1((1 + level) / 2, fit.dfResidual, true, false);
141
+ const { estimate, stdError } = fit.coefTable;
142
+ const lower = fit.coefficients.map(() => NaN);
143
+ const upper = fit.coefficients.map(() => NaN);
144
+ let k = 0;
145
+ fit.coefficients.forEach((c, j) => {
146
+ if (Number.isNaN(c))
147
+ return;
148
+ lower[j] = estimate[k] - t * stdError[k];
149
+ upper[j] = estimate[k] + t * stdError[k];
150
+ k++;
151
+ });
152
+ return { lower, upper };
153
+ }
154
+ /**
155
+ * Predictions of a linear model at new values of the predictors, with standard errors and optionally intervals.
156
+ * Equivalent to R's `predict()` for an `lm` fit (`se.fit = TRUE`). Aliased columns are left out, as R does.
157
+ *
158
+ * @param newdata New predictor values, with the columns of `x` (no column of ones).
159
+ */
160
+ export function predict(fit, newdata, options = {}) {
161
+ const rows = newdata.length > 0 && typeof newdata[0] !== "number"
162
+ ? newdata.map((r) => Array.from(r))
163
+ : Array.from(newdata, (v) => [v]);
164
+ const X = rows.map((r) => (fit.intercept ? [1, ...r] : r));
165
+ const rank = fit.rank;
166
+ const piv = fit.qr.pivot.slice(0, rank);
167
+ const Rr = fit.qr.qr.slice(0, rank).map((row) => row.slice(0, rank));
168
+ const resvar = fit.sigma ** 2;
169
+ const values = X.map((r) => r.reduce((s, v, j) => (Number.isNaN(fit.coefficients[j]) ? s : s + v * fit.coefficients[j]), 0));
170
+ // se: || R^-T x_piv || sigma, by forward substitution with R^T
171
+ const seFit = X.map((r) => {
172
+ const z = new Array(rank).fill(0);
173
+ for (let i = 0; i < rank; i++) {
174
+ let s = r[piv[i]];
175
+ for (let k = 0; k < i; k++)
176
+ s -= Rr[k][i] * z[k];
177
+ z[i] = s / Rr[i][i];
178
+ }
179
+ return Math.sqrt(z.reduce((a, v) => a + v * v, 0) * resvar);
180
+ });
181
+ const interval = options.interval ?? "none";
182
+ if (interval === "none")
183
+ return { fit: values, seFit };
184
+ const t = qt1((1 + (options.level ?? 0.95)) / 2, fit.dfResidual, true, false);
185
+ const half = seFit.map((s) => t * Math.sqrt(s * s + (interval === "prediction" ? resvar : 0)));
186
+ return { fit: values, seFit, lwr: values.map((v, i) => v - half[i]), upr: values.map((v, i) => v + half[i]) };
187
+ }
@@ -0,0 +1,37 @@
1
+ /** Options for {@link grad}, {@link jacobian} and {@link hessian}. */
2
+ export interface DerivOptions {
3
+ /** `"Richardson"` (default) or `"simple"` (forward difference; not for `hessian`). */
4
+ method?: "Richardson" | "simple";
5
+ /** Step for "simple", and for coordinates near 0 in "Richardson". Default `1e-4`. */
6
+ eps?: number;
7
+ /** Relative initial step of "Richardson". Default `1e-4` (`0.1` for `hessian`). */
8
+ d?: number;
9
+ /** Coordinates with |x| below this get the step `eps`. Default `sqrt(2.220446e-16 / 7e-7)`. */
10
+ zeroTol?: number;
11
+ /** Number of halvings of the step (Richardson iterations). Default `4`. */
12
+ r?: number;
13
+ }
14
+ /**
15
+ * Gradient of a scalar function of a vector. Equivalent to `numDeriv::grad(func, x, method)`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * grad((x) => x[0]! ** 2 + Math.sin(x[1]!), [1, 0]); // ≈ [2, 1]
20
+ * ```
21
+ */
22
+ export declare function grad(func: (x: number[]) => number, x: readonly number[], options?: DerivOptions): number[];
23
+ /**
24
+ * Jacobian of a vector function of a vector: row i holds the derivatives of the i-th value. Equivalent to
25
+ * `numDeriv::jacobian(func, x, method)`.
26
+ */
27
+ export declare function jacobian(func: (x: number[]) => readonly number[], x: readonly number[], options?: DerivOptions): number[][];
28
+ /**
29
+ * Hessian matrix of a scalar function of a vector, by Richardson extrapolation (Bates and Watts). Equivalent to
30
+ * `numDeriv::hessian(func, x)`.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * hessian((x) => x[0]! ** 2 * x[1]!, [1, 2]); // ≈ [[4, 2], [2, 0]]
35
+ * ```
36
+ */
37
+ export declare function hessian(func: (x: number[]) => number, x: readonly number[], options?: DerivOptions): number[][];
@@ -0,0 +1,150 @@
1
+ // Numerical derivatives: numDeriv's grad, jacobian and hessian (methods "Richardson" and "simple").
2
+ //
3
+ // Written for rStats.js from the textbook methods (no ported code); the defaults and the choice of steps follow
4
+ // numDeriv's documentation so the results agree with it. Richardson: central differences with steps h, h/2, ...,
5
+ // h/2^(r-1), h = d |x_i| (eps where |x_i| is below zeroTol), combined by Richardson extrapolation
6
+ // A <- (4^m A(h/2) - A(h)) / (4^m - 1) for m = 1, ..., r - 1. The Hessian is that of Bates and Watts (1988): the
7
+ // diagonal from second central differences, the off-diagonal entries from differences along e_i + e_j less the
8
+ // diagonal terms, each extrapolated the same way (with d = 0.1). "simple" is the forward difference with step eps.
9
+ // numDeriv's "complex" (complex-step) method needs a function of a complex argument and is not offered.
10
+ function steps(x, options, d0) {
11
+ const eps = options.eps ?? 1e-4;
12
+ const d = options.d ?? d0;
13
+ const zeroTol = options.zeroTol ?? Math.sqrt(Number.EPSILON / 7e-7);
14
+ const r = options.r ?? 4;
15
+ if (!(Number.isInteger(r) && r >= 1))
16
+ throw new RangeError("'r' must be a positive integer");
17
+ return { eps, r, h0: x.map((v) => Math.abs(d * v) + (Math.abs(v) < zeroTol ? eps : 0)) };
18
+ }
19
+ /** Richardson extrapolation of approximations at steps h, h/2, h/4, ... (errors in even powers of h) */
20
+ function extrapolate(a) {
21
+ const t = [...a];
22
+ for (let m = 1; m < t.length; m++) {
23
+ const p = 4 ** m;
24
+ for (let k = 0; k < t.length - m; k++)
25
+ t[k] = (t[k + 1] * p - t[k]) / (p - 1);
26
+ }
27
+ return t[0];
28
+ }
29
+ function shifted(x, i, h) {
30
+ const y = [...x];
31
+ y[i] = y[i] + h;
32
+ return y;
33
+ }
34
+ function method(options) {
35
+ const m = options.method ?? "Richardson";
36
+ if (m !== "Richardson" && m !== "simple")
37
+ throw new RangeError(`unknown method "${String(m)}" (expected "Richardson" or "simple")`);
38
+ return m;
39
+ }
40
+ /**
41
+ * Gradient of a scalar function of a vector. Equivalent to `numDeriv::grad(func, x, method)`.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * grad((x) => x[0]! ** 2 + Math.sin(x[1]!), [1, 0]); // ≈ [2, 1]
46
+ * ```
47
+ */
48
+ export function grad(func, x, options = {}) {
49
+ const xs = [...x];
50
+ const n = xs.length;
51
+ if (method(options) === "simple") {
52
+ const eps = options.eps ?? 1e-4;
53
+ const f0 = func([...xs]);
54
+ return xs.map((_, i) => (func(shifted(xs, i, eps)) - f0) / eps);
55
+ }
56
+ const { r, h0 } = steps(xs, options, 1e-4);
57
+ const out = [];
58
+ for (let i = 0; i < n; i++) {
59
+ const a = [];
60
+ let h = h0[i];
61
+ for (let k = 0; k < r; k++) {
62
+ // a central difference below 1e-20 is taken as 0 at the smaller steps (functions unstable near 0), as numDeriv does
63
+ if (k > 0 && Math.abs(a[k - 1]) < 1e-20)
64
+ a.push(0);
65
+ else
66
+ a.push((func(shifted(xs, i, h)) - func(shifted(xs, i, -h))) / (2 * h));
67
+ if (Number.isNaN(a[k]))
68
+ throw new RangeError(`func returned NaN at step ${h} from x`);
69
+ h /= 2;
70
+ }
71
+ out.push(extrapolate(a));
72
+ }
73
+ return out;
74
+ }
75
+ /**
76
+ * Jacobian of a vector function of a vector: row i holds the derivatives of the i-th value. Equivalent to
77
+ * `numDeriv::jacobian(func, x, method)`.
78
+ */
79
+ export function jacobian(func, x, options = {}) {
80
+ const xs = [...x];
81
+ const n = xs.length;
82
+ const f0 = Array.from(func([...xs]));
83
+ const J = f0.map(() => new Array(n).fill(NaN));
84
+ if (method(options) === "simple") {
85
+ const eps = options.eps ?? 1e-4;
86
+ for (let j = 0; j < n; j++) {
87
+ const f1 = func(shifted(xs, j, eps));
88
+ f0.forEach((v, i) => (J[i][j] = (f1[i] - v) / eps));
89
+ }
90
+ return J;
91
+ }
92
+ const { r, h0 } = steps(xs, options, 1e-4);
93
+ for (let j = 0; j < n; j++) {
94
+ const a = f0.map(() => []);
95
+ let h = h0[j];
96
+ for (let k = 0; k < r; k++) {
97
+ const fp = func(shifted(xs, j, h));
98
+ const fm = func(shifted(xs, j, -h));
99
+ f0.forEach((_, i) => a[i].push((fp[i] - fm[i]) / (2 * h)));
100
+ h /= 2;
101
+ }
102
+ f0.forEach((_, i) => (J[i][j] = extrapolate(a[i])));
103
+ }
104
+ return J;
105
+ }
106
+ /**
107
+ * Hessian matrix of a scalar function of a vector, by Richardson extrapolation (Bates and Watts). Equivalent to
108
+ * `numDeriv::hessian(func, x)`.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * hessian((x) => x[0]! ** 2 * x[1]!, [1, 2]); // ≈ [[4, 2], [2, 0]]
113
+ * ```
114
+ */
115
+ export function hessian(func, x, options = {}) {
116
+ if ((options.method ?? "Richardson") !== "Richardson")
117
+ throw new RangeError('hessian supports only method "Richardson"');
118
+ const xs = [...x];
119
+ const n = xs.length;
120
+ const { r, h0 } = steps(xs, options, 0.1);
121
+ const f0 = func([...xs]);
122
+ const diag = [];
123
+ for (let i = 0; i < n; i++) {
124
+ const a = [];
125
+ let h = h0[i];
126
+ for (let k = 0; k < r; k++) {
127
+ a.push((func(shifted(xs, i, h)) - 2 * f0 + func(shifted(xs, i, -h))) / h ** 2);
128
+ h /= 2;
129
+ }
130
+ diag.push(extrapolate(a));
131
+ }
132
+ const H = xs.map(() => new Array(n).fill(NaN));
133
+ for (let i = 0; i < n; i++) {
134
+ H[i][i] = diag[i];
135
+ for (let j = 0; j < i; j++) {
136
+ const a = [];
137
+ let hi = h0[i];
138
+ let hj = h0[j];
139
+ for (let k = 0; k < r; k++) {
140
+ const up = shifted(shifted(xs, i, hi), j, hj);
141
+ const down = shifted(shifted(xs, i, -hi), j, -hj);
142
+ a.push((func(up) - 2 * f0 + func(down) - diag[i] * hi ** 2 - diag[j] * hj ** 2) / (2 * hi * hj));
143
+ hi /= 2;
144
+ hj /= 2;
145
+ }
146
+ H[i][j] = H[j][i] = extrapolate(a);
147
+ }
148
+ }
149
+ return H;
150
+ }
@@ -0,0 +1,91 @@
1
+ /** Options for {@link integrate}. */
2
+ export interface IntegrateOptions {
3
+ /** Maximum number of subintervals. Default `100`. */
4
+ subdivisions?: number;
5
+ /** Relative accuracy requested. Default `eps^0.25` (about 1.2e-4). */
6
+ relTol?: number;
7
+ /** Absolute accuracy requested. Default `relTol`. */
8
+ absTol?: number;
9
+ /** Throw when QUADPACK reports a problem (default `true`); otherwise return the result with its code. */
10
+ stopOnError?: boolean;
11
+ }
12
+ /** Result of {@link integrate}. */
13
+ export interface IntegrateResult {
14
+ value: number;
15
+ /** Estimate of the absolute error. */
16
+ absError: number;
17
+ /** Number of subintervals used. */
18
+ subdivisions: number;
19
+ /** QUADPACK's code: 0 OK, 1 maximum number of subdivisions reached, 2 roundoff error, 3 bad integrand
20
+ * behaviour, 4 roundoff error in the extrapolation table, 5 probably divergent, 6 invalid input. */
21
+ ier: number;
22
+ /** R's message for `ier`. */
23
+ message: string;
24
+ }
25
+ /**
26
+ * Integral of a function of one variable over a finite or infinite range. Equivalent to R's `integrate()`.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * integrate((x) => Math.exp(-x * x / 2), -Infinity, Infinity).value; // 2.5066282746310002 (sqrt(2 pi))
31
+ * ```
32
+ */
33
+ export declare function integrate(f: (x: number) => number, lower: number, upper: number, options?: IntegrateOptions): IntegrateResult;
34
+ /** Options for {@link optimize}. */
35
+ export interface OptimizeOptions {
36
+ /** Maximise instead of minimise. Default `false`. */
37
+ maximum?: boolean;
38
+ /** Accuracy. Default `eps^0.25`. */
39
+ tol?: number;
40
+ }
41
+ /**
42
+ * Minimum (or maximum) of a function of one variable on an interval, by Brent's golden-section and parabolic
43
+ * search. Equivalent to R's `optimize()`: `optimum` is the location (R's `minimum`/`maximum`) and `objective` the
44
+ * function there. `lower` must be less than `upper`, as in R.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * optimize((x) => (x - 1 / 3) ** 2, 0, 1).optimum; // 0.3333333333333333
49
+ * ```
50
+ */
51
+ export declare function optimize(f: (x: number) => number, lower: number, upper: number, options?: OptimizeOptions): {
52
+ optimum: number;
53
+ objective: number;
54
+ };
55
+ /** Options for {@link uniroot}. */
56
+ export interface UnirootOptions {
57
+ /** Accuracy. Default `eps^0.25`. */
58
+ tol?: number;
59
+ /** Maximum number of iterations. Default `1000`. */
60
+ maxiter?: number;
61
+ /**
62
+ * Extend the interval when f has the same sign at both ends: `"yes"` (both ways), `"downX"` (f decreasing),
63
+ * `"upX"` (f increasing) or `"no"` (default).
64
+ */
65
+ extendInt?: "no" | "yes" | "downX" | "upX";
66
+ /** f(lower), when already known. */
67
+ fLower?: number;
68
+ /** f(upper), when already known. */
69
+ fUpper?: number;
70
+ }
71
+ /** Result of {@link uniroot}. */
72
+ export interface UnirootResult {
73
+ root: number;
74
+ /** f at the root. */
75
+ fRoot: number;
76
+ /** Iterations used (including those extending the interval). */
77
+ iter: number;
78
+ /** Iterations used to extend the interval (NaN when it was not extended). */
79
+ initIt: number;
80
+ /** Brent's estimate of the precision of the root. */
81
+ estimPrec: number;
82
+ }
83
+ /**
84
+ * A root of a function of one variable in an interval, by Brent's zeroin. Equivalent to R's `uniroot()`.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * uniroot((x) => x * x - 2, 0, 2, { tol: 1e-12 }).root; // 1.4142135623731364
89
+ * ```
90
+ */
91
+ export declare function uniroot(f: (x: number) => number, lower: number, upper: number, options?: UnirootOptions): UnirootResult;