@thi.ng/fuzzy 2.1.51 → 2.1.53
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/defuzz.js +34 -50
- package/package.json +8 -6
- package/rules.js +14 -42
- package/shapes.js +49 -189
- package/strategies/bisector.js +28 -49
- package/strategies/centroid.js +17 -44
- package/strategies/maxima.js +45 -124
- package/strategies/opts.js +7 -4
- package/tnorms.js +42 -109
- package/var.js +24 -83
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/defuzz.js
CHANGED
|
@@ -1,56 +1,40 @@
|
|
|
1
1
|
import { constant, intersect, union, weighted } from "./shapes.js";
|
|
2
2
|
import { centroidStrategy } from "./strategies/centroid.js";
|
|
3
3
|
import { snormMax, tnormMin } from "./tnorms.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* @param rules -
|
|
25
|
-
* @param vals -
|
|
26
|
-
* @param strategy -
|
|
27
|
-
* @param imply -
|
|
28
|
-
* @param combine -
|
|
29
|
-
*/
|
|
30
|
-
export const defuzz = (ins, outs, rules, vals, strategy = centroidStrategy(), imply = tnormMin, combine = snormMax) => {
|
|
31
|
-
const ruleTerms = rules.map((r) => {
|
|
32
|
-
let alpha = null;
|
|
33
|
-
for (let id in vals) {
|
|
34
|
-
if (r.if[id]) {
|
|
35
|
-
const v = ins[id].terms[r.if[id]](vals[id]);
|
|
36
|
-
alpha = alpha !== null ? r.op(alpha, v) : v;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const terms = {};
|
|
40
|
-
if (alpha) {
|
|
41
|
-
const aterm = constant(alpha);
|
|
42
|
-
for (let id in r.then) {
|
|
43
|
-
if (outs[id]) {
|
|
44
|
-
const oterm = outs[id].terms[r.then[id]];
|
|
45
|
-
terms[id] = intersect(imply, r.weight == 1 ? oterm : weighted(oterm, r.weight), aterm);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
4
|
+
const defuzz = (ins, outs, rules, vals, strategy = centroidStrategy(), imply = tnormMin, combine = snormMax) => {
|
|
5
|
+
const ruleTerms = rules.map((r) => {
|
|
6
|
+
let alpha = null;
|
|
7
|
+
for (let id in vals) {
|
|
8
|
+
if (r.if[id]) {
|
|
9
|
+
const v = ins[id].terms[r.if[id]](vals[id]);
|
|
10
|
+
alpha = alpha !== null ? r.op(alpha, v) : v;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const terms = {};
|
|
14
|
+
if (alpha) {
|
|
15
|
+
const aterm = constant(alpha);
|
|
16
|
+
for (let id in r.then) {
|
|
17
|
+
if (outs[id]) {
|
|
18
|
+
const oterm = outs[id].terms[r.then[id]];
|
|
19
|
+
terms[id] = intersect(
|
|
20
|
+
imply,
|
|
21
|
+
r.weight == 1 ? oterm : weighted(oterm, r.weight),
|
|
22
|
+
aterm
|
|
23
|
+
);
|
|
48
24
|
}
|
|
49
|
-
|
|
50
|
-
});
|
|
51
|
-
const res = {};
|
|
52
|
-
for (let id in outs) {
|
|
53
|
-
res[id] = strategy(union(combine, ...ruleTerms.map((r) => r[id]).filter((f) => !!f)), outs[id].domain);
|
|
25
|
+
}
|
|
54
26
|
}
|
|
55
|
-
return
|
|
27
|
+
return terms;
|
|
28
|
+
});
|
|
29
|
+
const res = {};
|
|
30
|
+
for (let id in outs) {
|
|
31
|
+
res[id] = strategy(
|
|
32
|
+
union(combine, ...ruleTerms.map((r) => r[id]).filter((f) => !!f)),
|
|
33
|
+
outs[id].domain
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return res;
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
defuzz
|
|
56
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/fuzzy",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.53",
|
|
4
4
|
"description": "Fuzzy logic operators & configurable rule inferencing engine",
|
|
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 strategies",
|
|
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,12 +35,12 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/math": "^5.7.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/math": "^5.7.7"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@microsoft/api-extractor": "^7.38.3",
|
|
41
|
-
"
|
|
43
|
+
"esbuild": "^0.19.8",
|
|
42
44
|
"rimraf": "^5.0.5",
|
|
43
45
|
"tools": "^0.0.1",
|
|
44
46
|
"typedoc": "^0.25.4",
|
|
@@ -104,5 +106,5 @@
|
|
|
104
106
|
"thi.ng": {
|
|
105
107
|
"year": 2020
|
|
106
108
|
},
|
|
107
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
108
110
|
}
|
package/rules.js
CHANGED
|
@@ -1,44 +1,16 @@
|
|
|
1
1
|
import { snormMax, tnormMin, tnormProduct } from "./tnorms.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @remarks
|
|
8
|
-
* The input and output objects are each using LVar names as keys and respective
|
|
9
|
-
* var terms (their names) as values (see example).
|
|
10
|
-
*
|
|
11
|
-
* The optional rule weight (default: 1) is used by {@link defuzz} to adjust
|
|
12
|
-
* rule importance.
|
|
13
|
-
*
|
|
14
|
-
* Also @see {@link and}, {@link strongAnd}, {@link or} for syntax sugar.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* // given 3 LVars from a classic fuzzy logic example:
|
|
19
|
-
* // food, service, tip
|
|
20
|
-
*
|
|
21
|
-
* // define this rule:
|
|
22
|
-
* // "If the food was bad AND service poor, then a small tip only"
|
|
23
|
-
* // here multiply is used for strong conjunction of the food & service terms
|
|
24
|
-
* rule(
|
|
25
|
-
* (a, b) => a * b,
|
|
26
|
-
* { food: "bad", service: "poor" },
|
|
27
|
-
* { tip: "small" }
|
|
28
|
-
* )
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* @param op -
|
|
32
|
-
* @param $if
|
|
33
|
-
* @param then -
|
|
34
|
-
* @param weight -
|
|
35
|
-
*/
|
|
36
|
-
export const rule = (op, $if, then, weight = 1) => ({
|
|
37
|
-
if: $if,
|
|
38
|
-
then,
|
|
39
|
-
op,
|
|
40
|
-
weight,
|
|
2
|
+
const rule = (op, $if, then, weight = 1) => ({
|
|
3
|
+
if: $if,
|
|
4
|
+
then,
|
|
5
|
+
op,
|
|
6
|
+
weight
|
|
41
7
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
8
|
+
const and = ($if, then, weight) => rule(tnormMin, $if, then, weight);
|
|
9
|
+
const strongAnd = ($if, then, weight) => rule(tnormProduct, $if, then, weight);
|
|
10
|
+
const or = ($if, then, weight) => rule(snormMax, $if, then, weight);
|
|
11
|
+
export {
|
|
12
|
+
and,
|
|
13
|
+
or,
|
|
14
|
+
rule,
|
|
15
|
+
strongAnd
|
|
16
|
+
};
|
package/shapes.js
CHANGED
|
@@ -2,195 +2,55 @@ import { EPS } from "@thi.ng/math/api";
|
|
|
2
2
|
import { eqDelta } from "@thi.ng/math/eqdelta";
|
|
3
3
|
import { fit, fitClamped } from "@thi.ng/math/fit";
|
|
4
4
|
import { gaussian as $gaussian, sigmoid as $sigmoid } from "@thi.ng/math/mix";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export const point = (p, eps = EPS) => (x) => eqDelta(x, p, eps) ? 1 : 0;
|
|
20
|
-
/**
|
|
21
|
-
* HOF {@link FuzzyFn} yielding a rising ramp in [a,b] interval, clamped to
|
|
22
|
-
* [0,1] outputs. Returns 0.0 for inputs <= `a` and 1.0 for inputs >= `b`.
|
|
23
|
-
*
|
|
24
|
-
* @param a -
|
|
25
|
-
* @param b -
|
|
26
|
-
*/
|
|
27
|
-
export const ramp = (a, b) => (x) => fitClamped(x, a, b, 0, 1);
|
|
28
|
-
/**
|
|
29
|
-
* HOF {@link FuzzyFn} yielding a triangle in the input range `[a..b..c]` with
|
|
30
|
-
* `b` defining the position of the peak value (1.0). Returns 0.0 for inputs <
|
|
31
|
-
* `a` or > `c`.
|
|
32
|
-
*
|
|
33
|
-
* @param a -
|
|
34
|
-
* @param b -
|
|
35
|
-
* @param c -
|
|
36
|
-
*/
|
|
37
|
-
export const triangle = (a, b, c) => (x) => x < a || x > c ? 0 : x <= b ? fit(x, a, b, 0, 1) : fit(x, b, c, 1, 0);
|
|
38
|
-
/**
|
|
39
|
-
* Similar to {@link triangle}, but yielding a trapezoid for the input range
|
|
40
|
-
* `[a..b..c..d]` with `b` and `c` defining the peak value range (with 1.0
|
|
41
|
-
* outputs). Returns 0.0 for inputs < `a` or > `d`.
|
|
42
|
-
*
|
|
43
|
-
* @param a -
|
|
44
|
-
* @param b -
|
|
45
|
-
* @param c -
|
|
46
|
-
* @param d -
|
|
47
|
-
*/
|
|
48
|
-
export const trapezoid = (a, b, c, d) => (x) => x < a || x > d
|
|
49
|
-
? 0
|
|
50
|
-
: x > b && x < c
|
|
51
|
-
? 1
|
|
52
|
-
: x <= b
|
|
53
|
-
? fit(x, a, b, 0, 1)
|
|
54
|
-
: fit(x, c, d, 1, 0);
|
|
55
|
-
/**
|
|
56
|
-
* HOF {@link FuzzyFn}, yielding sigmoid curve with configurable `steep` and
|
|
57
|
-
* positioned such that `f(bias) = 0.5`.
|
|
58
|
-
*
|
|
59
|
-
* @param bias -
|
|
60
|
-
* @param steep -
|
|
61
|
-
*/
|
|
62
|
-
export const sigmoid = (bias, steep) => (x) => $sigmoid(bias, steep, x);
|
|
63
|
-
/**
|
|
64
|
-
* HOF {@link FuzzyFn}, yielding gaussian bell curve with its peak at `bias` and
|
|
65
|
-
* width defined by `sigma`.
|
|
66
|
-
*
|
|
67
|
-
* @param bias -
|
|
68
|
-
* @param sigma -
|
|
69
|
-
*/
|
|
70
|
-
export const gaussian = (bias, sigma) => (x) => $gaussian(bias, sigma, x);
|
|
71
|
-
/**
|
|
72
|
-
* Higher-order function: Takes an existing {@link FuzzyFn} `fn` and returns
|
|
73
|
-
* a new one producing its negated outcome aka `1 - fn(x)`.
|
|
74
|
-
*
|
|
75
|
-
* @param fn -
|
|
76
|
-
*/
|
|
77
|
-
export const negate = (fn) => (x) => 1 - fn(x);
|
|
78
|
-
/**
|
|
79
|
-
* Inverse of {@link ramp}, i.e. a falling slope from `a` -> `b`.
|
|
80
|
-
*
|
|
81
|
-
* @param a -
|
|
82
|
-
* @param b -
|
|
83
|
-
*/
|
|
84
|
-
export const invRamp = (a, b) => negate(ramp(a, b));
|
|
85
|
-
/**
|
|
86
|
-
* Inverse of {@link sigmoid}.
|
|
87
|
-
*
|
|
88
|
-
* @param bias -
|
|
89
|
-
* @param steep -
|
|
90
|
-
*/
|
|
91
|
-
export const invSigmoid = (bias, steep) => negate(sigmoid(bias, steep));
|
|
92
|
-
/**
|
|
93
|
-
* Higher-order function: Takes an existing {@link FuzzyFn} `fn` and `weight`
|
|
94
|
-
* factor. Returns new function which computes: `weight * fn(x)`.
|
|
95
|
-
*
|
|
96
|
-
* @param fn -
|
|
97
|
-
* @param weight -
|
|
98
|
-
*/
|
|
99
|
-
export const weighted = (fn, weight) => (x) => weight * fn(x);
|
|
100
|
-
/**
|
|
101
|
-
* Higher order function. Returns new function which selects subset of given
|
|
102
|
-
* fuzzy set where `fn(x) > alpha`, or else returns 0.
|
|
103
|
-
*
|
|
104
|
-
* @param fn -
|
|
105
|
-
* @param alpha -
|
|
106
|
-
*/
|
|
107
|
-
export const alphaCut = (fn, alpha = 0.5) => (x) => {
|
|
108
|
-
const y = fn(x);
|
|
109
|
-
return y > alpha ? y : 0;
|
|
5
|
+
const constant = (x) => () => x;
|
|
6
|
+
const point = (p, eps = EPS) => (x) => eqDelta(x, p, eps) ? 1 : 0;
|
|
7
|
+
const ramp = (a, b) => (x) => fitClamped(x, a, b, 0, 1);
|
|
8
|
+
const triangle = (a, b, c) => (x) => x < a || x > c ? 0 : x <= b ? fit(x, a, b, 0, 1) : fit(x, b, c, 1, 0);
|
|
9
|
+
const trapezoid = (a, b, c, d) => (x) => x < a || x > d ? 0 : x > b && x < c ? 1 : x <= b ? fit(x, a, b, 0, 1) : fit(x, c, d, 1, 0);
|
|
10
|
+
const sigmoid = (bias, steep) => (x) => $sigmoid(bias, steep, x);
|
|
11
|
+
const gaussian = (bias, sigma) => (x) => $gaussian(bias, sigma, x);
|
|
12
|
+
const negate = (fn) => (x) => 1 - fn(x);
|
|
13
|
+
const invRamp = (a, b) => negate(ramp(a, b));
|
|
14
|
+
const invSigmoid = (bias, steep) => negate(sigmoid(bias, steep));
|
|
15
|
+
const weighted = (fn, weight) => (x) => weight * fn(x);
|
|
16
|
+
const alphaCut = (fn, alpha = 0.5) => (x) => {
|
|
17
|
+
const y = fn(x);
|
|
18
|
+
return y > alpha ? y : 0;
|
|
110
19
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
*
|
|
115
|
-
* @param fn -
|
|
116
|
-
* @param alpha -
|
|
117
|
-
*/
|
|
118
|
-
export const invAlphaCut = (fn, alpha = 0.5) => (x) => {
|
|
119
|
-
const y = fn(x);
|
|
120
|
-
return y < alpha ? y : 0;
|
|
20
|
+
const invAlphaCut = (fn, alpha = 0.5) => (x) => {
|
|
21
|
+
const y = fn(x);
|
|
22
|
+
return y < alpha ? y : 0;
|
|
121
23
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
* 0,
|
|
155
|
-
* triangle(1,3,5),
|
|
156
|
-
* triangle(3,5,7)
|
|
157
|
-
* )
|
|
158
|
-
*
|
|
159
|
-
* M(3) // 1
|
|
160
|
-
* M(4) // 0.5
|
|
161
|
-
* M(5) // 1
|
|
162
|
-
* ```
|
|
163
|
-
*
|
|
164
|
-
* @param op -
|
|
165
|
-
* @param initial -
|
|
166
|
-
* @param fns -
|
|
167
|
-
*/
|
|
168
|
-
export const compose = (op, initial, ...fns) => {
|
|
169
|
-
const [a, b] = fns;
|
|
170
|
-
switch (fns.length) {
|
|
171
|
-
case 0:
|
|
172
|
-
throw new Error("no fuzzy sets given");
|
|
173
|
-
case 1:
|
|
174
|
-
return a;
|
|
175
|
-
case 2:
|
|
176
|
-
return (x) => op(a(x), b(x));
|
|
177
|
-
default:
|
|
178
|
-
return (x) => fns.reduce((acc, f) => op(acc, f(x)), initial);
|
|
179
|
-
}
|
|
24
|
+
const compose = (op, initial, ...fns) => {
|
|
25
|
+
const [a, b] = fns;
|
|
26
|
+
switch (fns.length) {
|
|
27
|
+
case 0:
|
|
28
|
+
throw new Error("no fuzzy sets given");
|
|
29
|
+
case 1:
|
|
30
|
+
return a;
|
|
31
|
+
case 2:
|
|
32
|
+
return (x) => op(a(x), b(x));
|
|
33
|
+
default:
|
|
34
|
+
return (x) => fns.reduce((acc, f) => op(acc, f(x)), initial);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const intersect = (op, ...fns) => compose(op, 1, ...fns);
|
|
38
|
+
const union = (op, ...fns) => compose(op, 0, ...fns);
|
|
39
|
+
export {
|
|
40
|
+
alphaCut,
|
|
41
|
+
compose,
|
|
42
|
+
constant,
|
|
43
|
+
gaussian,
|
|
44
|
+
intersect,
|
|
45
|
+
invAlphaCut,
|
|
46
|
+
invRamp,
|
|
47
|
+
invSigmoid,
|
|
48
|
+
negate,
|
|
49
|
+
point,
|
|
50
|
+
ramp,
|
|
51
|
+
sigmoid,
|
|
52
|
+
trapezoid,
|
|
53
|
+
triangle,
|
|
54
|
+
union,
|
|
55
|
+
weighted
|
|
180
56
|
};
|
|
181
|
-
/**
|
|
182
|
-
* Syntax sugar for {@link compose} with an initial value of 1.0. The `op` is
|
|
183
|
-
* supposed to be a T-norm.
|
|
184
|
-
*
|
|
185
|
-
* @param op -
|
|
186
|
-
* @param fns -
|
|
187
|
-
*/
|
|
188
|
-
export const intersect = (op, ...fns) => compose(op, 1, ...fns);
|
|
189
|
-
/**
|
|
190
|
-
* Syntax sugar for {@link compose} with an initial value of 0.0. The `op` is
|
|
191
|
-
* supposed to be a S-norm.
|
|
192
|
-
*
|
|
193
|
-
* @param op -
|
|
194
|
-
* @param fns -
|
|
195
|
-
*/
|
|
196
|
-
export const union = (op, ...fns) => compose(op, 0, ...fns);
|
package/strategies/bisector.js
CHANGED
|
@@ -1,52 +1,31 @@
|
|
|
1
1
|
import { fit } from "@thi.ng/math/fit";
|
|
2
2
|
import { defaultOpts } from "./opts.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* @param opts -
|
|
32
|
-
*/
|
|
33
|
-
export const bisectorStrategy = (opts) => {
|
|
34
|
-
let { samples } = defaultOpts(opts);
|
|
35
|
-
return (fn, [min, max]) => {
|
|
36
|
-
const delta = (max - min) / samples;
|
|
37
|
-
let sum = [];
|
|
38
|
-
for (let i = 0, acc = 0; i <= samples; i++) {
|
|
39
|
-
acc += fn(min + i * delta);
|
|
40
|
-
sum.push(acc);
|
|
41
|
-
}
|
|
42
|
-
if (!sum.length)
|
|
43
|
-
return min;
|
|
44
|
-
const mean = sum[samples] * 0.5;
|
|
45
|
-
for (let i = 1; i <= samples; i++) {
|
|
46
|
-
if (sum[i] >= mean) {
|
|
47
|
-
return fit(mean, sum[i - 1], sum[i], min + (i - 1) * delta, min + i * delta);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return min;
|
|
51
|
-
};
|
|
3
|
+
const bisectorStrategy = (opts) => {
|
|
4
|
+
let { samples } = defaultOpts(opts);
|
|
5
|
+
return (fn, [min, max]) => {
|
|
6
|
+
const delta = (max - min) / samples;
|
|
7
|
+
let sum = [];
|
|
8
|
+
for (let i = 0, acc = 0; i <= samples; i++) {
|
|
9
|
+
acc += fn(min + i * delta);
|
|
10
|
+
sum.push(acc);
|
|
11
|
+
}
|
|
12
|
+
if (!sum.length)
|
|
13
|
+
return min;
|
|
14
|
+
const mean = sum[samples] * 0.5;
|
|
15
|
+
for (let i = 1; i <= samples; i++) {
|
|
16
|
+
if (sum[i] >= mean) {
|
|
17
|
+
return fit(
|
|
18
|
+
mean,
|
|
19
|
+
sum[i - 1],
|
|
20
|
+
sum[i],
|
|
21
|
+
min + (i - 1) * delta,
|
|
22
|
+
min + i * delta
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return min;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
bisectorStrategy
|
|
52
31
|
};
|
package/strategies/centroid.js
CHANGED
|
@@ -1,46 +1,19 @@
|
|
|
1
1
|
import { defaultOpts } from "./opts.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* // ......▁█████████████|█████████████▁.....
|
|
20
|
-
* // ......██████████████|██████████████.....
|
|
21
|
-
* // .....███████████████|███████████████....
|
|
22
|
-
* // ....▇███████████████|███████████████▇...
|
|
23
|
-
* // ...▅████████████████|████████████████▅..
|
|
24
|
-
* // ..▃█████████████████|█████████████████▃.
|
|
25
|
-
* // .▁██████████████████|██████████████████▁
|
|
26
|
-
* // .███████████████████|███████████████████
|
|
27
|
-
* // ^ 3.00
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* @param opts -
|
|
31
|
-
*/
|
|
32
|
-
export const centroidStrategy = (opts) => {
|
|
33
|
-
let { samples } = defaultOpts(opts);
|
|
34
|
-
return (fn, [min, max]) => {
|
|
35
|
-
const delta = (max - min) / samples;
|
|
36
|
-
let num = 0;
|
|
37
|
-
let denom = 0;
|
|
38
|
-
for (let i = 0; i <= samples; i++) {
|
|
39
|
-
const x = min + i * delta;
|
|
40
|
-
const y = fn(x);
|
|
41
|
-
num += x * y;
|
|
42
|
-
denom += y;
|
|
43
|
-
}
|
|
44
|
-
return num / denom;
|
|
45
|
-
};
|
|
2
|
+
const centroidStrategy = (opts) => {
|
|
3
|
+
let { samples } = defaultOpts(opts);
|
|
4
|
+
return (fn, [min, max]) => {
|
|
5
|
+
const delta = (max - min) / samples;
|
|
6
|
+
let num = 0;
|
|
7
|
+
let denom = 0;
|
|
8
|
+
for (let i = 0; i <= samples; i++) {
|
|
9
|
+
const x = min + i * delta;
|
|
10
|
+
const y = fn(x);
|
|
11
|
+
num += x * y;
|
|
12
|
+
denom += y;
|
|
13
|
+
}
|
|
14
|
+
return num / denom;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export {
|
|
18
|
+
centroidStrategy
|
|
46
19
|
};
|
package/strategies/maxima.js
CHANGED
|
@@ -1,129 +1,50 @@
|
|
|
1
1
|
import { eqDelta } from "@thi.ng/math/eqdelta";
|
|
2
2
|
import { defaultOpts } from "./opts.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* // .███████████████████|███████████████████
|
|
25
|
-
* // ^ 3.00
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* @param opts -
|
|
29
|
-
*/
|
|
30
|
-
export const meanOfMaximaStrategy = (opts) => {
|
|
31
|
-
const { samples, eps } = defaultOpts(opts);
|
|
32
|
-
return (fn, [min, max]) => {
|
|
33
|
-
const delta = (max - min) / samples;
|
|
34
|
-
let peak = -Infinity;
|
|
35
|
-
let peakPos = min;
|
|
36
|
-
let n = 1;
|
|
37
|
-
for (let i = 0; i <= samples; i++) {
|
|
38
|
-
const t = min + i * delta;
|
|
39
|
-
const x = fn(t);
|
|
40
|
-
if (eqDelta(x, peak, eps)) {
|
|
41
|
-
peakPos += t;
|
|
42
|
-
n++;
|
|
43
|
-
}
|
|
44
|
-
else if (x > peak) {
|
|
45
|
-
peak = x;
|
|
46
|
-
peakPos = t;
|
|
47
|
-
n = 1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return peakPos / n;
|
|
51
|
-
};
|
|
3
|
+
const meanOfMaximaStrategy = (opts) => {
|
|
4
|
+
const { samples, eps } = defaultOpts(opts);
|
|
5
|
+
return (fn, [min, max]) => {
|
|
6
|
+
const delta = (max - min) / samples;
|
|
7
|
+
let peak = -Infinity;
|
|
8
|
+
let peakPos = min;
|
|
9
|
+
let n = 1;
|
|
10
|
+
for (let i = 0; i <= samples; i++) {
|
|
11
|
+
const t = min + i * delta;
|
|
12
|
+
const x = fn(t);
|
|
13
|
+
if (eqDelta(x, peak, eps)) {
|
|
14
|
+
peakPos += t;
|
|
15
|
+
n++;
|
|
16
|
+
} else if (x > peak) {
|
|
17
|
+
peak = x;
|
|
18
|
+
peakPos = t;
|
|
19
|
+
n = 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return peakPos / n;
|
|
23
|
+
};
|
|
52
24
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* // ......█|███████████████████████████.....
|
|
70
|
-
* // .....██|████████████████████████████....
|
|
71
|
-
* // ....▇██|████████████████████████████▇...
|
|
72
|
-
* // ...▅███|█████████████████████████████▅..
|
|
73
|
-
* // ..▃████|██████████████████████████████▃.
|
|
74
|
-
* // .▁█████|███████████████████████████████▁
|
|
75
|
-
* // .██████|████████████████████████████████
|
|
76
|
-
* // ^ 1.02
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* @param opts -
|
|
80
|
-
*/
|
|
81
|
-
export const firstOfMaximaStrategy = (opts) => {
|
|
82
|
-
const { samples } = defaultOpts(opts);
|
|
83
|
-
return (fn, [min, max]) => {
|
|
84
|
-
const delta = (max - min) / samples;
|
|
85
|
-
let peak = -Infinity;
|
|
86
|
-
let peakPos = min;
|
|
87
|
-
for (let i = 0; i <= samples; i++) {
|
|
88
|
-
const t = min + i * delta;
|
|
89
|
-
const x = fn(t);
|
|
90
|
-
if (x > peak) {
|
|
91
|
-
peak = x;
|
|
92
|
-
peakPos = t;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return peakPos;
|
|
96
|
-
};
|
|
25
|
+
const firstOfMaximaStrategy = (opts) => {
|
|
26
|
+
const { samples } = defaultOpts(opts);
|
|
27
|
+
return (fn, [min, max]) => {
|
|
28
|
+
const delta = (max - min) / samples;
|
|
29
|
+
let peak = -Infinity;
|
|
30
|
+
let peakPos = min;
|
|
31
|
+
for (let i = 0; i <= samples; i++) {
|
|
32
|
+
const t = min + i * delta;
|
|
33
|
+
const x = fn(t);
|
|
34
|
+
if (x > peak) {
|
|
35
|
+
peak = x;
|
|
36
|
+
peakPos = t;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return peakPos;
|
|
40
|
+
};
|
|
97
41
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* @see {@Link DefuzzStrategyOpts}
|
|
107
|
-
*
|
|
108
|
-
* @example
|
|
109
|
-
* ```ts
|
|
110
|
-
* lastOfMaximaStrategy()(trapezoid(0,1,5,6), [0,6])
|
|
111
|
-
* // 4.98
|
|
112
|
-
*
|
|
113
|
-
* // ......▁██████████████████████████|▁.....
|
|
114
|
-
* // ......███████████████████████████|█.....
|
|
115
|
-
* // .....████████████████████████████|██....
|
|
116
|
-
* // ....▇████████████████████████████|██▇...
|
|
117
|
-
* // ...▅█████████████████████████████|███▅..
|
|
118
|
-
* // ..▃██████████████████████████████|████▃.
|
|
119
|
-
* // .▁███████████████████████████████|█████▁
|
|
120
|
-
* // .████████████████████████████████|██████
|
|
121
|
-
* // ^ 4.98
|
|
122
|
-
* ```
|
|
123
|
-
*
|
|
124
|
-
* @param opts -
|
|
125
|
-
*/
|
|
126
|
-
export const lastOfMaximaStrategy = (opts) => {
|
|
127
|
-
const impl = firstOfMaximaStrategy(opts);
|
|
128
|
-
return (fn, [min, max]) => impl(fn, [max, min]);
|
|
42
|
+
const lastOfMaximaStrategy = (opts) => {
|
|
43
|
+
const impl = firstOfMaximaStrategy(opts);
|
|
44
|
+
return (fn, [min, max]) => impl(fn, [max, min]);
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
firstOfMaximaStrategy,
|
|
48
|
+
lastOfMaximaStrategy,
|
|
49
|
+
meanOfMaximaStrategy
|
|
129
50
|
};
|
package/strategies/opts.js
CHANGED
package/tnorms.js
CHANGED
|
@@ -1,113 +1,46 @@
|
|
|
1
1
|
import { norm } from "@thi.ng/math/fit";
|
|
2
2
|
import { clamp0 } from "@thi.ng/math/interval";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*/
|
|
26
|
-
export const tnormYager = (p = 2) => p === 0
|
|
27
|
-
? () => 0
|
|
28
|
-
: (x, y) => clamp0(1 - ((1 - x) ** p + (1 - y) ** p) ** (1 / p));
|
|
29
|
-
/**
|
|
30
|
-
* HOF T-norm. Parametric Dombi with `p` controlling curvature.
|
|
31
|
-
*
|
|
32
|
-
* @remarks
|
|
33
|
-
* Interactive graph: https://www.desmos.com/calculator/4bneccqs3p
|
|
34
|
-
*
|
|
35
|
-
* @param p - curve param [0..∞], default: 2
|
|
36
|
-
*/
|
|
37
|
-
export const tnormDombi = (p = 2) => p === 0
|
|
38
|
-
? () => 0
|
|
39
|
-
: (x, y) => x === 0 || y === 0
|
|
40
|
-
? 0
|
|
41
|
-
: 1 /
|
|
42
|
-
(1 +
|
|
43
|
-
(((1 - x) / x) ** p + ((1 - y) / y) ** p) **
|
|
44
|
-
(1 / p));
|
|
45
|
-
/**
|
|
46
|
-
* HOF T-norm. Parametric Aczél–Alsina with `p` controlling curvature.
|
|
47
|
-
*
|
|
48
|
-
* @remarks
|
|
49
|
-
* Interactive graph: https://www.desmos.com/calculator/4bneccqs3p
|
|
50
|
-
*
|
|
51
|
-
* @param p - curve param [0..∞], default: 2
|
|
52
|
-
*/
|
|
53
|
-
export const tnormAczelAlsina = (p = 2) => (x, y) => Math.exp(-((Math.abs(Math.log(x)) ** p + Math.abs(Math.log(y)) ** p) **
|
|
54
|
-
(1 / p)));
|
|
55
|
-
/**
|
|
56
|
-
* S-norm (T-conorm), dual of {@link tnormMin}.
|
|
57
|
-
*
|
|
58
|
-
* @param x -
|
|
59
|
-
* @param y -
|
|
60
|
-
*/
|
|
61
|
-
export const snormMax = (x, y) => Math.max(x, y);
|
|
62
|
-
/**
|
|
63
|
-
* S-norm (T-conorm), dual of {@link tnormProduct}, probabilistic sum:
|
|
64
|
-
* `a + b - a * b`
|
|
65
|
-
*
|
|
66
|
-
* @param x -
|
|
67
|
-
* @param y -
|
|
68
|
-
*/
|
|
69
|
-
export const snormProbabilistic = (x, y) => x + y - x * y;
|
|
70
|
-
/**
|
|
71
|
-
* S-norm (T-conorm), dual of {@link tnormLukasiewicz}.
|
|
72
|
-
*
|
|
73
|
-
* @param x -
|
|
74
|
-
* @param y -
|
|
75
|
-
*/
|
|
76
|
-
export const snormBoundedSum = (x, y) => Math.min(x + y, 1);
|
|
77
|
-
/**
|
|
78
|
-
* S-norm (T-conorm), dual of {@link tnormDrastic}.
|
|
79
|
-
*/
|
|
80
|
-
export const snormDrastic = (x, y) => (x === 0 ? y : y === 0 ? x : 1);
|
|
81
|
-
/**
|
|
82
|
-
* S-norm (T-conorm), dual of {@link tnormNilpotent}.
|
|
83
|
-
*
|
|
84
|
-
* @param x -
|
|
85
|
-
* @param y -
|
|
86
|
-
*/
|
|
87
|
-
export const snormNilpotent = (x, y) => (x + y < 1 ? Math.max(x, y) : 1);
|
|
88
|
-
/**
|
|
89
|
-
* S-norm (T-conorm), dual of {@link tnormHamacher}, iff that T-norm's curve
|
|
90
|
-
* param is `p=2`.
|
|
91
|
-
*
|
|
92
|
-
* @param x -
|
|
93
|
-
* @param y -
|
|
94
|
-
*/
|
|
95
|
-
export const snormEinstein = (x, y) => (x + y) / (1 + x * y);
|
|
96
|
-
/**
|
|
97
|
-
* HOF t-norm. Constructs a new t-norm based on given t-norms for disjoint
|
|
98
|
-
* subintervals, completing remaining regions via {@link min}.
|
|
99
|
-
*
|
|
100
|
-
* @remarks
|
|
101
|
-
* Reference: https://en.wikipedia.org/wiki/Construction_of_t-norms#Ordinal_sums
|
|
102
|
-
*
|
|
103
|
-
* @param specs -
|
|
104
|
-
*/
|
|
105
|
-
export const ordinalSum = (specs) => (x, y) => {
|
|
106
|
-
for (let s of specs) {
|
|
107
|
-
const [a, b] = s.domain;
|
|
108
|
-
if (x >= a && x <= b && y >= a && y <= b) {
|
|
109
|
-
return a + (b - a) * s.tnorm(norm(x, a, b), norm(y, a, b));
|
|
110
|
-
}
|
|
3
|
+
const tnormMin = (x, y) => Math.min(x, y);
|
|
4
|
+
const tnormProduct = (x, y) => x * y;
|
|
5
|
+
const tnormLukasiewicz = (x, y) => clamp0(x + y - 1);
|
|
6
|
+
const tnormDrastic = (x, y) => x === 1 ? y : y === 1 ? x : 0;
|
|
7
|
+
const tnormNilpotent = (x, y) => x + y > 1 ? Math.min(x, y) : 0;
|
|
8
|
+
const tnormHamacher = (p = 2) => (x, y) => x === 0 && y === 0 ? 0 : x * y / (p + (1 - p) * (x + y - x * y));
|
|
9
|
+
const tnormYager = (p = 2) => p === 0 ? () => 0 : (x, y) => clamp0(1 - ((1 - x) ** p + (1 - y) ** p) ** (1 / p));
|
|
10
|
+
const tnormDombi = (p = 2) => p === 0 ? () => 0 : (x, y) => x === 0 || y === 0 ? 0 : 1 / (1 + (((1 - x) / x) ** p + ((1 - y) / y) ** p) ** (1 / p));
|
|
11
|
+
const tnormAczelAlsina = (p = 2) => (x, y) => Math.exp(
|
|
12
|
+
-((Math.abs(Math.log(x)) ** p + Math.abs(Math.log(y)) ** p) ** (1 / p))
|
|
13
|
+
);
|
|
14
|
+
const snormMax = (x, y) => Math.max(x, y);
|
|
15
|
+
const snormProbabilistic = (x, y) => x + y - x * y;
|
|
16
|
+
const snormBoundedSum = (x, y) => Math.min(x + y, 1);
|
|
17
|
+
const snormDrastic = (x, y) => x === 0 ? y : y === 0 ? x : 1;
|
|
18
|
+
const snormNilpotent = (x, y) => x + y < 1 ? Math.max(x, y) : 1;
|
|
19
|
+
const snormEinstein = (x, y) => (x + y) / (1 + x * y);
|
|
20
|
+
const ordinalSum = (specs) => (x, y) => {
|
|
21
|
+
for (let s of specs) {
|
|
22
|
+
const [a, b] = s.domain;
|
|
23
|
+
if (x >= a && x <= b && y >= a && y <= b) {
|
|
24
|
+
return a + (b - a) * s.tnorm(norm(x, a, b), norm(y, a, b));
|
|
111
25
|
}
|
|
112
|
-
|
|
26
|
+
}
|
|
27
|
+
return Math.min(x, y);
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
ordinalSum,
|
|
31
|
+
snormBoundedSum,
|
|
32
|
+
snormDrastic,
|
|
33
|
+
snormEinstein,
|
|
34
|
+
snormMax,
|
|
35
|
+
snormNilpotent,
|
|
36
|
+
snormProbabilistic,
|
|
37
|
+
tnormAczelAlsina,
|
|
38
|
+
tnormDombi,
|
|
39
|
+
tnormDrastic,
|
|
40
|
+
tnormHamacher,
|
|
41
|
+
tnormLukasiewicz,
|
|
42
|
+
tnormMin,
|
|
43
|
+
tnormNilpotent,
|
|
44
|
+
tnormProduct,
|
|
45
|
+
tnormYager
|
|
113
46
|
};
|
package/var.js
CHANGED
|
@@ -1,87 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* {@link defuzz}.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* // temperature sets (in celsius)
|
|
9
|
-
* const temp = variable([-20, 40], {
|
|
10
|
-
* freezing: invSigmoid(0, 2),
|
|
11
|
-
* cold: trapezoid(0, 4, 16, 20),
|
|
12
|
-
* warm: trapezoid(15, 20, 25, 30),
|
|
13
|
-
* hot: sigmoid(30, 2)
|
|
14
|
-
* });
|
|
15
|
-
* ```
|
|
16
|
-
*
|
|
17
|
-
* @param domain -
|
|
18
|
-
* @param terms -
|
|
19
|
-
*/
|
|
20
|
-
export const variable = (domain, terms) => ({
|
|
21
|
-
domain,
|
|
22
|
-
terms,
|
|
1
|
+
const variable = (domain, terms) => ({
|
|
2
|
+
domain,
|
|
3
|
+
terms
|
|
23
4
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* // temperature sets (in celsius)
|
|
33
|
-
* const temp = variable([-20, 40], {
|
|
34
|
-
* freezing: invSigmoid(0, 2),
|
|
35
|
-
* cold: trapezoid(0, 4, 16, 20),
|
|
36
|
-
* warm: trapezoid(15, 20, 25, 30),
|
|
37
|
-
* hot: sigmoid(30, 2)
|
|
38
|
-
* });
|
|
39
|
-
*
|
|
40
|
-
* classify(temp, 28)
|
|
41
|
-
* // "warm"
|
|
42
|
-
* ```
|
|
43
|
-
*
|
|
44
|
-
* @param var -
|
|
45
|
-
* @param x -
|
|
46
|
-
* @param threshold -
|
|
47
|
-
*/
|
|
48
|
-
export const classify = ({ terms }, x, threshold = 0.5) => {
|
|
49
|
-
let max = threshold;
|
|
50
|
-
let maxID;
|
|
51
|
-
for (let id in terms) {
|
|
52
|
-
const t = terms[id](x);
|
|
53
|
-
if (t >= max) {
|
|
54
|
-
max = t;
|
|
55
|
-
maxID = id;
|
|
56
|
-
}
|
|
5
|
+
const classify = ({ terms }, x, threshold = 0.5) => {
|
|
6
|
+
let max = threshold;
|
|
7
|
+
let maxID;
|
|
8
|
+
for (let id in terms) {
|
|
9
|
+
const t = terms[id](x);
|
|
10
|
+
if (t >= max) {
|
|
11
|
+
max = t;
|
|
12
|
+
maxID = id;
|
|
57
13
|
}
|
|
58
|
-
|
|
14
|
+
}
|
|
15
|
+
return maxID;
|
|
59
16
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* hot: sigmoid(30, 2)
|
|
72
|
-
* });
|
|
73
|
-
*
|
|
74
|
-
* evaluate(temp, 28)
|
|
75
|
-
* // { freezing: 0, cold: 0, warm: 0.4, hot: 0.01798620996209156 }
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @param var -
|
|
79
|
-
* @param x -
|
|
80
|
-
*/
|
|
81
|
-
export const evaluate = ({ terms }, x) => {
|
|
82
|
-
const res = {};
|
|
83
|
-
for (let id in terms) {
|
|
84
|
-
res[id] = terms[id](x);
|
|
85
|
-
}
|
|
86
|
-
return res;
|
|
17
|
+
const evaluate = ({ terms }, x) => {
|
|
18
|
+
const res = {};
|
|
19
|
+
for (let id in terms) {
|
|
20
|
+
res[id] = terms[id](x);
|
|
21
|
+
}
|
|
22
|
+
return res;
|
|
23
|
+
};
|
|
24
|
+
export {
|
|
25
|
+
classify,
|
|
26
|
+
evaluate,
|
|
27
|
+
variable
|
|
87
28
|
};
|