@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-03T12:13:31Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
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
- * Takes an object of input {@link variable}s, an object of output variable,
6
- * rule array and an object of input values. Evaluates relevant terms of input
7
- * variables in all matching rules, then combines and defuzzes them using given
8
- * optional strategy (by default {@link centroidStrategy} w/ its own default
9
- * options). Returns object of computed output variable values.
10
- *
11
- * @remarks
12
- * The `imply` T-norm (default: {@link tnormMin} is used to transform each
13
- * rule's output set(s) using each rule's computed/aggregated truth value, as
14
- * well as each rule's weight. Different T-norms might produce different fuzzy
15
- * set shapes and different results, even if the defuzz strategy remains
16
- * constant.
17
- *
18
- * The `combine` S-norm (default: {@link snormMax}) is used to combine the
19
- * relevant output sets of all rules for integration/analysis by the given
20
- * defuzz `strategy` actually producing the crisp result.
21
- *
22
- * @param ins -
23
- * @param outs -
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
- return terms;
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 res;
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.51",
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 clean && tsc --declaration",
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.10",
37
- "@thi.ng/math": "^5.7.5"
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
- "@thi.ng/testament": "^0.4.3",
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": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
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
- * Defines and returns a new rule object. Takes a T-norm (or S-norm) `op`, an
4
- * object of input conditions, an object of results and optional rule `weight`.
5
- * The `op` function is used to combine input terms.
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
- export const and = ($if, then, weight) => rule(tnormMin, $if, then, weight);
43
- export const strongAnd = ($if, then, weight) => rule(tnormProduct, $if, then, weight);
44
- export const or = ($if, then, weight) => rule(snormMax, $if, then, weight);
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
- * HOF {@link FuzzyFn} always yielding given `x` (should be in [0,1]
7
- * interval).
8
- *
9
- * @param x -
10
- */
11
- export const constant = (x) => () => x;
12
- /**
13
- * HOF {@link FuzzyFn} which takes a value `p` and tolerance `eps`, then yields
14
- * a discrete window function: `|p - x| <= eps ? 1 : 0`
15
- *
16
- * @param p -
17
- * @param eps -
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
- * Higher order function. Returns new function which selects subset of given
113
- * fuzzy set where `fn(x) < alpha`, or else returns 0.
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
- * Higher order function, complex shape generator. Takes a T-norm (or S-norm) as
124
- * reduction function `op` and any number of {@link FuzzyFn}s. Returns new
125
- * `FuzzyFn` which evaluates all given `fns` and combines/reduces their results
126
- * with `op`.
127
- *
128
- * @remarks
129
- * Depending on the use case and choice of `op`, the `initial` value should
130
- * either be set to:
131
- *
132
- * - T-norm like function: 1.0
133
- * - S-norm like function: 0.0
134
- *
135
- * References:
136
- * - https://www.desmos.com/calculator/pnq6kqzfb5 (interactive graph)
137
- * - https://en.wikipedia.org/wiki/T-norm
138
- * - https://github.com/thi-ng/umbrella/blob/develop/packages/fuzzy/src/tnorms.ts
139
- *
140
- * @example
141
- * ```ts
142
- * const f = compose(tnormMin, 1, triangle(0,2,4), triangle(1,3,5));
143
- * f(1); // 0
144
- * f(2); // 0.5
145
- * f(3); // 0.5
146
- * f(4); // 0
147
- * ```
148
- *
149
- * @example
150
- * ```ts
151
- * // M-like shape w/ peaks at 3 & 5
152
- * const M = compose(
153
- * Math.max,
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);
@@ -1,52 +1,31 @@
1
1
  import { fit } from "@thi.ng/math/fit";
2
2
  import { defaultOpts } from "./opts.js";
3
- /**
4
- * Higher-order function: Bisector-of-Area defuzzification strategy. Returns
5
- * strategy which computes the integral of a given fuzzy set in the defined
6
- * `[min,max]` domain and returns the position of the bisector which partitions
7
- * the area into 2 equal halves. The domain is sampled at `opts.samples`
8
- * uniformly spaced points.
9
- *
10
- * @remarks
11
- * Use `samples` option to adjust precision.
12
- *
13
- * @see {@Link DefuzzStrategyOpts}
14
- *
15
- * @example
16
- * ```ts
17
- * bisectorStrategy()(trapezoid(0,1,5,6), [0,6])
18
- * // 2.97
19
- *
20
- * // ......▁█████████████|█████████████▁.....
21
- * // ......██████████████|██████████████.....
22
- * // .....███████████████|███████████████....
23
- * // ....▇███████████████|███████████████▇...
24
- * // ...▅████████████████|████████████████▅..
25
- * // ..▃█████████████████|█████████████████▃.
26
- * // .▁██████████████████|██████████████████▁
27
- * // .███████████████████|███████████████████
28
- * // ^ 2.97
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
  };
@@ -1,46 +1,19 @@
1
1
  import { defaultOpts } from "./opts.js";
2
- /**
3
- * Higher-order function: Centre-of-gravity defuzzification strategy, yielding
4
- * the approx. center of gravity position of a given fuzzy set. The domain is
5
- * sampled at `opts.samples` uniformly spaced points.
6
- *
7
- * @remarks
8
- * This is the default strategy used by {@link defuzz}. Produces similar results
9
- * to {@link bisectorStrategy}, but with lower computational cost. Use `samples`
10
- * option to adjust precision.
11
- *
12
- * @see {@Link DefuzzStrategyOpts}
13
- *
14
- * @example
15
- * ```ts
16
- * centroidStrategy()(trapezoid(0,1,5,6), [0,6])
17
- * // 3.0000000000000004
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
  };
@@ -1,129 +1,50 @@
1
1
  import { eqDelta } from "@thi.ng/math/eqdelta";
2
2
  import { defaultOpts } from "./opts.js";
3
- /**
4
- * Higher-order function. Returns Mean-of-Maxima defuzzification strategy,
5
- * yielding the approx. mean of the maximum region of a given fuzzy set.
6
- *
7
- * @remarks
8
- * Use `samples` option to adjust precision.
9
- *
10
- * @see {@Link DefuzzStrategyOpts}
11
- *
12
- * @example
13
- * ```ts
14
- * meanOfMaximaStrategy()(trapezoid(0,1,5,6), [0,6])
15
- * // 3
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
- * Higher-order function. Returns First-of-Maxima defuzzification strategy,
55
- * yielding the approx. start position of the maximum region of a given fuzzy
56
- * set.
57
- *
58
- * @remarks
59
- * Use `samples` option to adjust precision.
60
- *
61
- * @see {@Link DefuzzStrategyOpts}
62
- *
63
- * @example
64
- * ```ts
65
- * firstOfMaximaStrategy()(trapezoid(0,1,5,6), [0,6])
66
- * // 1.02
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
- * Higher-order function. Returns Last-of-Maxima defuzzification strategy,
100
- * yielding the approx. final position of the maximum region of a given fuzzy
101
- * set.
102
- *
103
- * @remarks
104
- * Use `samples` option to adjust precision.
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
  };
@@ -1,5 +1,8 @@
1
- export const defaultOpts = (opts) => ({
2
- samples: 100,
3
- eps: 1e-6,
4
- ...opts,
1
+ const defaultOpts = (opts) => ({
2
+ samples: 100,
3
+ eps: 1e-6,
4
+ ...opts
5
5
  });
6
+ export {
7
+ defaultOpts
8
+ };
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
- // https://en.wikipedia.org/wiki/T-norm
4
- export const tnormMin = (x, y) => Math.min(x, y);
5
- export const tnormProduct = (x, y) => x * y;
6
- export const tnormLukasiewicz = (x, y) => clamp0(x + y - 1);
7
- export const tnormDrastic = (x, y) => (x === 1 ? y : y === 1 ? x : 0);
8
- export const tnormNilpotent = (x, y) => (x + y > 1 ? Math.min(x, y) : 0);
9
- /**
10
- * HOF T-norm. Parametric Hamacher with `p` controlling curvature.
11
- *
12
- * @remarks
13
- * Interactive graph: https://www.desmos.com/calculator/4bneccqs3p
14
- *
15
- * @param p - curve param [0..∞], default: 2
16
- */
17
- export const tnormHamacher = (p = 2) => (x, y) => x === 0 && y === 0 ? 0 : (x * y) / (p + (1 - p) * (x + y - x * y));
18
- /**
19
- * HOF T-norm. Parametric Yager with `p` controlling curvature.
20
- *
21
- * @remarks
22
- * Interactive graph: https://www.desmos.com/calculator/4bneccqs3p
23
- *
24
- * @param p - curve param [0..∞], default: 2
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
- return Math.min(x, y);
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
- * Takes a `domain` interval and on object of named fuzzy sets and returns a new
3
- * Linguistic variable, which can then be used as input or output var for
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
- * Takes an LVar and a domain value `x`. Returns the ID of the var's fuzzy set
26
- * term which yields the max truth value for given `x`. If `threshold` is
27
- * enabled (default: 0.5), any truth value MUST also be > `threshold` to be
28
- * considered. The function returns undefined if classification failed.
29
- *
30
- * @example
31
- * ```ts
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
- return maxID;
14
+ }
15
+ return maxID;
59
16
  };
60
- /**
61
- * Takes an LVar and a domain value `x`, evaluates all of var's terms for given
62
- * `x` and returns object of results.
63
- *
64
- * @example
65
- * ```ts
66
- * // temperature sets (in celsius)
67
- * const temp = variable([-20, 40], {
68
- * freezing: invSigmoid(0, 2),
69
- * cold: trapezoid(0, 4, 16, 20),
70
- * warm: trapezoid(15, 20, 25, 30),
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
  };