@thi.ng/gp 0.4.81 → 0.4.83
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/CHANGELOG.md +1 -1
- package/api.js +0 -1
- package/ast.js +142 -128
- package/mep.js +89 -95
- package/package.json +13 -10
- package/utils.js +24 -16
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/ast.js
CHANGED
|
@@ -6,134 +6,148 @@ import { repeatedly } from "@thi.ng/transducers/repeatedly";
|
|
|
6
6
|
import { takeWhile } from "@thi.ng/transducers/take-while";
|
|
7
7
|
import { zipper } from "@thi.ng/zipper/zipper";
|
|
8
8
|
import { opNode, probabilities, terminalNode } from "./utils.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return loc.root;
|
|
79
|
-
loc = nextLoc;
|
|
9
|
+
class AST {
|
|
10
|
+
opts;
|
|
11
|
+
choices;
|
|
12
|
+
probTerminal;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
this.opts = { rnd: SYSTEM, ...opts };
|
|
15
|
+
assert(this.opts.probMutate < 1, "mutation probability must be < 1.0");
|
|
16
|
+
const probs = probabilities(this.opts);
|
|
17
|
+
this.probTerminal = probs.probTerminal;
|
|
18
|
+
this.choices = probs.iter;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns a random AST with given max tree depth. The max depth
|
|
22
|
+
* provided in {@link ASTOpts} is used by default.
|
|
23
|
+
*
|
|
24
|
+
* @param maxDepth -
|
|
25
|
+
*/
|
|
26
|
+
randomAST(maxDepth = this.opts.maxDepth) {
|
|
27
|
+
return this.randomASTNode(0, maxDepth);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Immutably transplants a randomly chosen subtree of `parent2` into
|
|
31
|
+
* a randomly chosen location in `parent1` and vice versa. Returns 2
|
|
32
|
+
* new trees.
|
|
33
|
+
*
|
|
34
|
+
* @param parent1 -
|
|
35
|
+
* @param parent2 -
|
|
36
|
+
*/
|
|
37
|
+
crossoverSingle(parent1, parent2) {
|
|
38
|
+
return [
|
|
39
|
+
this.selectRandomNode(parent1).replace(
|
|
40
|
+
this.selectRandomNode(parent2).node
|
|
41
|
+
).root,
|
|
42
|
+
this.selectRandomNode(parent2).replace(
|
|
43
|
+
this.selectRandomNode(parent1).node
|
|
44
|
+
).root
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Probilistically replaces randomly chosen tree nodes with a new random AST
|
|
49
|
+
* of given `maxDepth` (default: 1). Never mutates root.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* The AST's pre-configured max tree depth will always be respected, so
|
|
53
|
+
* depending on the depth of the selected mutation location, the randomly
|
|
54
|
+
* inserted/replaced subtree might be more shallow than given `maxDepth`.
|
|
55
|
+
* I.e. if a tree node at max depth is selected for mutation, it will always
|
|
56
|
+
* result in a randomly chosen terminal node only.
|
|
57
|
+
*
|
|
58
|
+
* @param tree -
|
|
59
|
+
* @param maxDepth -
|
|
60
|
+
*/
|
|
61
|
+
mutate(tree, maxDepth = 1) {
|
|
62
|
+
const { rnd, probMutate, maxDepth: limit } = this.opts;
|
|
63
|
+
let loc = this.asZipper(tree).next;
|
|
64
|
+
if (!loc)
|
|
65
|
+
return tree;
|
|
66
|
+
while (true) {
|
|
67
|
+
let nextLoc;
|
|
68
|
+
if (rnd.probability(probMutate)) {
|
|
69
|
+
loc = loc.replace(
|
|
70
|
+
this.randomASTNode(0, Math.min(limit - loc.depth, maxDepth))
|
|
71
|
+
);
|
|
72
|
+
nextLoc = loc.right;
|
|
73
|
+
if (!nextLoc) {
|
|
74
|
+
nextLoc = loc.up;
|
|
75
|
+
if (nextLoc) {
|
|
76
|
+
nextLoc = nextLoc.right;
|
|
77
|
+
}
|
|
80
78
|
}
|
|
79
|
+
} else {
|
|
80
|
+
nextLoc = loc.next;
|
|
81
|
+
}
|
|
82
|
+
if (!nextLoc)
|
|
83
|
+
return loc.root;
|
|
84
|
+
loc = nextLoc;
|
|
81
85
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns linearized/flat version of given AST as an array of [zipper
|
|
89
|
+
* locations](https://docs.thi.ng/umbrella/zipper/classes/Location.html).
|
|
90
|
+
*
|
|
91
|
+
* @param tree -
|
|
92
|
+
*/
|
|
93
|
+
linearizedAST(tree) {
|
|
94
|
+
return [
|
|
95
|
+
...iterator(
|
|
96
|
+
takeWhile((x) => !!x),
|
|
97
|
+
iterate((x) => x.next, this.asZipper(tree))
|
|
98
|
+
)
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns random {@link ASTNode} from given tree, using the user provided
|
|
103
|
+
* PRNG (via `opts`) or
|
|
104
|
+
* [`SYSTEM`](https://docs.thi.ng/umbrella/random/variables/SYSTEM.html).
|
|
105
|
+
* The returned value is a [zipper
|
|
106
|
+
* location](https://docs.thi.ng/umbrella/zipper/classes/Location.html) of
|
|
107
|
+
* the selected node.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* The actual `ASTNode` can be obtained via `.node`.
|
|
111
|
+
*
|
|
112
|
+
* Only nodes in the linearized index range `[min..max)` are returned
|
|
113
|
+
* (default: entire tree range). Since the linear tree length isn't known
|
|
114
|
+
* beforehand, `max` < 0 (default) is equivalent to the linearized tree end.
|
|
115
|
+
*
|
|
116
|
+
* @param opts -
|
|
117
|
+
* @param tree -
|
|
118
|
+
* @param min -
|
|
119
|
+
* @param max -
|
|
120
|
+
*/
|
|
121
|
+
selectRandomNode(tree, min = 0, max = -1) {
|
|
122
|
+
const rnd = this.opts.rnd;
|
|
123
|
+
const linTree = this.linearizedAST(tree);
|
|
124
|
+
let node;
|
|
125
|
+
max < 0 && (max = linTree.length);
|
|
126
|
+
node = linTree[rnd.minmax(min, max) | 0];
|
|
127
|
+
return node;
|
|
128
|
+
}
|
|
129
|
+
randomASTNode(d, maxDepth) {
|
|
130
|
+
const rnd = this.opts.rnd;
|
|
131
|
+
const geneID = this.choices.next().value;
|
|
132
|
+
if (geneID === 0 || d >= maxDepth)
|
|
133
|
+
return terminalNode(this.opts.terminal(rnd));
|
|
134
|
+
const op = this.opts.ops[geneID - 1];
|
|
135
|
+
const children = [
|
|
136
|
+
...repeatedly(() => this.randomASTNode(d + 1, maxDepth), op.arity)
|
|
137
|
+
];
|
|
138
|
+
return opNode(op.fn(rnd, children), children);
|
|
139
|
+
}
|
|
140
|
+
asZipper(tree) {
|
|
141
|
+
return zipper(
|
|
142
|
+
{
|
|
143
|
+
branch: (x) => x.type === "op",
|
|
144
|
+
children: (x) => x.args,
|
|
145
|
+
factory: (n, args) => opNode(n.op, args)
|
|
146
|
+
},
|
|
147
|
+
tree
|
|
148
|
+
);
|
|
149
|
+
}
|
|
139
150
|
}
|
|
151
|
+
export {
|
|
152
|
+
AST
|
|
153
|
+
};
|
package/mep.js
CHANGED
|
@@ -2,104 +2,98 @@ import { inRange } from "@thi.ng/math/interval";
|
|
|
2
2
|
import { SYSTEM } from "@thi.ng/random/system";
|
|
3
3
|
import { repeatedly } from "@thi.ng/transducers/repeatedly";
|
|
4
4
|
import { opNode, probabilities, terminalNode } from "./utils.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
class MEP {
|
|
6
|
+
opts;
|
|
7
|
+
probTerminal;
|
|
8
|
+
choices;
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
this.opts = { rnd: SYSTEM, ...opts };
|
|
11
|
+
const probs = probabilities(this.opts);
|
|
12
|
+
this.probTerminal = probs.probTerminal;
|
|
13
|
+
this.choices = probs.iter;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns a new random {@link MEPChromosome} with the configured
|
|
17
|
+
* probabilities of operator and terminal nodes (constants).
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* See {@link MEP.decodeChromosome} for conversion to
|
|
21
|
+
* {@link ASTNode}s.
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
randomChromosome() {
|
|
25
|
+
const res = [];
|
|
26
|
+
for (let i = 0, n = this.opts.chromoSize; i < n; i++) {
|
|
27
|
+
res[i] = this.randomGene(i);
|
|
14
28
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Decodes given chromosome into an array of {@link ASTNode}s and
|
|
33
|
+
* optionally applies tree depth filter (by default includes all).
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* A {@link MEPChromosome} encodes multiple solutions (one per gene
|
|
37
|
+
* slot), therefore a chromosome of length `n` will produce the same
|
|
38
|
+
* number ASTs (less if min/max tree depth filters are applied).
|
|
39
|
+
*
|
|
40
|
+
* @param chromosome -
|
|
41
|
+
* @param minDepth -
|
|
42
|
+
* @param maxDepth -
|
|
43
|
+
*/
|
|
44
|
+
decodeChromosome(chromosome, minDepth = 0, maxDepth = Infinity) {
|
|
45
|
+
const res = [];
|
|
46
|
+
const depths = [];
|
|
47
|
+
for (let i = 0; i < chromosome.length; i++) {
|
|
48
|
+
const gene = chromosome[i];
|
|
49
|
+
if (gene.type == "term") {
|
|
50
|
+
res[i] = gene;
|
|
51
|
+
depths[i] = 1;
|
|
52
|
+
} else {
|
|
53
|
+
res[i] = opNode(
|
|
54
|
+
gene.op,
|
|
55
|
+
gene.args.map((g) => res[g])
|
|
56
|
+
);
|
|
57
|
+
depths[i] = 1 + gene.args.reduce((d, a) => Math.max(d, depths[a]), 0);
|
|
58
|
+
}
|
|
30
59
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const depths = [];
|
|
47
|
-
for (let i = 0; i < chromosome.length; i++) {
|
|
48
|
-
const gene = chromosome[i];
|
|
49
|
-
if (gene.type == "term") {
|
|
50
|
-
res[i] = gene;
|
|
51
|
-
depths[i] = 1;
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
res[i] = opNode(gene.op, gene.args.map((g) => res[g]));
|
|
55
|
-
depths[i] =
|
|
56
|
-
1 + gene.args.reduce((d, a) => Math.max(d, depths[a]), 0);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return res.filter((_, i) => inRange(depths[i], minDepth, maxDepth));
|
|
60
|
+
return res.filter((_, i) => inRange(depths[i], minDepth, maxDepth));
|
|
61
|
+
}
|
|
62
|
+
crossoverSingle(chromo1, chromo2, cut) {
|
|
63
|
+
cut = cut !== void 0 ? cut : this.opts.rnd.int() % Math.min(chromo1.length, chromo2.length);
|
|
64
|
+
return [
|
|
65
|
+
chromo1.slice(0, cut).concat(chromo2.slice(cut)),
|
|
66
|
+
chromo2.slice(0, cut).concat(chromo1.slice(cut))
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
crossoverUniform(chromo1, chromo2) {
|
|
70
|
+
const rnd = this.opts.rnd;
|
|
71
|
+
const res = [];
|
|
72
|
+
const minLen = Math.min(chromo1.length, chromo2.length);
|
|
73
|
+
for (let i = 0; i < minLen; i++) {
|
|
74
|
+
res[i] = rnd.probability(0.5) ? chromo1[i] : chromo2[i];
|
|
60
75
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
chromo1.slice(0, cut).concat(chromo2.slice(cut)),
|
|
69
|
-
chromo2.slice(0, cut).concat(chromo1.slice(cut)),
|
|
70
|
-
];
|
|
76
|
+
return chromo1.length > minLen ? res.concat(chromo1.slice(minLen)) : chromo2.length > minLen ? res.concat(chromo2.slice(minLen)) : res;
|
|
77
|
+
}
|
|
78
|
+
mutate(chromo) {
|
|
79
|
+
const { rnd, probMutate } = this.opts;
|
|
80
|
+
const res = new Array(chromo.length);
|
|
81
|
+
for (let i = chromo.length; i-- > 0; ) {
|
|
82
|
+
res[i] = rnd.probability(probMutate) ? this.randomGene(i) : chromo[i];
|
|
71
83
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return chromo1.length > minLen
|
|
80
|
-
? res.concat(chromo1.slice(minLen))
|
|
81
|
-
: chromo2.length > minLen
|
|
82
|
-
? res.concat(chromo2.slice(minLen))
|
|
83
|
-
: res;
|
|
84
|
-
}
|
|
85
|
-
mutate(chromo) {
|
|
86
|
-
const { rnd, probMutate } = this.opts;
|
|
87
|
-
const res = new Array(chromo.length);
|
|
88
|
-
for (let i = chromo.length; i-- > 0;) {
|
|
89
|
-
res[i] = rnd.probability(probMutate)
|
|
90
|
-
? this.randomGene(i)
|
|
91
|
-
: chromo[i];
|
|
92
|
-
}
|
|
93
|
-
return res;
|
|
94
|
-
}
|
|
95
|
-
randomGene(i) {
|
|
96
|
-
const geneID = this.choices.next().value;
|
|
97
|
-
const rnd = this.opts.rnd;
|
|
98
|
-
if (i === 0 || geneID === 0) {
|
|
99
|
-
return terminalNode(this.opts.terminal(rnd));
|
|
100
|
-
}
|
|
101
|
-
const op = this.opts.ops[geneID - 1];
|
|
102
|
-
const args = [...repeatedly(() => rnd.int() % i, op.arity)];
|
|
103
|
-
return opNode(op.fn(rnd, args), args);
|
|
84
|
+
return res;
|
|
85
|
+
}
|
|
86
|
+
randomGene(i) {
|
|
87
|
+
const geneID = this.choices.next().value;
|
|
88
|
+
const rnd = this.opts.rnd;
|
|
89
|
+
if (i === 0 || geneID === 0) {
|
|
90
|
+
return terminalNode(this.opts.terminal(rnd));
|
|
104
91
|
}
|
|
92
|
+
const op = this.opts.ops[geneID - 1];
|
|
93
|
+
const args = [...repeatedly(() => rnd.int() % i, op.arity)];
|
|
94
|
+
return opNode(op.fn(rnd, args), args);
|
|
95
|
+
}
|
|
105
96
|
}
|
|
97
|
+
export {
|
|
98
|
+
MEP
|
|
99
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/gp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.83",
|
|
4
4
|
"description": "Genetic programming helpers & strategies (tree based & multi-expression programming)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,15 +35,16 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/errors": "^2.4.
|
|
38
|
-
"@thi.ng/math": "^5.7.
|
|
39
|
-
"@thi.ng/random": "^3.6.
|
|
40
|
-
"@thi.ng/transducers": "^8.8.
|
|
41
|
-
"@thi.ng/zipper": "^2.1.
|
|
38
|
+
"@thi.ng/api": "^8.9.13",
|
|
39
|
+
"@thi.ng/errors": "^2.4.7",
|
|
40
|
+
"@thi.ng/math": "^5.7.8",
|
|
41
|
+
"@thi.ng/random": "^3.6.19",
|
|
42
|
+
"@thi.ng/transducers": "^8.8.16",
|
|
43
|
+
"@thi.ng/zipper": "^2.1.70"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@microsoft/api-extractor": "^7.38.3",
|
|
47
|
+
"esbuild": "^0.19.8",
|
|
45
48
|
"rimraf": "^5.0.5",
|
|
46
49
|
"tools": "^0.0.1",
|
|
47
50
|
"typedoc": "^0.25.4",
|
|
@@ -69,7 +72,7 @@
|
|
|
69
72
|
"access": "public"
|
|
70
73
|
},
|
|
71
74
|
"engines": {
|
|
72
|
-
"node": ">=
|
|
75
|
+
"node": ">=18"
|
|
73
76
|
},
|
|
74
77
|
"files": [
|
|
75
78
|
"./*.js",
|
|
@@ -106,5 +109,5 @@
|
|
|
106
109
|
"status": "alpha",
|
|
107
110
|
"year": 2019
|
|
108
111
|
},
|
|
109
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "25a42a81fac8603a1e440a7aa8bc343276211ff4\n"
|
|
110
113
|
}
|
package/utils.js
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
// thing:no-export
|
|
2
1
|
import { assert } from "@thi.ng/errors/assert";
|
|
3
2
|
import { add } from "@thi.ng/transducers/add";
|
|
4
3
|
import { choices } from "@thi.ng/transducers/choices";
|
|
5
4
|
import { range } from "@thi.ng/transducers/range";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const terminalNode = (value) => ({
|
|
6
|
+
type: "term",
|
|
7
|
+
value
|
|
9
8
|
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const opNode = (op, args) => ({
|
|
10
|
+
type: "op",
|
|
11
|
+
op,
|
|
12
|
+
args
|
|
14
13
|
});
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const probabilities = (opts) => {
|
|
15
|
+
const probabilities2 = opts.ops.map((op) => op.prob);
|
|
16
|
+
const psum = add(probabilities2);
|
|
17
|
+
assert(psum < 1, "total op probabilities MUST be < 1");
|
|
18
|
+
return {
|
|
19
|
+
iter: choices(
|
|
20
|
+
[...range(probabilities2.length + 1)],
|
|
21
|
+
[1 - psum, ...probabilities2],
|
|
22
|
+
opts.rnd
|
|
23
|
+
),
|
|
24
|
+
probTerminal: 1 - psum
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export {
|
|
28
|
+
opNode,
|
|
29
|
+
probabilities,
|
|
30
|
+
terminalNode
|
|
23
31
|
};
|