@compstats/core 0.3.0 → 0.4.0

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 (42) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +117 -116
  3. package/dist/3d.js +1120 -122
  4. package/dist/3d.js.map +16 -9
  5. package/dist/core/arith.d.ts.map +1 -1
  6. package/dist/core/linalg/cov.d.ts +50 -0
  7. package/dist/core/linalg/cov.d.ts.map +1 -0
  8. package/dist/core/linalg/eigen.d.ts +53 -0
  9. package/dist/core/linalg/eigen.d.ts.map +1 -0
  10. package/dist/core/linalg/lm.d.ts +78 -0
  11. package/dist/core/linalg/lm.d.ts.map +1 -0
  12. package/dist/core/linalg/lu.d.ts +154 -0
  13. package/dist/core/linalg/lu.d.ts.map +1 -0
  14. package/dist/core/linalg/matrix.d.ts +131 -0
  15. package/dist/core/linalg/matrix.d.ts.map +1 -0
  16. package/dist/core/linalg/modelMatrix.d.ts +69 -0
  17. package/dist/core/linalg/modelMatrix.d.ts.map +1 -0
  18. package/dist/core/linalg/namedVector.d.ts +37 -0
  19. package/dist/core/linalg/namedVector.d.ts.map +1 -0
  20. package/dist/core/linalg/ops.d.ts +120 -0
  21. package/dist/core/linalg/ops.d.ts.map +1 -0
  22. package/dist/core/linalg/prcomp.d.ts +66 -0
  23. package/dist/core/linalg/prcomp.d.ts.map +1 -0
  24. package/dist/core/linalg/qr.d.ts +134 -0
  25. package/dist/core/linalg/qr.d.ts.map +1 -0
  26. package/dist/core/linalg/vector.d.ts +68 -0
  27. package/dist/core/linalg/vector.d.ts.map +1 -0
  28. package/dist/core/moderation.d.ts +6 -3
  29. package/dist/core/moderation.d.ts.map +1 -1
  30. package/dist/core/ols.d.ts +4 -7
  31. package/dist/core/ols.d.ts.map +1 -1
  32. package/dist/data/moderationData.d.ts +2 -2
  33. package/dist/data/pcaDegenerate.d.ts +1 -1
  34. package/dist/index.js +1455 -863
  35. package/dist/index.js.map +16 -10
  36. package/dist/linalg.d.ts +36 -0
  37. package/dist/linalg.d.ts.map +1 -0
  38. package/dist/linalg.js +1860 -0
  39. package/dist/linalg.js.map +24 -0
  40. package/dist/plot/moderation3d.d.ts +1 -1
  41. package/dist/plot/scatter3d.d.ts +1 -1
  42. package/package.json +7 -2
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The elementary matrix operations, named as R names them.
3
+ *
4
+ * `t()`, `%*%` (`matmul`), `crossprod()`, `tcrossprod()`, `cbind()`,
5
+ * `rbind()` and `diag()`. Each takes matrices (and, where R allows it, plain
6
+ * vectors) and returns a new matrix; nothing here modifies its input.
7
+ *
8
+ * A bare vector in a product takes the shape R gives it, which is not
9
+ * always the one that would conform (fixtures 1g and 1h probe the rules).
10
+ * In `%*%`, a left vector is a row when its length matches the rows of the
11
+ * right factor and otherwise a column; a right vector is a column when its
12
+ * length matches the columns of the left factor and otherwise a row; two
13
+ * vectors give their inner product. In `crossprod` a vector `x` is always
14
+ * a column, and a vector `y` is a column when its length matches the rows
15
+ * of `x` and otherwise a row. In `tcrossprod` a vector `x` is a row when
16
+ * its length matches the columns of a matrix `y` and otherwise a column,
17
+ * and a vector `y` is a row only when `x` has one row; two vectors are both
18
+ * columns, the outer product.
19
+ *
20
+ * Dimnames travel as they do in R: a transpose swaps them, a product keeps
21
+ * the row names of the left factor and the column names of the right, and
22
+ * binding stacks them, with `""` for a bare vector joined to a named matrix.
23
+ *
24
+ * The product follows the reference BLAS `dgemm` that R ships: the loop
25
+ * over `i` innermost walks each column of the left factor in order, and each
26
+ * product is rounded once into its running sum with `fusedMultiplyAdd`,
27
+ * because the build the conformance fixtures come from contracts
28
+ * `C + A * B` into one instruction (the fixture README records this; the LU
29
+ * and QR here already follow it). Index loops throughout: a product
30
+ * addresses entries by position. (CLAUDE.md allows an index loop with a
31
+ * stated reason; that is the reason.)
32
+ */
33
+ import { type Matrix } from "./matrix";
34
+ import type { Vector } from "./vector";
35
+ /** A matrix, or a vector R would treat as a one-column matrix. */
36
+ export type MatrixOrVector = Matrix | Vector;
37
+ /** R's `t()`. Also exported as `transpose`, for an app whose `t` is already taken. */
38
+ export declare function t(m: Matrix): Matrix;
39
+ /** R's `t()`, under a name that does not collide with an i18n `t`. */
40
+ export declare const transpose: typeof t;
41
+ /**
42
+ * R's `x %*% y`.
43
+ *
44
+ * @param x The left factor. A vector is a row when its length matches the
45
+ * rows of `y`, else a column when `y` has one row.
46
+ * @param y The right factor. A vector is a column when its length matches
47
+ * the columns of `x`, else a row when `x` has one column. Two vectors of
48
+ * one length give their inner product as a 1 x 1 matrix.
49
+ * @returns The product, with the row names of `x` and the column names of
50
+ * `y`.
51
+ * @throws RangeError If the inner extents differ: R's "non-conformable
52
+ * arguments".
53
+ */
54
+ export declare function matmul(x: MatrixOrVector, y: MatrixOrVector): Matrix;
55
+ /**
56
+ * R's `crossprod(x, y)`: `t(x) %*% y`, with `crossprod(x)` for `t(x) %*% x`.
57
+ * A bare vector `x` is a column; a bare vector `y` is a column when its
58
+ * length matches the rows of `x`, else a row (fixture 1h).
59
+ *
60
+ * @throws RangeError If the row counts differ.
61
+ */
62
+ export declare function crossprod(x: MatrixOrVector, y?: MatrixOrVector): Matrix;
63
+ /**
64
+ * R's `tcrossprod(x, y)`: `x %*% t(y)`, with `tcrossprod(x)` for
65
+ * `x %*% t(x)`. A bare vector `x` is a row when its length matches the
66
+ * columns of a matrix `y`, else a column; a bare vector `y` is a row only
67
+ * when `x` has one row, and two bare vectors are both columns (fixture 1h).
68
+ *
69
+ * @throws RangeError If the column counts differ.
70
+ */
71
+ export declare function tcrossprod(x: MatrixOrVector, y?: MatrixOrVector): Matrix;
72
+ /**
73
+ * Tell a matrix from a vector by shape, and refuse anything else — a typed
74
+ * array or a stray object — with a TypeError rather than a wrong shape.
75
+ *
76
+ * @internal Shared with the other linalg modules; not part of the entry.
77
+ */
78
+ export declare function isMatrix(value: MatrixOrVector): value is Matrix;
79
+ /**
80
+ * R's `cbind()`: join matrices and vectors side by side.
81
+ *
82
+ * @param parts Matrices, and vectors taken as columns. At least one.
83
+ * @returns The joined matrix. Row names come from the first part that has
84
+ * them. Column names are kept when any part has them, with `""` for a part
85
+ * that does not, as R names a bare vector.
86
+ * @throws RangeError If the row counts differ, in R's words for a matrix
87
+ * part and for a vector part. R recycles a short vector with a warning;
88
+ * the port refuses.
89
+ */
90
+ export declare function cbind(...parts: readonly MatrixOrVector[]): Matrix;
91
+ /**
92
+ * R's `rbind()`: stack matrices and vectors.
93
+ *
94
+ * @param parts Matrices, and vectors taken as rows. At least one.
95
+ * @returns The stacked matrix, with names as `cbind` carries them, rows and
96
+ * columns exchanged.
97
+ * @throws RangeError If the column counts differ.
98
+ */
99
+ export declare function rbind(...parts: readonly MatrixOrVector[]): Matrix;
100
+ /**
101
+ * R's `diag()` in its three forms: a vector gives the diagonal matrix of it,
102
+ * a count gives the identity of that order, and a matrix gives its diagonal.
103
+ *
104
+ * The form is chosen by type, not by length, which is R's own gotcha
105
+ * removed: `diag([5])` is the 1 x 1 matrix `[5]` where R's `diag(c(5))` is
106
+ * the 5 x 5 identity, and `diag(2.5)` refuses where R truncates to 2 x 2.
107
+ * The diagonal of a matrix comes back as a plain array; R names it when the
108
+ * row and column names agree, which the port will do once a named vector
109
+ * exists (plan Q11).
110
+ */
111
+ export declare function diag(values: Vector): Matrix;
112
+ export declare function diag(order: number): Matrix;
113
+ export declare function diag(m: Matrix): number[];
114
+ /**
115
+ * The identity matrix of the given order. R's `diag(n)`.
116
+ *
117
+ * @throws RangeError If the order is not a non-negative integer.
118
+ */
119
+ export declare function identity(order: number): Matrix;
120
+ //# sourceMappingURL=ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../../src/core/linalg/ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,sFAAsF;AACtF,wBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAWnC;AAED,sEAAsE;AACtE,eAAO,MAAM,SAAS,UAAI,CAAC;AAE3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CASnE;AAkBD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,GAAE,cAAkB,GAAG,MAAM,CAS1E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,GAAE,cAAkB,GAAG,MAAM,CAc3E;AA0CD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,IAAI,MAAM,CAc/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CA8BjE;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,GAAG,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAejE;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;AAC7C,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;AAC5C,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAmB1C;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS9C"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * R's `prcomp()`: principal components of a matrix or a data frame, any
3
+ * number of variables.
4
+ *
5
+ * R centers (and optionally scales) the columns and takes the singular
6
+ * value decomposition of the result. The port centers and scales the same
7
+ * way and then decomposes the covariance matrix with `eigenSymmetric`: the
8
+ * standard deviations are the square roots of its eigenvalues, the rotation
9
+ * its eigenvectors, and the scores the centered data times the rotation.
10
+ * The two routes agree to a relative `1e-12` on the standard deviations and
11
+ * up to the sign of each component on the rest (plan Q1), which the SVD
12
+ * leaves arbitrary as well; the port's sign rule is `eigenSymmetric`'s.
13
+ * Verified in `prcomp.test.ts`, including agreement with the two-variable
14
+ * `principalComponents` of `pca.ts` on the bundled `pcaDegenerate` points.
15
+ *
16
+ * As `pca.ts` does, the port returns one component per variable and never
17
+ * reduces the rank: a collinear input reports a near-zero standard
18
+ * deviation rather than a shorter result. R's `prcomp` returns `min(n, p)`
19
+ * components, which is the same unless there are fewer rows than columns.
20
+ */
21
+ import type { DataFrame } from "../frame";
22
+ import { type Matrix } from "./matrix";
23
+ import type { Vector } from "./vector";
24
+ export interface PrcompOptions {
25
+ /** Subtract the column means first. True by default, as R's is. */
26
+ readonly center?: boolean;
27
+ /**
28
+ * Divide each column by its root mean square after centering — its
29
+ * standard deviation, when centered. False by default, as R's is.
30
+ */
31
+ readonly scale?: boolean;
32
+ }
33
+ /** R's `prcomp` result. */
34
+ export interface Prcomp {
35
+ /** The standard deviation along each component, largest first. */
36
+ readonly sdev: Vector;
37
+ /**
38
+ * The loadings: one row per variable, one column per component, named
39
+ * `PC1`, `PC2`, … with the variable names as row names when the input
40
+ * has column names.
41
+ */
42
+ readonly rotation: Matrix;
43
+ /** The value subtracted from each column; zeros when not centered. */
44
+ readonly center: Vector;
45
+ /** The value each column was divided by, or null when not scaled. */
46
+ readonly scale: Vector | null;
47
+ /**
48
+ * The scores: the centered, scaled data in component coordinates, with
49
+ * the row names of the input and `PC1`, `PC2`, … as column names, as R's
50
+ * are (fixture 5e).
51
+ */
52
+ readonly x: Matrix;
53
+ }
54
+ /**
55
+ * Compute the principal components, as R's `prcomp()` does.
56
+ *
57
+ * @param input A matrix with one column per variable, or a data frame whose
58
+ * numeric columns are the variables.
59
+ * @param options Whether to center and whether to scale.
60
+ * @returns The standard deviations, the rotation, the centering and scaling
61
+ * applied, and the scores.
62
+ * @throws RangeError If any value is missing or infinite (R's `svd` refuses
63
+ * the same), or if a column to be scaled is constant, in R's words.
64
+ */
65
+ export declare function prcomp(input: Matrix | DataFrame, options?: PrcompOptions): Prcomp;
66
+ //# sourceMappingURL=prcomp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prcomp.d.ts","sourceRoot":"","sources":["../../../src/core/linalg/prcomp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAExD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,2BAA2B;AAC3B,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAwDrF"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The QR factorization, R's `qr()` with `LAPACK = FALSE`.
3
+ *
4
+ * R factors a design with LINPACK's `dqrdc2`, a Householder QR with a
5
+ * limited column-pivoting rule: a column whose norm has collapsed against
6
+ * the columns to its left is moved to the right edge and its coefficient is
7
+ * reported as `NA`. That rule is what makes `lm()` and `glm()` report an
8
+ * aliased coefficient instead of dividing by a near-zero pivot. This module
9
+ * reproduces it and exposes R's result — the compact `qr` matrix, `qraux`,
10
+ * `pivot` and `rank` — and R's readers of it: `qr.coef`, `qr.fitted`,
11
+ * `qr.resid`, `qr.qy`, `qr.qty`, `qr.Q` and `qr.R`. Verified against R in
12
+ * `qr.test.ts`; `leastSquares` in `../ols.ts` is a wrapper over it.
13
+ *
14
+ * Designs here are small — two columns for logit, four for a moderation
15
+ * surface — so the code follows the LINPACK routines plainly rather than
16
+ * blocking or vectorizing them. Index loops throughout: a factorization
17
+ * addresses single entries by position, and this one follows `dqrdc2` and
18
+ * `dqrsl` step for step so that the fixtures pin bit for bit.
19
+ */
20
+ import { type Matrix } from "./matrix";
21
+ import type { Vector } from "./vector";
22
+ /** R's `qr()` result. */
23
+ export interface QrDecomposition {
24
+ /**
25
+ * The compact factorization, R's `qr$qr`: `R` on and above the diagonal,
26
+ * the Householder vectors below it, columns in pivot order. Column names
27
+ * follow the pivot, as R's do.
28
+ */
29
+ readonly qr: Matrix;
30
+ /**
31
+ * R's `qr$qraux`: for each reflected column, the leading entry of its
32
+ * Householder reflector; for a column never reflected — the trailing
33
+ * column of a square or wide design, or an aliased column — its remaining
34
+ * norm, which is what R reports there (fixtures 2b, 2c, 2g).
35
+ */
36
+ readonly qraux: Vector;
37
+ /**
38
+ * The column order after pivoting, R's `qr$pivot` **zero-based**:
39
+ * `pivot[k]` is the original index of the column now at position `k`.
40
+ * The aliased columns are at the end.
41
+ */
42
+ readonly pivot: readonly number[];
43
+ /** The number of columns the factorization could identify. */
44
+ readonly rank: number;
45
+ }
46
+ export interface QrOptions {
47
+ /**
48
+ * How far a column's norm may collapse before it is aliased: the column is
49
+ * moved to the end when its remaining norm falls below this fraction of
50
+ * its original norm. The default is R's `qr()` and `lm.fit()` default.
51
+ * `glm.fit()` passes `min(1e-7, epsilon / 1000)`. Zero aliases nothing,
52
+ * as R's `tol = 0` does; a negative or NaN value is refused, where R
53
+ * would pass it through — a deliberate narrowing.
54
+ */
55
+ readonly tolerance?: number;
56
+ }
57
+ /** The rank tolerance of R's `qr()` and `lm.fit()`. */
58
+ export declare const DEFAULT_QR_TOLERANCE = 1e-7;
59
+ /**
60
+ * Factor a matrix, as R's `qr(x, LAPACK = FALSE)` does.
61
+ *
62
+ * @param x The matrix. The function does not modify it. Row names travel
63
+ * onto the compact form; column names follow the pivot.
64
+ * @param options The rank tolerance.
65
+ * @returns The compact factorization, `qraux`, the pivot, and the rank.
66
+ * @throws RangeError If the tolerance is negative or NaN, or if an entry is
67
+ * not finite — R's "NA/NaN/Inf in foreign function call (arg 1)". NaN is
68
+ * this library's missing value; a caller drops incomplete rows first, as
69
+ * `modelMatrix` does.
70
+ * @throws TypeError If `x` is not a matrix.
71
+ */
72
+ export declare function qr(x: Matrix, options?: QrOptions): QrDecomposition;
73
+ /**
74
+ * Solve for the coefficients, R's `qr.coef(qr, y)`.
75
+ *
76
+ * @param q The factorization.
77
+ * @param y The response, one value per row — or a matrix with one response
78
+ * per column, as R also takes.
79
+ * @returns One coefficient per column of the factored matrix, **in the
80
+ * original column order**, with null for an aliased column (R's `NA`).
81
+ * For a matrix `y`, a matrix with one column per response, the factored
82
+ * matrix's column names as row names and `y`'s column names as column
83
+ * names; an aliased column reads NaN there, since a matrix holds no null.
84
+ * @throws RangeError If `y` has the wrong number of rows.
85
+ */
86
+ export declare function qrCoef(q: QrDecomposition, y: Vector): (number | null)[];
87
+ export declare function qrCoef(q: QrDecomposition, y: Matrix): Matrix;
88
+ /**
89
+ * The fitted values, R's `qr.fitted(qr, y)`: `Q` applied to the first `rank`
90
+ * entries of `Qᵀy`, the rest set to zero. A matrix `y` gives a matrix with
91
+ * its column names.
92
+ *
93
+ * @throws RangeError If `y` has the wrong number of rows.
94
+ */
95
+ export declare function qrFitted(q: QrDecomposition, y: Vector): number[];
96
+ export declare function qrFitted(q: QrDecomposition, y: Matrix): Matrix;
97
+ /**
98
+ * The residuals, R's `qr.resid(qr, y)`: `Q` applied to `Qᵀy` with its first
99
+ * `rank` entries set to zero. A matrix `y` gives a matrix with its column
100
+ * names.
101
+ *
102
+ * @throws RangeError If `y` has the wrong number of rows.
103
+ */
104
+ export declare function qrResid(q: QrDecomposition, y: Vector): number[];
105
+ export declare function qrResid(q: QrDecomposition, y: Matrix): Matrix;
106
+ /**
107
+ * `Qᵀy`, R's `qr.qty(qr, y)`: the reflectors applied first to last. A
108
+ * matrix `y` gives a matrix with its column names.
109
+ *
110
+ * @throws RangeError If `y` has the wrong number of rows.
111
+ */
112
+ export declare function qrQty(q: QrDecomposition, y: Vector): number[];
113
+ export declare function qrQty(q: QrDecomposition, y: Matrix): Matrix;
114
+ /**
115
+ * `Qy`, R's `qr.qy(qr, y)`: the reflectors applied last to first. A matrix
116
+ * `y` gives a matrix with its column names.
117
+ *
118
+ * @throws RangeError If `y` has the wrong number of rows.
119
+ */
120
+ export declare function qrQy(q: QrDecomposition, y: Vector): number[];
121
+ export declare function qrQy(q: QrDecomposition, y: Matrix): Matrix;
122
+ /**
123
+ * The orthogonal factor, R's `qr.Q(qr)`: `n` rows by `min(n, p)` columns —
124
+ * `Q` applied to the leading columns of the identity. It carries no names,
125
+ * as R's does not.
126
+ */
127
+ export declare function qrQ(q: QrDecomposition): Matrix;
128
+ /**
129
+ * The triangular factor, R's `qr.R(qr)`: `min(n, p)` rows by `p` columns,
130
+ * the upper triangle of the compact form, with the leading row names and
131
+ * the column names in pivot order.
132
+ */
133
+ export declare function qrR(q: QrDecomposition): Matrix;
134
+ //# sourceMappingURL=qr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../../src/core/linalg/qr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAE5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,yBAAyB;AACzB,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,SAAc,GAAG,eAAe,CAgCtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;AACzE,wBAAgB,MAAM,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;AA0C9D;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAClE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;AAOhE;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AACjE,wBAAgB,OAAO,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;AAO/D;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAC/D,wBAAgB,KAAK,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;AAK7D;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;AAC9D,wBAAgB,IAAI,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;AA6C5D;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAU9C;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAY9C"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * R's operators over vectors, by name.
3
+ *
4
+ * R vectors are not objects with methods: `a + 1`, `a * b` and `sum(a * b)`
5
+ * are functions over plain vectors, with a scalar recycled to the length of
6
+ * the other argument. This module is those functions with the operator
7
+ * spelled out — `add(a, 1)`, `mul(a, b)`, `dot(a, b)` — over `Vector`,
8
+ * which is `Vector` and nothing more: a bare array is a vector
9
+ * (plan Q11, Q12).
10
+ *
11
+ * R recycles any shorter vector, with a warning when the lengths do not
12
+ * divide. The port recycles a scalar only and refuses every other mismatch:
13
+ * a silent recycle lets a typo fit the wrong model.
14
+ */
15
+ /**
16
+ * A numeric vector: R's atomic double vector, which here is a plain array of
17
+ * numbers. `NaN` is the missing value, as `NA_real_` is in R.
18
+ *
19
+ * The name is erased at compile time and structural, not nominal. Any
20
+ * `number[]` is a `Vector` and any `Vector` is an array, with nothing to wrap
21
+ * at either end. It names the concept in a signature and carries this note to
22
+ * every place one is taken; it constrains nothing, and in particular it does
23
+ * not check a length.
24
+ *
25
+ * A function *takes* a `Vector` and *returns* a fresh `number[]` — R's
26
+ * copy-on-modify, and the reason there is no mutable counterpart to this
27
+ * name. A vector is never an object with methods (plan Q12).
28
+ */
29
+ export type Vector = readonly number[];
30
+ /** A vector, or a scalar to recycle to the vector's length. */
31
+ export type VectorOrScalar = Vector | number;
32
+ /** R's `a + b`. */
33
+ export declare function add(a: Vector, b: VectorOrScalar): number[];
34
+ /** R's `a - b`. */
35
+ export declare function sub(a: Vector, b: VectorOrScalar): number[];
36
+ /** R's `a * b`. With a scalar, `2 * a`. */
37
+ export declare function mul(a: Vector, b: VectorOrScalar): number[];
38
+ /** R's `a / b`. */
39
+ export declare function div(a: Vector, b: VectorOrScalar): number[];
40
+ /** R's `a^2`. */
41
+ export declare function square(a: Vector): number[];
42
+ /**
43
+ * R's `sum(a * b)`: the inner product.
44
+ *
45
+ * @throws RangeError If the lengths differ.
46
+ */
47
+ export declare function dot(a: Vector, b: Vector): number;
48
+ /**
49
+ * R's `sqrt(sum(a^2))`: the Euclidean length.
50
+ *
51
+ * Written as a sum, not `Math.hypot(...a)`: the spread overflows the call
52
+ * stack on a long vector, and the sum is the form R computes, so the two
53
+ * round the same way.
54
+ */
55
+ export declare function norm(a: Vector): number;
56
+ /**
57
+ * The cosine of the angle between two vectors: `dot(a, b) / (norm(a) * norm(b))`.
58
+ *
59
+ * Computed in exactly that order, which is the order a reader writes it in
60
+ * R, so the port and R land on the same double — including
61
+ * `cosine(a, a) = 1.0000000000000002` for a vector whose norm does not
62
+ * square back to its sum of squares. A zero vector gives NaN, as `0 / 0` does
63
+ * in R.
64
+ *
65
+ * @throws RangeError If the lengths differ.
66
+ */
67
+ export declare function cosine(a: Vector, b: Vector): number;
68
+ //# sourceMappingURL=vector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector.d.ts","sourceRoot":"","sources":["../../../src/core/linalg/vector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;AAEvC,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAsB7C,mBAAmB;AACnB,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAE1D;AAED,mBAAmB;AACnB,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAE1D;AAED,2CAA2C;AAC3C,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAE1D;AAED,mBAAmB;AACnB,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAE1D;AAED,iBAAiB;AACjB,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -19,8 +19,11 @@
19
19
  * **The design follows R's model matrix, not the option order.** R's
20
20
  * `model.matrix` puts every main effect before any interaction, whatever
21
21
  * order the formula was typed in, so `y ~ x + z + w + x:z` gives the columns
22
- * `(Intercept), x, z, w, x:z`. This module builds them in that order, which
23
- * is what lets a caller read the coefficients next to R's.
22
+ * `(Intercept), x, z, w, x:z`. This module names the terms and hands them to
23
+ * `linalg/lm`, which is `model.matrix()` followed by `lm.fit()` and puts the
24
+ * columns in that order, so a caller can read the coefficients next to R's.
25
+ * Going through `lm` also makes the fitted values `y` minus the residuals of
26
+ * the factorization, which is R's own definition, rather than `X · β`.
24
27
  *
25
28
  * **Controls are held at their mean, and only numbers are accepted.** R's
26
29
  * `hold_value()` also handles factors (first level), characters (first in
@@ -28,7 +31,7 @@
28
31
  * column and no doc example uses one, so this port supports the numeric
29
32
  * branch alone and refuses the rest, rather than shipping three rules nothing
30
33
  * exercises. The other rules are recorded in
31
- * `.claude/plans/moderation-fixtures.md` section 4 if they are ever wanted.
34
+ * `.claude/plans/001-PLAN-port/moderation-fixtures.md` section 4 if they are ever wanted.
32
35
  *
33
36
  * **Missing values leave the fit, as R's do.** `lm()` drops incomplete rows
34
37
  * through `na.omit` and `hold_value()` averages with `na.rm = TRUE`; this
@@ -1 +1 @@
1
- {"version":3,"file":"moderation.d.ts","sourceRoot":"","sources":["../../src/core/moderation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAS1E,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,iBAAiB,GACzB,iBAAiB,CAoInB"}
1
+ {"version":3,"file":"moderation.d.ts","sourceRoot":"","sources":["../../src/core/moderation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAGH,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAS1E,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,SAAS,cAAc,EAAE,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,iBAAiB,GACzB,iBAAiB,CAyGnB"}
@@ -5,14 +5,11 @@
5
5
  * every step of `glm.fit()`'s IRLS loop. R factors the design with `dqrdc2`,
6
6
  * a Householder QR with a limited column-pivoting rule: a column whose norm
7
7
  * has collapsed against the columns to its left is moved to the right edge
8
- * and its coefficient is reported as `NA`. The port reproduces that rule,
9
- * because it is what makes `lm()` and `glm()` report an aliased coefficient
10
- * instead of dividing by a near-zero pivot. Verified against R in
8
+ * and its coefficient is reported as `NA`. That factorization lives in
9
+ * `linalg/qr.ts` as R's `qr()`; this module is the `lm.wfit()` wrapper over
10
+ * it the square-root weighting, the row-array interface the fitting
11
+ * functions use, and the fitted values as `X · β`. Verified against R in
11
12
  * `ols.test.ts`.
12
- *
13
- * Designs here are tiny — two columns for logit, four for a moderation
14
- * surface — so the code follows the LINPACK routine plainly rather than
15
- * blocking or vectorizing it.
16
13
  */
17
14
  /**
18
15
  * The result of a fit.
@@ -1 +1 @@
1
- {"version":3,"file":"ols.d.ts","sourceRoot":"","sources":["../../src/core/ols.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAClD,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,4CAA4C;AAC5C,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACtC,CAAC,EAAE,SAAS,MAAM,EAAE,EACpB,OAAO,GAAE,mBAAwB,GAChC,eAAe,CA2DjB"}
1
+ {"version":3,"file":"ols.d.ts","sourceRoot":"","sources":["../../src/core/ols.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAClD,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,uDAAuD;IACvD,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,4CAA4C;AAC5C,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,EACtC,CAAC,EAAE,SAAS,MAAM,EAAE,EACpB,OAAO,GAAE,mBAAwB,GAChC,eAAe,CAyDjB"}
@@ -20,8 +20,8 @@
20
20
  * regenerated** — the draw used `set.seed(42)` under R's own generator, so a
21
21
  * JavaScript regeneration would produce different numbers and silently change
22
22
  * every default demo. Source of the printed values:
23
- * `.claude/plans/moderation-data.tsv`, checked against the column checksums of
24
- * `.claude/plans/moderation-fixtures.md` in `moderationData.test.ts`.
23
+ * `.claude/plans/001-PLAN-port/moderation-data.tsv`, checked against the column checksums of
24
+ * `.claude/plans/001-PLAN-port/moderation-fixtures.md` in `moderationData.test.ts`.
25
25
  *
26
26
  * The columns are in the order of the R data frame — `y, x, z, w`, not
27
27
  * alphabetical and not `x, y, z`. That order is load-bearing: `plot_scatter3d()`
@@ -10,7 +10,7 @@
10
10
  * (`../compstatslib/data/pca_degenerate.rda`), printed at 17 significant
11
11
  * digits, which round-trips an IEEE-754 double. **Exported from R, never
12
12
  * regenerated** — a JavaScript regeneration would silently change the demo.
13
- * Source of the printed values: `.claude/plans/pca-fixtures.md`, section F1.
13
+ * Source of the printed values: `.claude/plans/001-PLAN-port/pca-fixtures.md`, section F1.
14
14
  */
15
15
  import type { Point } from "../core/regression";
16
16
  /** The 16 rows of the R `pca_degenerate` data frame, in file order. */