@hviana/sema 0.1.7 → 0.1.9
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.
- package/example/train_base.ts +29 -4
- package/package.json +1 -1
- package/src/alu/src/parser.ts +105 -4
- package/src/canon.ts +65 -0
- package/src/geometry.ts +26 -30
- package/src/index.ts +1 -0
- package/src/mind/learning.ts +39 -1
- package/src/mind/match.ts +29 -6
- package/src/mind/mechanisms/cast.ts +33 -13
- package/src/mind/mechanisms/confluence.ts +15 -1
- package/src/mind/mechanisms/recall.ts +116 -41
- package/src/mind/mind.ts +172 -29
- package/src/mind/primitives.ts +66 -1
- package/src/mind/recognition.ts +10 -0
- package/src/mind/types.ts +10 -0
- package/src/store-sqlite.ts +68 -0
- package/src/store.ts +27 -0
- package/test/13-conversation.test.mjs +77 -27
- package/test/35-prefix-edge.test.mjs +86 -0
- package/dist/example/demo.d.ts +0 -1
- package/dist/example/demo.js +0 -39
- package/dist/example/train_base.d.ts +0 -87
- package/dist/example/train_base.js +0 -2232
- package/dist/src/alphabet.d.ts +0 -7
- package/dist/src/alphabet.js +0 -33
- package/dist/src/alu/src/alu.d.ts +0 -185
- package/dist/src/alu/src/alu.js +0 -440
- package/dist/src/alu/src/expr.d.ts +0 -61
- package/dist/src/alu/src/expr.js +0 -318
- package/dist/src/alu/src/index.d.ts +0 -11
- package/dist/src/alu/src/index.js +0 -19
- package/dist/src/alu/src/kernel-arith.d.ts +0 -16
- package/dist/src/alu/src/kernel-arith.js +0 -264
- package/dist/src/alu/src/kernel-bits.d.ts +0 -19
- package/dist/src/alu/src/kernel-bits.js +0 -152
- package/dist/src/alu/src/kernel-logic.d.ts +0 -4
- package/dist/src/alu/src/kernel-logic.js +0 -60
- package/dist/src/alu/src/kernel-nd.d.ts +0 -3
- package/dist/src/alu/src/kernel-nd.js +0 -208
- package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
- package/dist/src/alu/src/kernel-numeric.js +0 -366
- package/dist/src/alu/src/operation.d.ts +0 -168
- package/dist/src/alu/src/operation.js +0 -189
- package/dist/src/alu/src/parser.d.ts +0 -212
- package/dist/src/alu/src/parser.js +0 -469
- package/dist/src/alu/src/resonance.d.ts +0 -55
- package/dist/src/alu/src/resonance.js +0 -126
- package/dist/src/alu/src/text.d.ts +0 -31
- package/dist/src/alu/src/text.js +0 -73
- package/dist/src/alu/src/value.d.ts +0 -109
- package/dist/src/alu/src/value.js +0 -300
- package/dist/src/alu/test/alu.test.d.ts +0 -1
- package/dist/src/alu/test/alu.test.js +0 -764
- package/dist/src/bytes.d.ts +0 -14
- package/dist/src/bytes.js +0 -59
- package/dist/src/config.d.ts +0 -111
- package/dist/src/config.js +0 -91
- package/dist/src/derive/src/deduction.d.ts +0 -125
- package/dist/src/derive/src/deduction.js +0 -155
- package/dist/src/derive/src/index.d.ts +0 -7
- package/dist/src/derive/src/index.js +0 -11
- package/dist/src/derive/src/priority-queue.d.ts +0 -20
- package/dist/src/derive/src/priority-queue.js +0 -73
- package/dist/src/derive/src/rewrite.d.ts +0 -56
- package/dist/src/derive/src/rewrite.js +0 -100
- package/dist/src/derive/src/trie.d.ts +0 -90
- package/dist/src/derive/src/trie.js +0 -217
- package/dist/src/derive/test/derive.test.d.ts +0 -1
- package/dist/src/derive/test/derive.test.js +0 -122
- package/dist/src/extension.d.ts +0 -37
- package/dist/src/extension.js +0 -7
- package/dist/src/geometry.d.ts +0 -148
- package/dist/src/geometry.js +0 -517
- package/dist/src/index.d.ts +0 -15
- package/dist/src/index.js +0 -17
- package/dist/src/ingest-cache.d.ts +0 -41
- package/dist/src/ingest-cache.js +0 -161
- package/dist/src/mind/articulation.d.ts +0 -6
- package/dist/src/mind/articulation.js +0 -99
- package/dist/src/mind/attention.d.ts +0 -72
- package/dist/src/mind/attention.js +0 -898
- package/dist/src/mind/canonical.d.ts +0 -29
- package/dist/src/mind/canonical.js +0 -88
- package/dist/src/mind/graph-search.d.ts +0 -270
- package/dist/src/mind/graph-search.js +0 -847
- package/dist/src/mind/index.d.ts +0 -5
- package/dist/src/mind/index.js +0 -5
- package/dist/src/mind/junction.d.ts +0 -95
- package/dist/src/mind/junction.js +0 -262
- package/dist/src/mind/learning.d.ts +0 -47
- package/dist/src/mind/learning.js +0 -201
- package/dist/src/mind/match.d.ts +0 -111
- package/dist/src/mind/match.js +0 -422
- package/dist/src/mind/mechanisms/alu.d.ts +0 -4
- package/dist/src/mind/mechanisms/alu.js +0 -29
- package/dist/src/mind/mechanisms/cast.d.ts +0 -35
- package/dist/src/mind/mechanisms/cast.js +0 -447
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
- package/dist/src/mind/mechanisms/confluence.js +0 -213
- package/dist/src/mind/mechanisms/cover.d.ts +0 -6
- package/dist/src/mind/mechanisms/cover.js +0 -179
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -67
- package/dist/src/mind/mechanisms/extraction.js +0 -342
- package/dist/src/mind/mechanisms/recall.d.ts +0 -13
- package/dist/src/mind/mechanisms/recall.js +0 -166
- package/dist/src/mind/mind.d.ts +0 -211
- package/dist/src/mind/mind.js +0 -433
- package/dist/src/mind/pipeline-mechanism.d.ts +0 -142
- package/dist/src/mind/pipeline-mechanism.js +0 -213
- package/dist/src/mind/pipeline.d.ts +0 -20
- package/dist/src/mind/pipeline.js +0 -185
- package/dist/src/mind/primitives.d.ts +0 -54
- package/dist/src/mind/primitives.js +0 -184
- package/dist/src/mind/rationale.d.ts +0 -134
- package/dist/src/mind/rationale.js +0 -162
- package/dist/src/mind/reasoning.d.ts +0 -15
- package/dist/src/mind/reasoning.js +0 -162
- package/dist/src/mind/recognition.d.ts +0 -20
- package/dist/src/mind/recognition.js +0 -236
- package/dist/src/mind/resonance.d.ts +0 -23
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +0 -15
- package/dist/src/mind/trace.js +0 -73
- package/dist/src/mind/traverse.d.ts +0 -113
- package/dist/src/mind/traverse.js +0 -488
- package/dist/src/mind/types.d.ts +0 -176
- package/dist/src/mind/types.js +0 -84
- package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
- package/dist/src/rabitq-ivf/src/database.js +0 -201
- package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
- package/dist/src/rabitq-ivf/src/index.js +0 -4
- package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
- package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
- package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
- package/dist/src/rabitq-ivf/src/prng.js +0 -36
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-ivf/src/rabitq.js +0 -283
- package/dist/src/sema.d.ts +0 -31
- package/dist/src/sema.js +0 -63
- package/dist/src/store-sqlite.d.ts +0 -176
- package/dist/src/store-sqlite.js +0 -890
- package/dist/src/store.d.ts +0 -642
- package/dist/src/store.js +0 -1612
- package/dist/src/vec.d.ts +0 -31
- package/dist/src/vec.js +0 -109
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
// kernel-nd.ts — the n-dimensional (list-processing) layer.
|
|
2
|
-
//
|
|
3
|
-
// The scalar kernels compute on single quantities; this layer computes on the
|
|
4
|
-
// recursive `nd` container (see value.ts) — an ordered, ragged, heterogeneous
|
|
5
|
-
// list whose elements are themselves Values of any domain, nested arbitrarily.
|
|
6
|
-
//
|
|
7
|
-
// It stands on a tiny irreducible CORE of three STRUCTURAL primitives, exactly
|
|
8
|
-
// as logic stands on `nand` and the limit layer on `converge`:
|
|
9
|
-
//
|
|
10
|
-
// • nd — PACK its operands into a list value. (construct)
|
|
11
|
-
// • length — the top-level element count, as an int. (measure)
|
|
12
|
-
// • at — project the i-th element (negative = from end). (project)
|
|
13
|
-
//
|
|
14
|
-
// These three are the ONLY ops that read an nd's `items` directly. Everything
|
|
15
|
-
// else is a derivation that composes them with the scalar kernels:
|
|
16
|
-
//
|
|
17
|
-
// map(xs, f) = nd( f(at xs 0), f(at xs 1), … ) over length xs
|
|
18
|
-
// filter(xs, p) = nd( the at xs i for which p(at xs i) )
|
|
19
|
-
// reduce(xs, f[,z]) = f(… f(f(z, at xs 0), at xs 1) …)
|
|
20
|
-
// find(xs, p) = the first at xs i with p(at xs i), else the empty nd
|
|
21
|
-
// …concat / reverse / flatten / zip / range / shape / rank likewise.
|
|
22
|
-
//
|
|
23
|
-
// THE FUNCTION ARGUMENT IS ANY EXISTING OPERATION. A higher-order op does not
|
|
24
|
-
// take a bespoke callback enum; it takes an operation-denoting VALUE and resolves
|
|
25
|
-
// it through {@link OpContext.resolveOp} — the SAME machinery every operator goes
|
|
26
|
-
// through (a literal surface form, else its resonant meaning). So `reduce(xs, +)`
|
|
27
|
-
// is a sum, `reduce(xs, *)` a product, `reduce(xs, max)` the maximum,
|
|
28
|
-
// `filter(xs, >0?)`… every scalar op the kernel already has becomes a fold /
|
|
29
|
-
// map / predicate. That is why this layer adds almost no operations of its own:
|
|
30
|
-
// the scalar kernel IS the vocabulary, lifted over lists. (And because reduce's
|
|
31
|
-
// `f(acc, elem)` re-enters `apply`, folding a list of LISTS broadcasts — a column
|
|
32
|
-
// sum is just `reduce(rows, +)`.)
|
|
33
|
-
//
|
|
34
|
-
// STRUCTURAL means broadcast-exempt: a reduce must see the whole list, not be
|
|
35
|
-
// lifted across the elements it folds (see operation.ts). Every SCALAR op is
|
|
36
|
-
// non-structural and so broadcasts over an nd automatically — the two halves of
|
|
37
|
-
// "all operations support nd" meet exactly here.
|
|
38
|
-
import { asBit, asInt, int, isNd, nd } from "./value.js";
|
|
39
|
-
/** The element list of an nd, or throw — so a structural op called on a non-list
|
|
40
|
-
* declines (the Alu facade maps the throw to "this rule does not fire"). */
|
|
41
|
-
function listOf(v) {
|
|
42
|
-
if (!isNd(v))
|
|
43
|
-
throw new Error("nd op: operand is not an n-dimensional value");
|
|
44
|
-
return v.items;
|
|
45
|
-
}
|
|
46
|
-
/** Resolve an operation-value to a canonical name of the wanted arity, or throw
|
|
47
|
-
* (decline) when it names no known operation — the higher-order ops' shared
|
|
48
|
-
* front door, so an unrecognised function argument never silently no-ops. */
|
|
49
|
-
function resolveFn(ctx, op, arity) {
|
|
50
|
-
const name = ctx.resolveOp(op, arity);
|
|
51
|
-
if (name === null)
|
|
52
|
-
throw new Error("nd op: unrecognised operation argument");
|
|
53
|
-
return name;
|
|
54
|
-
}
|
|
55
|
-
/** Register the n-dimensional layer into `r`. All ops are STRUCTURAL. */
|
|
56
|
-
export function registerNd(r) {
|
|
57
|
-
// ── core primitives (the only ops that touch `items`) ────────────────────
|
|
58
|
-
// nd: PACK operands into a list. Variadic, so `nd(a, b, c)` = [a, b, c] and
|
|
59
|
-
// `nd()` = the empty list. An operand that is itself a list nests (a matrix
|
|
60
|
-
// is `nd(row0, row1, …)`), so rank grows by composition, never a new type.
|
|
61
|
-
r.prim("nd", "variadic", ["nd", "list", "vector", "tuple", "array"], (args) => nd(args.slice()), { structural: true });
|
|
62
|
-
// length: the top-level element count, as an exact int.
|
|
63
|
-
r.prim("length", 1, ["length", "size", "len"], (args) => int(BigInt(listOf(args[0]).length)), { structural: true });
|
|
64
|
-
// at: project the i-th element. A negative index counts from the end
|
|
65
|
-
// (−1 = last), Python-style; out of range throws (the rule declines).
|
|
66
|
-
r.prim("at", 2, ["at", "nth", "elem", "index"], (args) => {
|
|
67
|
-
const xs = listOf(args[0]);
|
|
68
|
-
let i = Number(asInt(args[1]));
|
|
69
|
-
if (i < 0)
|
|
70
|
-
i += xs.length;
|
|
71
|
-
if (i < 0 || i >= xs.length)
|
|
72
|
-
throw new Error("at: index out of range");
|
|
73
|
-
return xs[i];
|
|
74
|
-
}, { structural: true });
|
|
75
|
-
// ── derived: construction ────────────────────────────────────────────────
|
|
76
|
-
// range(n) = [0, 1, …, n−1]; range(a, b) = [a, a+1, …, b−1]. The index
|
|
77
|
-
// generator map/reduce iterate over when there is no input list yet.
|
|
78
|
-
r.derive("range", "variadic", ["range", "iota", "upto"], (args) => {
|
|
79
|
-
const lo = args.length >= 2 ? asInt(args[0]) : 0n;
|
|
80
|
-
const hi = args.length >= 2 ? asInt(args[1]) : asInt(args[0]);
|
|
81
|
-
const out = [];
|
|
82
|
-
for (let k = lo; k < hi; k++)
|
|
83
|
-
out.push(int(k));
|
|
84
|
-
return nd(out);
|
|
85
|
-
}, { structural: true });
|
|
86
|
-
// ── derived: the higher-order trio (op argument = any existing operation) ──
|
|
87
|
-
// map(xs, f): apply the UNARY op f to each element. Built on at/length/nd; f
|
|
88
|
-
// is resolved by meaning, so `map(xs, negate)`, `map(xs, sqrt)`, `map(xs,
|
|
89
|
-
// inverse)` all work, and (via broadcast) map over a list of lists is fine.
|
|
90
|
-
r.derive("map", 2, ["map", "each"], (args, ctx) => {
|
|
91
|
-
const xs = listOf(args[0]);
|
|
92
|
-
const f = resolveFn(ctx, args[1], 1);
|
|
93
|
-
return nd(xs.map((e) => ctx.apply(f, [e])));
|
|
94
|
-
}, { structural: true });
|
|
95
|
-
// filter(xs, p): keep the elements for which the UNARY predicate p is truthy
|
|
96
|
-
// (its result read as a bit). `filter(xs, isprime)`, `filter(xs, >0)`, … —
|
|
97
|
-
// any op that returns a truth value is a predicate.
|
|
98
|
-
r.derive("filter", 2, ["filter", "where", "keep"], (args, ctx) => {
|
|
99
|
-
const xs = listOf(args[0]);
|
|
100
|
-
const p = resolveFn(ctx, args[1], 1);
|
|
101
|
-
return nd(xs.filter((e) => asBit(ctx.apply(p, [e])) === 1));
|
|
102
|
-
}, { structural: true });
|
|
103
|
-
// reduce(xs, f) / reduce(xs, f, z): left-fold the BINARY op f across the list,
|
|
104
|
-
// optionally from a seed z. `reduce(xs, +)` sums, `reduce(xs, *)` multiplies,
|
|
105
|
-
// `reduce(xs, max)` is the maximum, `reduce(xs, or)` is "any", … — the scalar
|
|
106
|
-
// kernel's whole binary vocabulary becomes a fold. With no seed an empty list
|
|
107
|
-
// declines (nothing to fold); with a seed it returns the seed.
|
|
108
|
-
r.derive("reduce", "variadic", ["reduce", "fold", "accumulate"], (args, ctx) => {
|
|
109
|
-
if (args.length < 2) {
|
|
110
|
-
throw new Error("reduce: needs a list and an operation");
|
|
111
|
-
}
|
|
112
|
-
const xs = listOf(args[0]);
|
|
113
|
-
const f = resolveFn(ctx, args[1], 2);
|
|
114
|
-
const hasSeed = args.length >= 3;
|
|
115
|
-
let acc;
|
|
116
|
-
let start;
|
|
117
|
-
if (hasSeed) {
|
|
118
|
-
acc = args[2];
|
|
119
|
-
start = 0;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
if (xs.length === 0)
|
|
123
|
-
throw new Error("reduce: empty list, no seed");
|
|
124
|
-
acc = xs[0];
|
|
125
|
-
start = 1;
|
|
126
|
-
}
|
|
127
|
-
for (let i = start; i < xs.length; i++)
|
|
128
|
-
acc = ctx.apply(f, [acc, xs[i]]);
|
|
129
|
-
return acc;
|
|
130
|
-
}, { structural: true });
|
|
131
|
-
// find(xs, p): the FIRST element satisfying the unary predicate p, or the
|
|
132
|
-
// empty nd when none does (a graph-evidenced "nothing matched", never a guess).
|
|
133
|
-
r.derive("find", 2, ["find", "first", "search"], (args, ctx) => {
|
|
134
|
-
const xs = listOf(args[0]);
|
|
135
|
-
const p = resolveFn(ctx, args[1], 1);
|
|
136
|
-
for (const e of xs)
|
|
137
|
-
if (asBit(ctx.apply(p, [e])) === 1)
|
|
138
|
-
return e;
|
|
139
|
-
return nd([]);
|
|
140
|
-
}, { structural: true });
|
|
141
|
-
// ── derived: shape-preserving plumbing ────────────────────────────────────
|
|
142
|
-
// concat(a, b, …): join lists end to end into one list.
|
|
143
|
-
r.derive("concat", "variadic", ["concat", "append", "join"], (args) => {
|
|
144
|
-
const out = [];
|
|
145
|
-
for (const a of args)
|
|
146
|
-
for (const e of listOf(a))
|
|
147
|
-
out.push(e);
|
|
148
|
-
return nd(out);
|
|
149
|
-
}, { structural: true });
|
|
150
|
-
// reverse(xs): the list back to front.
|
|
151
|
-
r.derive("reverse", 1, ["reverse", "flip"], (args) => nd(listOf(args[0]).slice().reverse()), { structural: true });
|
|
152
|
-
// flatten(xs): splice one level — each nd element is spliced in, each scalar
|
|
153
|
-
// kept. flatten ∘ flatten goes deeper; one level is the primitive step.
|
|
154
|
-
r.derive("flatten", 1, ["flatten", "flat"], (args) => {
|
|
155
|
-
const out = [];
|
|
156
|
-
for (const e of listOf(args[0])) {
|
|
157
|
-
if (isNd(e)) {
|
|
158
|
-
for (const sub of e.items)
|
|
159
|
-
out.push(sub);
|
|
160
|
-
}
|
|
161
|
-
else
|
|
162
|
-
out.push(e);
|
|
163
|
-
}
|
|
164
|
-
return nd(out);
|
|
165
|
-
}, { structural: true });
|
|
166
|
-
// zip(a, b, …): the lists braided into a list of tuples; length = the shortest
|
|
167
|
-
// input (a ragged braid stops at the short one, never inventing elements).
|
|
168
|
-
r.derive("zip", "variadic", ["zip", "braid"], (args) => {
|
|
169
|
-
const lists = args.map(listOf);
|
|
170
|
-
if (lists.length === 0)
|
|
171
|
-
return nd([]);
|
|
172
|
-
const m = Math.min(...lists.map((l) => l.length));
|
|
173
|
-
const out = [];
|
|
174
|
-
for (let i = 0; i < m; i++)
|
|
175
|
-
out.push(nd(lists.map((l) => l[i])));
|
|
176
|
-
return nd(out);
|
|
177
|
-
}, { structural: true });
|
|
178
|
-
// rank(v): the nesting DEPTH — 0 for a scalar, 1 + the deepest element's rank
|
|
179
|
-
// for a list (so a vector is 1, a matrix 2, ragged depth taken as the max).
|
|
180
|
-
r.derive("rank", 1, ["rank", "depth", "ndim"], (args) => {
|
|
181
|
-
const rank = (v) => {
|
|
182
|
-
if (!isNd(v))
|
|
183
|
-
return 0n;
|
|
184
|
-
let m = 0n;
|
|
185
|
-
for (const e of v.items) {
|
|
186
|
-
const rr = rank(e);
|
|
187
|
-
if (rr > m)
|
|
188
|
-
m = rr;
|
|
189
|
-
}
|
|
190
|
-
return m + 1n;
|
|
191
|
-
};
|
|
192
|
-
return int(rank(args[0]));
|
|
193
|
-
}, { structural: true });
|
|
194
|
-
// shape(v): the size along each axis, following the FIRST element down — the
|
|
195
|
-
// regular-tensor shape (a ragged list is reported by its top length, the
|
|
196
|
-
// recursion only descending the head). Returns a list of ints.
|
|
197
|
-
r.derive("shape", 1, ["shape", "dims"], (args) => {
|
|
198
|
-
const dims = [];
|
|
199
|
-
let cur = args[0];
|
|
200
|
-
while (isNd(cur)) {
|
|
201
|
-
dims.push(int(BigInt(cur.items.length)));
|
|
202
|
-
if (cur.items.length === 0)
|
|
203
|
-
break;
|
|
204
|
-
cur = cur.items[0];
|
|
205
|
-
}
|
|
206
|
-
return nd(dims);
|
|
207
|
-
}, { structural: true });
|
|
208
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { OperationRegistry } from "./operation.js";
|
|
2
|
-
/** The convergence primitive: iterate `step` from `x0` until successive iterates
|
|
3
|
-
* agree within `tol` (absolute, or relative for large magnitudes), or until
|
|
4
|
-
* `maxIter` is reached. Returns the last iterate — the refinement's fixed
|
|
5
|
-
* point. This is the single irreducible operator of the numerical layer. */
|
|
6
|
-
export declare function converge(step: (x: number) => number, x0: number, tol: number, maxIter: number): number;
|
|
7
|
-
/** Numerical derivative f′(a) by a central difference refined as h → 0 — a
|
|
8
|
-
* `converge` instance over shrinking step sizes (Richardson-style halving). */
|
|
9
|
-
export declare function diff(f: (x: number) => number, a: number, tol: number, maxIter: number): number;
|
|
10
|
-
/** A root of f near x0, by Newton's method: converge x − f(x)/f′(x), with f′
|
|
11
|
-
* from {@link diff}. Falls back to a secant step if the derivative vanishes. */
|
|
12
|
-
export declare function solve(f: (x: number) => number, x0: number, tol: number, maxIter: number): number;
|
|
13
|
-
/** Definite integral of f over [a, b], by composite Simpson refined until the
|
|
14
|
-
* estimate stops moving — a `converge` over partition count. */
|
|
15
|
-
export declare function integrate(f: (x: number) => number, a: number, b: number, tol: number, maxIter: number): number;
|
|
16
|
-
/** e^x by its Taylor series, summed until a term is below tolerance — a
|
|
17
|
-
* `converge` over partial sums. (Range-reduction-free; adequate for the
|
|
18
|
-
* arguments a query produces, and exact in the limit.) */
|
|
19
|
-
export declare function expSeries(x: number, tol: number, maxIter: number): number;
|
|
20
|
-
/** ln(x), x > 0, as the root of e^t − x = 0 (Newton via {@link solve}, with a
|
|
21
|
-
* good initial guess from the host log to keep iteration counts low). */
|
|
22
|
-
export declare function logNewton(x: number, tol: number, maxIter: number): number;
|
|
23
|
-
/** sin / cos by their Taylor series (a `converge` over partial sums), with
|
|
24
|
-
* argument reduction into [−π, π] for accuracy. */
|
|
25
|
-
export declare function sinSeries(x: number, tol: number, maxIter: number): number;
|
|
26
|
-
export declare function cosSeries(x: number, tol: number, maxIter: number): number;
|
|
27
|
-
/** √x as the positive root of t² − x = 0 (Newton via {@link solve}). */
|
|
28
|
-
export declare function sqrtNewton(x: number, tol: number, maxIter: number): number;
|
|
29
|
-
/** A local minimum of f near x0: a root of f′ = 0 (optimize = solve(diff f)). */
|
|
30
|
-
export declare function optimize(f: (x: number) => number, x0: number, tol: number, maxIter: number): number;
|
|
31
|
-
/** Integrate an ODE y′ = f(t, y) from (t0, y0) to t1 by classical RK4 stepping
|
|
32
|
-
* — explicit stepping is the layer's other use of refinement (here a fixed
|
|
33
|
-
* fine step; an implicit step would close with {@link solve}). */
|
|
34
|
-
export declare function odeSolve(f: (t: number, y: number) => number, t0: number, y0: number, t1: number, steps?: number): number;
|
|
35
|
-
/** Least-squares polynomial regression of degree `deg` over (x, y) samples:
|
|
36
|
-
* build the normal equations and solve them with {@link linsolve} — regression
|
|
37
|
-
* reduces to structured arithmetic. Returns coefficients constant-first. */
|
|
38
|
-
export declare function regress(xs: number[], ys: number[], deg: number): number[] | null;
|
|
39
|
-
/** Linear interpolation of y at `xq` over sorted sample points (xs, ys). */
|
|
40
|
-
export declare function interpolate(xs: number[], ys: number[], xq: number): number;
|
|
41
|
-
/** Dominant eigenpair of a (preferably symmetric) matrix by POWER ITERATION —
|
|
42
|
-
* the canonical "iteration = converge" of this layer. Returns the eigenvalue
|
|
43
|
-
* (Rayleigh quotient) and its unit eigenvector. */
|
|
44
|
-
export declare function powerEig(A: number[][], tol: number, maxIter: number): {
|
|
45
|
-
value: number;
|
|
46
|
-
vector: number[];
|
|
47
|
-
};
|
|
48
|
-
/** Largest singular value of A: √(dominant eigenvalue of AᵀA) — power iteration
|
|
49
|
-
* again, the prototypical `converge`. */
|
|
50
|
-
export declare function topSingular(A: number[][], tol: number, maxIter: number): number;
|
|
51
|
-
/** Register the scalar transcendentals as ops (they have surface forms). The
|
|
52
|
-
* matrix routines stay exported functions — there is no infix surface for a
|
|
53
|
-
* matrix, and auto-firing them from query spans is neither wanted nor safe. */
|
|
54
|
-
export declare function registerNumeric(r: OperationRegistry): void;
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
// kernel-numeric.ts — the limit layer.
|
|
2
|
-
//
|
|
3
|
-
// A classical ALU stops at logic + arithmetic: exact, finite computation. The
|
|
4
|
-
// ONE thing that makes this a numerical engine is a single new primitive —
|
|
5
|
-
// `converge(step, tol)`: iterate a refinement until successive results agree
|
|
6
|
-
// within ε. Everything else here is a `converge` instance or a derivation over
|
|
7
|
-
// it plus the arithmetic already built:
|
|
8
|
-
//
|
|
9
|
-
// diff = converge a shrinking central difference (Richardson-style)
|
|
10
|
-
// solve = Newton: converge on x − f(x)/f′(x) (f′ via diff)
|
|
11
|
-
// integrate = converge an adaptive partition (Simpson, refine until stable)
|
|
12
|
-
// sqrt = solve t² − x = 0
|
|
13
|
-
// exp/log/sin/cos = converge their series / a Newton inverse
|
|
14
|
-
// optimize = solve(diff f = 0)
|
|
15
|
-
// ode_solve = step + (implicit) solve
|
|
16
|
-
// regress = linsolve on the normal equations
|
|
17
|
-
// eig/svd = power iteration = converge
|
|
18
|
-
//
|
|
19
|
-
// Direct methods (Gaussian elimination in kernel-arith.linsolve) are NOT new
|
|
20
|
-
// primitives — they are arithmetic plus the control flow layer 1 gave us. Only
|
|
21
|
-
// convergence-to-tolerance is irreducible here.
|
|
22
|
-
//
|
|
23
|
-
// Scalar transcendentals are registered as ops (they have surface forms a query
|
|
24
|
-
// can name); the matrix routines (regress, eig, svd, interpolate) are exported
|
|
25
|
-
// pure functions, composed from the same primitives and tested directly, never
|
|
26
|
-
// auto-fired from an arbitrary query span.
|
|
27
|
-
import { asReal, real } from "./value.js";
|
|
28
|
-
import { linsolve } from "./kernel-arith.js";
|
|
29
|
-
/** The convergence primitive: iterate `step` from `x0` until successive iterates
|
|
30
|
-
* agree within `tol` (absolute, or relative for large magnitudes), or until
|
|
31
|
-
* `maxIter` is reached. Returns the last iterate — the refinement's fixed
|
|
32
|
-
* point. This is the single irreducible operator of the numerical layer. */
|
|
33
|
-
export function converge(step, x0, tol, maxIter) {
|
|
34
|
-
let x = x0;
|
|
35
|
-
for (let i = 0; i < maxIter; i++) {
|
|
36
|
-
const next = step(x);
|
|
37
|
-
const scale = Math.max(1, Math.abs(next), Math.abs(x));
|
|
38
|
-
if (Math.abs(next - x) <= tol * scale)
|
|
39
|
-
return next;
|
|
40
|
-
x = next;
|
|
41
|
-
}
|
|
42
|
-
return x;
|
|
43
|
-
}
|
|
44
|
-
/** Numerical derivative f′(a) by a central difference refined as h → 0 — a
|
|
45
|
-
* `converge` instance over shrinking step sizes (Richardson-style halving). */
|
|
46
|
-
export function diff(f, a, tol, maxIter) {
|
|
47
|
-
let h = Math.max(1e-2, Math.abs(a) * 1e-2) || 1e-2;
|
|
48
|
-
let prev = (f(a + h) - f(a - h)) / (2 * h);
|
|
49
|
-
for (let i = 0; i < maxIter; i++) {
|
|
50
|
-
h /= 2;
|
|
51
|
-
const cur = (f(a + h) - f(a - h)) / (2 * h);
|
|
52
|
-
const scale = Math.max(1, Math.abs(cur), Math.abs(prev));
|
|
53
|
-
if (Math.abs(cur - prev) <= tol * scale)
|
|
54
|
-
return cur;
|
|
55
|
-
prev = cur;
|
|
56
|
-
if (h < 1e-13)
|
|
57
|
-
break; // floor: finer steps amplify round-off
|
|
58
|
-
}
|
|
59
|
-
return prev;
|
|
60
|
-
}
|
|
61
|
-
/** A root of f near x0, by Newton's method: converge x − f(x)/f′(x), with f′
|
|
62
|
-
* from {@link diff}. Falls back to a secant step if the derivative vanishes. */
|
|
63
|
-
export function solve(f, x0, tol, maxIter) {
|
|
64
|
-
return converge((x) => {
|
|
65
|
-
const fx = f(x);
|
|
66
|
-
let d = diff(f, x, tol, maxIter);
|
|
67
|
-
if (Math.abs(d) < 1e-14) {
|
|
68
|
-
// Derivative ~0: nudge with a secant over a small offset.
|
|
69
|
-
const h = 1e-6 * (Math.abs(x) + 1);
|
|
70
|
-
d = (f(x + h) - fx) / h;
|
|
71
|
-
if (Math.abs(d) < 1e-14)
|
|
72
|
-
return x;
|
|
73
|
-
}
|
|
74
|
-
return x - fx / d;
|
|
75
|
-
}, x0, tol, maxIter);
|
|
76
|
-
}
|
|
77
|
-
/** Definite integral of f over [a, b], by composite Simpson refined until the
|
|
78
|
-
* estimate stops moving — a `converge` over partition count. */
|
|
79
|
-
export function integrate(f, a, b, tol, maxIter) {
|
|
80
|
-
const simpson = (n) => {
|
|
81
|
-
const h = (b - a) / n;
|
|
82
|
-
let s = f(a) + f(b);
|
|
83
|
-
for (let i = 1; i < n; i++)
|
|
84
|
-
s += (i % 2 === 0 ? 2 : 4) * f(a + i * h);
|
|
85
|
-
return (s * h) / 3;
|
|
86
|
-
};
|
|
87
|
-
let n = 2;
|
|
88
|
-
let prev = simpson(n);
|
|
89
|
-
for (let i = 0; i < maxIter; i++) {
|
|
90
|
-
n *= 2;
|
|
91
|
-
const cur = simpson(n);
|
|
92
|
-
const scale = Math.max(1, Math.abs(cur), Math.abs(prev));
|
|
93
|
-
if (Math.abs(cur - prev) <= tol * scale)
|
|
94
|
-
return cur;
|
|
95
|
-
prev = cur;
|
|
96
|
-
}
|
|
97
|
-
return prev;
|
|
98
|
-
}
|
|
99
|
-
/** e^x by its Taylor series, summed until a term is below tolerance — a
|
|
100
|
-
* `converge` over partial sums. (Range-reduction-free; adequate for the
|
|
101
|
-
* arguments a query produces, and exact in the limit.) */
|
|
102
|
-
export function expSeries(x, tol, maxIter) {
|
|
103
|
-
let term = 1;
|
|
104
|
-
let sum = 1;
|
|
105
|
-
for (let n = 1; n < maxIter; n++) {
|
|
106
|
-
term *= x / n;
|
|
107
|
-
sum += term;
|
|
108
|
-
if (Math.abs(term) <= tol * Math.max(1, Math.abs(sum)))
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
return sum;
|
|
112
|
-
}
|
|
113
|
-
/** ln(x), x > 0, as the root of e^t − x = 0 (Newton via {@link solve}, with a
|
|
114
|
-
* good initial guess from the host log to keep iteration counts low). */
|
|
115
|
-
export function logNewton(x, tol, maxIter) {
|
|
116
|
-
if (x <= 0)
|
|
117
|
-
return NaN;
|
|
118
|
-
return solve((t) => expSeries(t, tol, maxIter) - x, Math.log(x), tol, maxIter);
|
|
119
|
-
}
|
|
120
|
-
/** sin / cos by their Taylor series (a `converge` over partial sums), with
|
|
121
|
-
* argument reduction into [−π, π] for accuracy. */
|
|
122
|
-
export function sinSeries(x, tol, maxIter) {
|
|
123
|
-
const TWO_PI = 2 * Math.PI;
|
|
124
|
-
let a = x % TWO_PI;
|
|
125
|
-
if (a > Math.PI)
|
|
126
|
-
a -= TWO_PI;
|
|
127
|
-
if (a < -Math.PI)
|
|
128
|
-
a += TWO_PI;
|
|
129
|
-
let term = a;
|
|
130
|
-
let sum = a;
|
|
131
|
-
for (let n = 1; n < maxIter; n++) {
|
|
132
|
-
term *= (-a * a) / ((2 * n) * (2 * n + 1));
|
|
133
|
-
sum += term;
|
|
134
|
-
if (Math.abs(term) <= tol)
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
return sum;
|
|
138
|
-
}
|
|
139
|
-
export function cosSeries(x, tol, maxIter) {
|
|
140
|
-
const TWO_PI = 2 * Math.PI;
|
|
141
|
-
let a = x % TWO_PI;
|
|
142
|
-
if (a > Math.PI)
|
|
143
|
-
a -= TWO_PI;
|
|
144
|
-
if (a < -Math.PI)
|
|
145
|
-
a += TWO_PI;
|
|
146
|
-
let term = 1;
|
|
147
|
-
let sum = 1;
|
|
148
|
-
for (let n = 1; n < maxIter; n++) {
|
|
149
|
-
term *= (-a * a) / ((2 * n - 1) * (2 * n));
|
|
150
|
-
sum += term;
|
|
151
|
-
if (Math.abs(term) <= tol)
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
return sum;
|
|
155
|
-
}
|
|
156
|
-
/** √x as the positive root of t² − x = 0 (Newton via {@link solve}). */
|
|
157
|
-
export function sqrtNewton(x, tol, maxIter) {
|
|
158
|
-
if (x < 0)
|
|
159
|
-
return NaN;
|
|
160
|
-
if (x === 0)
|
|
161
|
-
return 0;
|
|
162
|
-
return solve((t) => t * t - x, Math.max(x, 1), tol, maxIter);
|
|
163
|
-
}
|
|
164
|
-
/** A local minimum of f near x0: a root of f′ = 0 (optimize = solve(diff f)). */
|
|
165
|
-
export function optimize(f, x0, tol, maxIter) {
|
|
166
|
-
return solve((x) => diff(f, x, tol, maxIter), x0, tol, maxIter);
|
|
167
|
-
}
|
|
168
|
-
/** Integrate an ODE y′ = f(t, y) from (t0, y0) to t1 by classical RK4 stepping
|
|
169
|
-
* — explicit stepping is the layer's other use of refinement (here a fixed
|
|
170
|
-
* fine step; an implicit step would close with {@link solve}). */
|
|
171
|
-
export function odeSolve(f, t0, y0, t1, steps = 1000) {
|
|
172
|
-
const h = (t1 - t0) / steps;
|
|
173
|
-
let t = t0;
|
|
174
|
-
let y = y0;
|
|
175
|
-
for (let i = 0; i < steps; i++) {
|
|
176
|
-
const k1 = f(t, y);
|
|
177
|
-
const k2 = f(t + h / 2, y + (h / 2) * k1);
|
|
178
|
-
const k3 = f(t + h / 2, y + (h / 2) * k2);
|
|
179
|
-
const k4 = f(t + h, y + h * k3);
|
|
180
|
-
y += (h / 6) * (k1 + 2 * k2 + 2 * k3 + k4);
|
|
181
|
-
t += h;
|
|
182
|
-
}
|
|
183
|
-
return y;
|
|
184
|
-
}
|
|
185
|
-
/** Least-squares polynomial regression of degree `deg` over (x, y) samples:
|
|
186
|
-
* build the normal equations and solve them with {@link linsolve} — regression
|
|
187
|
-
* reduces to structured arithmetic. Returns coefficients constant-first. */
|
|
188
|
-
export function regress(xs, ys, deg) {
|
|
189
|
-
const m = deg + 1;
|
|
190
|
-
// Vandermonde-style normal matrix AᵀA and vector Aᵀy.
|
|
191
|
-
const ata = Array.from({ length: m }, () => new Array(m).fill(0));
|
|
192
|
-
const aty = new Array(m).fill(0);
|
|
193
|
-
for (let s = 0; s < xs.length; s++) {
|
|
194
|
-
const pows = new Array(2 * deg + 1);
|
|
195
|
-
pows[0] = 1;
|
|
196
|
-
for (let p = 1; p <= 2 * deg; p++)
|
|
197
|
-
pows[p] = pows[p - 1] * xs[s];
|
|
198
|
-
for (let i = 0; i < m; i++) {
|
|
199
|
-
for (let j = 0; j < m; j++)
|
|
200
|
-
ata[i][j] += pows[i + j];
|
|
201
|
-
aty[i] += pows[i] * ys[s];
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return linsolve(ata, aty);
|
|
205
|
-
}
|
|
206
|
-
/** Linear interpolation of y at `xq` over sorted sample points (xs, ys). */
|
|
207
|
-
export function interpolate(xs, ys, xq) {
|
|
208
|
-
if (xq <= xs[0])
|
|
209
|
-
return ys[0];
|
|
210
|
-
const n = xs.length;
|
|
211
|
-
if (xq >= xs[n - 1])
|
|
212
|
-
return ys[n - 1];
|
|
213
|
-
for (let i = 0; i < n - 1; i++) {
|
|
214
|
-
if (xq >= xs[i] && xq <= xs[i + 1]) {
|
|
215
|
-
const t = (xq - xs[i]) / (xs[i + 1] - xs[i]);
|
|
216
|
-
return ys[i] + t * (ys[i + 1] - ys[i]);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return ys[n - 1];
|
|
220
|
-
}
|
|
221
|
-
/** Dominant eigenpair of a (preferably symmetric) matrix by POWER ITERATION —
|
|
222
|
-
* the canonical "iteration = converge" of this layer. Returns the eigenvalue
|
|
223
|
-
* (Rayleigh quotient) and its unit eigenvector. */
|
|
224
|
-
export function powerEig(A, tol, maxIter) {
|
|
225
|
-
const n = A.length;
|
|
226
|
-
// Start from a normalised all-ones vector: deterministic (no PRNG), and
|
|
227
|
-
// generically NOT aligned with any single eigenvector of a diagonal/symmetric
|
|
228
|
-
// matrix, so iteration is not trapped on a non-dominant axis (a unit basis
|
|
229
|
-
// vector would be, e.g. e₀ is fixed under diag(9,16) and converges to the
|
|
230
|
-
// wrong eigenvalue).
|
|
231
|
-
let v = new Array(n).fill(1 / Math.sqrt(n));
|
|
232
|
-
let lambda = 0;
|
|
233
|
-
for (let it = 0; it < maxIter; it++) {
|
|
234
|
-
// w = A v
|
|
235
|
-
const w = A.map((row) => row.reduce((s, a, j) => s + a * v[j], 0));
|
|
236
|
-
const norm = Math.sqrt(w.reduce((s, x) => s + x * x, 0)) || 1;
|
|
237
|
-
const vn = w.map((x) => x / norm);
|
|
238
|
-
// Rayleigh quotient vᵀAv.
|
|
239
|
-
const Av = A.map((row) => row.reduce((s, a, j) => s + a * vn[j], 0));
|
|
240
|
-
const lam = vn.reduce((s, x, i) => s + x * Av[i], 0);
|
|
241
|
-
if (Math.abs(lam - lambda) <= tol * Math.max(1, Math.abs(lam))) {
|
|
242
|
-
return { value: lam, vector: vn };
|
|
243
|
-
}
|
|
244
|
-
lambda = lam;
|
|
245
|
-
v = vn;
|
|
246
|
-
}
|
|
247
|
-
return { value: lambda, vector: v };
|
|
248
|
-
}
|
|
249
|
-
/** Largest singular value of A: √(dominant eigenvalue of AᵀA) — power iteration
|
|
250
|
-
* again, the prototypical `converge`. */
|
|
251
|
-
export function topSingular(A, tol, maxIter) {
|
|
252
|
-
const n = A[0].length;
|
|
253
|
-
// AᵀA (n×n).
|
|
254
|
-
const ata = Array.from({ length: n }, () => new Array(n).fill(0));
|
|
255
|
-
for (let i = 0; i < n; i++) {
|
|
256
|
-
for (let j = 0; j < n; j++) {
|
|
257
|
-
let s = 0;
|
|
258
|
-
for (let r = 0; r < A.length; r++)
|
|
259
|
-
s += A[r][i] * A[r][j];
|
|
260
|
-
ata[i][j] = s;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const { value } = powerEig(ata, tol, maxIter);
|
|
264
|
-
return Math.sqrt(Math.max(0, value));
|
|
265
|
-
}
|
|
266
|
-
/** Register the scalar transcendentals as ops (they have surface forms). The
|
|
267
|
-
* matrix routines stay exported functions — there is no infix surface for a
|
|
268
|
-
* matrix, and auto-firing them from query spans is neither wanted nor safe. */
|
|
269
|
-
export function registerNumeric(r) {
|
|
270
|
-
const one = (name, forms, f) => {
|
|
271
|
-
r.derive(name, 1, forms, (args, ctx) => real(f(asReal(args[0]), ctx.rt.tol, ctx.rt.maxIter)));
|
|
272
|
-
};
|
|
273
|
-
one("exp", ["exp", "e^"], expSeries);
|
|
274
|
-
// ── named constants ───────────────────────────────────────────────────────
|
|
275
|
-
// Nullary ops, so a constant is RESOLVED like any operation (registry forms),
|
|
276
|
-
// never special-cased by a parser: the expression grammar reads "an arity-0
|
|
277
|
-
// op" as "a constant" generically.
|
|
278
|
-
r.derive("pi", 0, ["pi", "π"], () => real(Math.PI));
|
|
279
|
-
r.derive("euler", 0, ["e"], () => real(Math.E));
|
|
280
|
-
one("log", ["log", "ln"], logNewton);
|
|
281
|
-
one("sin", ["sin"], sinSeries);
|
|
282
|
-
one("cos", ["cos"], cosSeries);
|
|
283
|
-
one("sqrt", ["sqrt", "√"], sqrtNewton);
|
|
284
|
-
// ── operations over an EXPRESSION (the recursive-call case) ───────────────
|
|
285
|
-
// A derivative, an integral, a limit, an optimisation — these act on a
|
|
286
|
-
// FUNCTION, given as a symbol (the expression's bytes) plus point/bounds. The
|
|
287
|
-
// function is sampled by ctx.evalExpr, which evaluates the expression by a
|
|
288
|
-
// RECURSIVE application of the same ALU (see expr.ts). Their surface `forms`
|
|
289
|
-
// are CONCEPT forms — words a query might literally carry ("derivative",
|
|
290
|
-
// "integral", "limit") — but the GENERIC, modality-agnostic recognition is by
|
|
291
|
-
// resonance over these same concepts (pipeline.ts's recogniseOp), so an operation
|
|
292
|
-
// the bytes do not spell at all is still recognised by meaning. These ops
|
|
293
|
-
// decline (return their first operand unchanged is wrong; instead they throw,
|
|
294
|
-
// which the Alu facade maps to "rule does not fire") when no evaluator is
|
|
295
|
-
// wired or the expression is unparseable — synthesis stays evidence-based.
|
|
296
|
-
const exprOf = (v) => {
|
|
297
|
-
if (v.domain === "symbol")
|
|
298
|
-
return v.bytes;
|
|
299
|
-
// A bare number is a constant expression — encode it back to bytes so the
|
|
300
|
-
// evaluator sees a literal.
|
|
301
|
-
throw new Error("expression operand expected");
|
|
302
|
-
};
|
|
303
|
-
const evalAt = (ctx, expr, vbl, at) => {
|
|
304
|
-
if (!ctx.evalExpr)
|
|
305
|
-
throw new Error("no expression evaluator");
|
|
306
|
-
const y = ctx.evalExpr(expr, vbl, at);
|
|
307
|
-
if (y === null || !Number.isFinite(y))
|
|
308
|
-
throw new Error("expression failed");
|
|
309
|
-
return y;
|
|
310
|
-
};
|
|
311
|
-
// diff: derivative of f at a point. args = [expr, at].
|
|
312
|
-
r.derive("diff", 2, ["derivative", "differentiate", "d/dx", "diff"], (args, ctx) => {
|
|
313
|
-
const expr = exprOf(args[0]);
|
|
314
|
-
const at = asReal(args[1]);
|
|
315
|
-
return real(diff((x) => evalAt(ctx, expr, "", x), at, ctx.rt.tol, ctx.rt.maxIter));
|
|
316
|
-
}, { expression: true });
|
|
317
|
-
// integrate: definite integral of f over [a, b]. args = [expr, a, b].
|
|
318
|
-
r.derive("integrate", 3, ["integral", "integrate", "∫"], (args, ctx) => {
|
|
319
|
-
const expr = exprOf(args[0]);
|
|
320
|
-
const a = asReal(args[1]);
|
|
321
|
-
const b = asReal(args[2]);
|
|
322
|
-
return real(integrate((x) => evalAt(ctx, expr, "", x), a, b, ctx.rt.tol, ctx.rt.maxIter));
|
|
323
|
-
}, { expression: true });
|
|
324
|
-
// solve: a root of f near a guess. args = [expr, guess].
|
|
325
|
-
r.derive("solve", 2, ["solve", "root", "zero"], (args, ctx) => {
|
|
326
|
-
const expr = exprOf(args[0]);
|
|
327
|
-
const guess = asReal(args[1]);
|
|
328
|
-
return real(solve((x) => evalAt(ctx, expr, "", x), guess, ctx.rt.tol, ctx.rt.maxIter));
|
|
329
|
-
}, { expression: true });
|
|
330
|
-
// limit: the value f approaches as its variable → a, estimated by sampling
|
|
331
|
-
// ever closer from both sides and converging (a limit IS a converge instance).
|
|
332
|
-
r.derive("limit", 2, ["limit", "lim"], (args, ctx) => {
|
|
333
|
-
const expr = exprOf(args[0]);
|
|
334
|
-
const a = asReal(args[1]);
|
|
335
|
-
const sample = (h) => {
|
|
336
|
-
// average of the two one-sided samples, skirting the point itself
|
|
337
|
-
const left = evalAt(ctx, expr, "", a - h);
|
|
338
|
-
const right = evalAt(ctx, expr, "", a + h);
|
|
339
|
-
return (left + right) / 2;
|
|
340
|
-
};
|
|
341
|
-
let h = 0.1;
|
|
342
|
-
let prev = sample(h);
|
|
343
|
-
for (let i = 0; i < ctx.rt.maxIter; i++) {
|
|
344
|
-
h /= 2;
|
|
345
|
-
const cur = sample(h);
|
|
346
|
-
const scale = Math.max(1, Math.abs(cur), Math.abs(prev));
|
|
347
|
-
if (Math.abs(cur - prev) <= ctx.rt.tol * scale)
|
|
348
|
-
return real(cur);
|
|
349
|
-
prev = cur;
|
|
350
|
-
if (h < 1e-13)
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
return real(prev);
|
|
354
|
-
}, { expression: true });
|
|
355
|
-
// optimize: a local minimiser of f near a guess (= solve(diff f)). args =
|
|
356
|
-
// [expr, guess].
|
|
357
|
-
r.derive("optimize", 2, ["minimize", "minimise", "optimize", "argmin"], (args, ctx) => {
|
|
358
|
-
const expr = exprOf(args[0]);
|
|
359
|
-
const guess = asReal(args[1]);
|
|
360
|
-
return real(optimize((x) => evalAt(ctx, expr, "", x), guess, ctx.rt.tol, ctx.rt.maxIter));
|
|
361
|
-
}, { expression: true });
|
|
362
|
-
// converge and the matrix routines (regress, eig, svd, interpolate) take
|
|
363
|
-
// FUNCTION or ARRAY operands with no flat infix surface; they are offered
|
|
364
|
-
// through the pure exports above and used internally (e.g. sqrt = solve(t²−x),
|
|
365
|
-
// optimize = solve(diff f)).
|
|
366
|
-
}
|