fuzzytool 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/CHANGELOG.md +14 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/CITATION.cff +1 -1
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/PKG-INFO +8 -1
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/README.md +7 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/ROADMAP.md +14 -4
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/api/python.md +4 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/type2.md +46 -0
- fuzzytool-0.5.0/docs/images/gt2_zslices.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/__init__.py +9 -1
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/type2/__init__.py +10 -0
- fuzzytool-0.5.0/fuzzytool/type2/general.py +211 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/pyproject.toml +1 -1
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_type2.py +56 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/.github/workflows/ci.yml +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/.github/workflows/docs.yml +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/.github/workflows/release-pypi.yml +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/.gitignore +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/.zenodo.json +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/CLAUDE.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/CONTRIBUTING.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/LICENSE +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/citing.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/extending.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/getting-started.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/anfis.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/batch-and-io.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/clustering.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/defuzzification.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/fraud-alert.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/ftransform.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/mamdani.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/mcdm.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/membership.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/rule-learning.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/rules.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/tsk.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/guide/visualization.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/clusters.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/credit_control_surface.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/fraud_anomaly_terms.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/fraud_control_surface.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/gs_score_terms.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/it2_score.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/premium_terms.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/tut_advisor_surface.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/tut_advisor_volatility.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/tut_anfis.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/images/tut_clusters.png +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/index.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/installation.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/integrations.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/javascripts/mathjax.js +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/tutorials/anfis-learning.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/tutorials/clustering-segmentation.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/tutorials/risk-advisor.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/docs/tutorials/topsis-decision.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/anfis.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/clustering.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/credit_risk.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/credit_risk_it2.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/fraud_alert.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/ftransform.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/examples/mcdm.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/anfis.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/cluster.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/datasets.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/defuzz.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/ftransform.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/fuzzynum.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/inference/__init__.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/inference/mamdani.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/inference/tsk.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/inference/tsukamoto.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/__init__.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/_util.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/agents.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/optuna.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/pandas.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/parallel.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/scipy.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/sklearn.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/torch.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/integrations/turboswarm.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/learn.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/mcdm.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/membership.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/norms.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/rules.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/serialize.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/sets.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/type2/inference.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/type2/reduction.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/type2/sets.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/fuzzytool/viz.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/mkdocs.yml +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/notebooks/01_quickstart.ipynb +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/notebooks/02_type2.ipynb +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/notebooks/03_clustering.ipynb +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/notebooks/04_learning.ipynb +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/notebooks/README.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/paper.bib +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/paper.md +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_cluster.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_engineering.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_inference.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_integrations.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_learn.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_learning.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_mcdm.py +0 -0
- {fuzzytool-0.4.0 → fuzzytool-0.5.0}/tests/test_membership.py +0 -0
|
@@ -6,6 +6,20 @@ All notable changes to this project are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-06-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **General type-2 (GT2) fuzzy sets** via the zSlices / alpha-plane
|
|
14
|
+
representation (`fuzzytool.type2.general`):
|
|
15
|
+
- `GeneralType2MF` — a GT2 set as a stack of IT2 z-slices; also exposes the
|
|
16
|
+
overall FOU (`lower`/`upper`), so it can stand in for its IT2 footprint.
|
|
17
|
+
- Constructors `gt2_from_it2`, `gt2_gauss_uncertain_mean`, `gt2_scale`
|
|
18
|
+
(triangular secondary membership peaking at the principal MF).
|
|
19
|
+
- `GeneralType2Mamdani` — inference as a z-weighted stack of IT2 Mamdani
|
|
20
|
+
runs, reusing the Karnik-Mendel machinery.
|
|
21
|
+
- `centroid_gt2` — zSlices type reduction of a single GT2 set.
|
|
22
|
+
|
|
9
23
|
## [0.4.0] - 2026-06-28
|
|
10
24
|
|
|
11
25
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fuzzytool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: A clean, extensible fuzzy-logic toolkit in pure Python + NumPy
|
|
5
5
|
Project-URL: Homepage, https://fuzzytool.github.io
|
|
6
6
|
Project-URL: Documentation, https://fuzzytool.github.io
|
|
@@ -118,6 +118,13 @@ s-norm, `~` the complement.
|
|
|
118
118
|
| 6 | Notebooks, JOSS `paper.md`, Zenodo DOI, PyPI release | ✅ |
|
|
119
119
|
| 7 | **Ecosystem integrations**: pandas, scikit-learn, PyTorch, SciPy, turboswarm, Optuna, Joblib/Dask, LLM agents | ✅ |
|
|
120
120
|
|
|
121
|
+
### v0.5.0
|
|
122
|
+
|
|
123
|
+
- **General type-2 (GT2) fuzzy sets** via the zSlices / alpha-plane
|
|
124
|
+
representation: `GeneralType2MF`, the `gt2_*` constructors,
|
|
125
|
+
`GeneralType2Mamdani` inference and `centroid_gt2` type reduction — reusing
|
|
126
|
+
the existing interval type-2 / Karnik-Mendel machinery.
|
|
127
|
+
|
|
121
128
|
### v0.4.0
|
|
122
129
|
|
|
123
130
|
- **turboswarm integration:** gradient-free, global tuning of a fuzzy system's
|
|
@@ -57,6 +57,13 @@ s-norm, `~` the complement.
|
|
|
57
57
|
| 6 | Notebooks, JOSS `paper.md`, Zenodo DOI, PyPI release | ✅ |
|
|
58
58
|
| 7 | **Ecosystem integrations**: pandas, scikit-learn, PyTorch, SciPy, turboswarm, Optuna, Joblib/Dask, LLM agents | ✅ |
|
|
59
59
|
|
|
60
|
+
### v0.5.0
|
|
61
|
+
|
|
62
|
+
- **General type-2 (GT2) fuzzy sets** via the zSlices / alpha-plane
|
|
63
|
+
representation: `GeneralType2MF`, the `gt2_*` constructors,
|
|
64
|
+
`GeneralType2Mamdani` inference and `centroid_gt2` type reduction — reusing
|
|
65
|
+
the existing interval type-2 / Karnik-Mendel machinery.
|
|
66
|
+
|
|
60
67
|
### v0.4.0
|
|
61
68
|
|
|
62
69
|
- **turboswarm integration:** gradient-free, global tuning of a fuzzy system's
|
|
@@ -72,16 +72,26 @@ Each pulls its dependency in only on import, behind its own extra.
|
|
|
72
72
|
- [x] **Agents (LangChain/LangGraph)** (`[agents]`): `explain` plus an
|
|
73
73
|
`inference_tool` LangChain tool that reports which rules fired.
|
|
74
74
|
|
|
75
|
+
## ✅ General type-2 (zSlices)
|
|
76
|
+
|
|
77
|
+
- [x] `GeneralType2MF` via the zSlices / alpha-plane representation (a stack of
|
|
78
|
+
IT2 slices; also exposes the overall FOU, so it degrades to IT2).
|
|
79
|
+
- [x] Constructors `gt2_from_it2`, `gt2_gauss_uncertain_mean`, `gt2_scale`
|
|
80
|
+
(triangular secondary MF).
|
|
81
|
+
- [x] `GeneralType2Mamdani` (z-weighted stack of IT2 Mamdani inferences) and
|
|
82
|
+
`centroid_gt2` zSlices type reduction.
|
|
83
|
+
|
|
75
84
|
## Ideas for later
|
|
76
85
|
|
|
77
86
|
- Fuzzy cognitive maps (+ grey FCM).
|
|
78
|
-
-
|
|
87
|
+
- More defuzzifiers; non-triangular GT2 secondary MFs.
|
|
79
88
|
|
|
80
89
|
## ⏳ Phase 6 — Release (v0.1.0)
|
|
81
90
|
|
|
82
91
|
- [x] Example notebooks (`notebooks/`) and a comparison page vs scikit-fuzzy.
|
|
83
92
|
- [x] JOSS `paper.md`; Zenodo metadata (`.zenodo.json`) and a citing/releasing page.
|
|
84
93
|
- [x] GitHub Pages live (`https://fuzzytool.github.io`).
|
|
85
|
-
- [
|
|
86
|
-
|
|
87
|
-
- [
|
|
94
|
+
- [x] PyPI trusted publisher registered; tagging a version runs `release-pypi.yml`
|
|
95
|
+
(published 0.1.0 → 0.4.0 via OIDC, no stored token).
|
|
96
|
+
- [x] Zenodo GitHub integration; each GitHub release is archived with a DOI
|
|
97
|
+
(`10.5281/zenodo.20836712`).
|
|
@@ -82,3 +82,49 @@ plt.show()
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|

|
|
85
|
+
|
|
86
|
+
## General type-2 (zSlices)
|
|
87
|
+
|
|
88
|
+
An IT2 set treats every point inside its FOU as equally possible. A **general
|
|
89
|
+
type-2** set adds a *secondary* membership that weights those possibilities —
|
|
90
|
+
the third dimension IT2 discards. fuzzytool represents a GT2 set with the
|
|
91
|
+
**zSlices / alpha-plane** decomposition: slicing the secondary domain at levels
|
|
92
|
+
`z` turns the GT2 set into a stack of ordinary IT2 sets, so inference and type
|
|
93
|
+
reduction reuse the IT2 machinery and combine the slices weighted by `z`.
|
|
94
|
+
|
|
95
|
+
Each constructor builds a GT2 set from an IT2 footprint plus a **triangular
|
|
96
|
+
secondary** peaking at the principal (mid) MF — narrow at `z = 1`, the full FOU
|
|
97
|
+
at `z → 0`:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import fuzzytool as fz
|
|
101
|
+
|
|
102
|
+
score = fz.Variable("score", (300, 850))
|
|
103
|
+
score["good"] = fz.gt2_gauss_uncertain_mean(740, 780, 50, n_slices=5)
|
|
104
|
+
score["poor"] = fz.gt2_gauss_uncertain_mean(420, 480, 70, n_slices=5)
|
|
105
|
+
premium = fz.Variable("premium", (0, 12))
|
|
106
|
+
premium["low"] = fz.gt2_gauss_uncertain_mean(1.5, 2.5, 1.5)
|
|
107
|
+
premium["high"] = fz.gt2_gauss_uncertain_mean(9.5, 10.5, 1.5)
|
|
108
|
+
|
|
109
|
+
sys = fz.GeneralType2Mamdani()
|
|
110
|
+
sys.rule(score["good"], premium["low"])
|
|
111
|
+
sys.rule(score["poor"], premium["high"])
|
|
112
|
+
sys(score=780) # -> 2.31 (z-weighted average of per-slice IT2 results)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
A GT2 term also exposes `lower`/`upper` (its overall FOU), so it can stand in for
|
|
118
|
+
its IT2 footprint inside an `IT2Mamdani` if you want the cheaper approximation.
|
|
119
|
+
Build a GT2 set from *any* IT2 footprint with
|
|
120
|
+
[`gt2_from_it2`][fuzzytool.type2.general.gt2_from_it2], and type-reduce a single
|
|
121
|
+
GT2 set with `centroid_gt2`:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import numpy as np
|
|
125
|
+
from fuzzytool.type2 import centroid_gt2
|
|
126
|
+
|
|
127
|
+
good = fz.gt2_gauss_uncertain_mean(740, 780, 50, n_slices=6)
|
|
128
|
+
universe = np.linspace(300, 850, 400)
|
|
129
|
+
centroid_gt2(good, universe) # -> ~754.7 (z-weighted centroid)
|
|
130
|
+
```
|
|
Binary file
|
|
@@ -29,14 +29,19 @@ from .serialize import load, save
|
|
|
29
29
|
from .sets import Variable
|
|
30
30
|
from .type2 import (
|
|
31
31
|
IT2TSK,
|
|
32
|
+
GeneralType2Mamdani,
|
|
33
|
+
GeneralType2MF,
|
|
32
34
|
IT2Mamdani,
|
|
35
|
+
gt2_from_it2,
|
|
36
|
+
gt2_gauss_uncertain_mean,
|
|
37
|
+
gt2_scale,
|
|
33
38
|
it2,
|
|
34
39
|
it2_gauss_uncertain_mean,
|
|
35
40
|
it2_gauss_uncertain_std,
|
|
36
41
|
it2_scale,
|
|
37
42
|
)
|
|
38
43
|
|
|
39
|
-
__version__ = "0.
|
|
44
|
+
__version__ = "0.5.0"
|
|
40
45
|
|
|
41
46
|
__all__ = [
|
|
42
47
|
"__version__",
|
|
@@ -51,6 +56,9 @@ __all__ = [
|
|
|
51
56
|
# interval type-2
|
|
52
57
|
"IT2Mamdani", "IT2TSK",
|
|
53
58
|
"it2", "it2_scale", "it2_gauss_uncertain_mean", "it2_gauss_uncertain_std",
|
|
59
|
+
# general type-2
|
|
60
|
+
"GeneralType2Mamdani", "GeneralType2MF",
|
|
61
|
+
"gt2_from_it2", "gt2_gauss_uncertain_mean", "gt2_scale",
|
|
54
62
|
# fuzzy clustering
|
|
55
63
|
"fuzzy_cmeans", "gustafson_kessel", "possibilistic_cmeans",
|
|
56
64
|
# learning & approximation
|
|
@@ -9,6 +9,14 @@ IT2 rules reuse the very same operator syntax as type-1 rules (``|``, ``&``,
|
|
|
9
9
|
tree and collapse the result with Karnik-Mendel type reduction.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
from .general import (
|
|
13
|
+
GeneralType2Mamdani,
|
|
14
|
+
GeneralType2MF,
|
|
15
|
+
centroid_gt2,
|
|
16
|
+
gt2_from_it2,
|
|
17
|
+
gt2_gauss_uncertain_mean,
|
|
18
|
+
gt2_scale,
|
|
19
|
+
)
|
|
12
20
|
from .inference import IT2TSK, IT2Mamdani
|
|
13
21
|
from .reduction import centroid_it2, karnik_mendel, km_endpoint
|
|
14
22
|
from .sets import (
|
|
@@ -24,4 +32,6 @@ __all__ = [
|
|
|
24
32
|
"it2", "it2_scale", "it2_gauss_uncertain_mean", "it2_gauss_uncertain_std",
|
|
25
33
|
"IT2Mamdani", "IT2TSK",
|
|
26
34
|
"karnik_mendel", "km_endpoint", "centroid_it2",
|
|
35
|
+
"GeneralType2MF", "GeneralType2Mamdani", "centroid_gt2",
|
|
36
|
+
"gt2_from_it2", "gt2_gauss_uncertain_mean", "gt2_scale",
|
|
27
37
|
]
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""General type-2 (GT2) fuzzy sets via the zSlices / alpha-plane representation.
|
|
2
|
+
|
|
3
|
+
An interval type-2 set treats every point inside its footprint of uncertainty
|
|
4
|
+
(FOU) as equally possible. A **general** type-2 set adds a *secondary* membership
|
|
5
|
+
that weights those possibilities — the third dimension IT2 throws away.
|
|
6
|
+
|
|
7
|
+
A practical, exact way to handle that third dimension is the **zSlices** (alpha
|
|
8
|
+
-plane) decomposition (Wagner & Hagras): slice the secondary domain at levels
|
|
9
|
+
``z`` and, at each level, the GT2 set reduces to an ordinary IT2 set whose FOU is
|
|
10
|
+
the ``z``-cut. Inference and type reduction then run the existing IT2 machinery
|
|
11
|
+
on each slice and combine the results, weighted by ``z``.
|
|
12
|
+
|
|
13
|
+
Here a GT2 set is built from an IT2 footprint plus a **triangular secondary**
|
|
14
|
+
peaking at the FOU's principal (mid) MF: at ``z -> 0`` a slice is the full FOU,
|
|
15
|
+
at ``z = 1`` it collapses to the principal MF.
|
|
16
|
+
|
|
17
|
+
* :class:`GeneralType2MF` — a stack of IT2 z-slices (also exposes the overall
|
|
18
|
+
FOU via ``lower``/``upper``, so it degrades gracefully to IT2).
|
|
19
|
+
* :func:`gt2_from_it2`, :func:`gt2_gauss_uncertain_mean`, :func:`gt2_scale` —
|
|
20
|
+
constructors.
|
|
21
|
+
* :class:`GeneralType2Mamdani` — z-weighted stack of IT2 Mamdani inferences.
|
|
22
|
+
* :func:`centroid_gt2` — zSlices centroid of a single GT2 set.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
from ..sets import Variable
|
|
30
|
+
from .inference import IT2Mamdani
|
|
31
|
+
from .reduction import centroid_it2
|
|
32
|
+
from .sets import IntervalType2MF, it2
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GeneralType2MF:
|
|
36
|
+
"""A general type-2 set represented as a stack of IT2 ``z``-slices.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
slices: IT2 sets, one per ``z`` level, narrowing toward the principal MF
|
|
40
|
+
as ``z`` increases.
|
|
41
|
+
zlevels: the secondary levels (in ``(0, 1]``), ascending, aligned with
|
|
42
|
+
``slices``.
|
|
43
|
+
footprint: the overall FOU as an IT2 set (used for ``lower``/``upper`` so
|
|
44
|
+
the GT2 set can stand in for its IT2 footprint).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, slices: list[IntervalType2MF], zlevels: np.ndarray,
|
|
48
|
+
footprint: IntervalType2MF) -> None:
|
|
49
|
+
self.slices = list(slices)
|
|
50
|
+
self.zlevels = np.asarray(zlevels, dtype=float)
|
|
51
|
+
self._footprint = footprint
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def n_slices(self) -> int:
|
|
55
|
+
return len(self.slices)
|
|
56
|
+
|
|
57
|
+
def slice(self, i: int) -> IntervalType2MF:
|
|
58
|
+
"""The IT2 set at ``z``-level index ``i``."""
|
|
59
|
+
return self.slices[i]
|
|
60
|
+
|
|
61
|
+
def lower(self, x):
|
|
62
|
+
return self._footprint.lower(x)
|
|
63
|
+
|
|
64
|
+
def upper(self, x):
|
|
65
|
+
return self._footprint.upper(x)
|
|
66
|
+
|
|
67
|
+
def __call__(self, x):
|
|
68
|
+
return self._footprint(x)
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
return f"GeneralType2MF(n_slices={self.n_slices})"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def gt2_from_it2(footprint: IntervalType2MF, n_slices: int = 5) -> GeneralType2MF:
|
|
75
|
+
"""Build a GT2 set from an IT2 FOU with a triangular secondary MF.
|
|
76
|
+
|
|
77
|
+
The secondary membership peaks at the principal MF (the midline of the FOU)
|
|
78
|
+
and falls linearly to 0 at the LMF/UMF. The ``z``-slice at level ``z`` is the
|
|
79
|
+
IT2 set whose FOU is narrowed from the full footprint toward that midline by
|
|
80
|
+
a factor ``(1 - z)``.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
footprint: the IT2 set describing the full footprint of uncertainty.
|
|
84
|
+
n_slices: number of ``z`` levels (``z = 1/n .. 1``). More slices = a finer
|
|
85
|
+
secondary, at proportional cost.
|
|
86
|
+
"""
|
|
87
|
+
if n_slices < 1:
|
|
88
|
+
raise ValueError("n_slices must be >= 1")
|
|
89
|
+
zlevels = np.linspace(1.0 / n_slices, 1.0, n_slices)
|
|
90
|
+
|
|
91
|
+
def mid(x):
|
|
92
|
+
return (footprint.lower(x) + footprint.upper(x)) / 2.0
|
|
93
|
+
|
|
94
|
+
slices = []
|
|
95
|
+
for z in zlevels:
|
|
96
|
+
# Default-arg binding captures the current z in each closure.
|
|
97
|
+
def lower(x, z=z):
|
|
98
|
+
m = mid(x)
|
|
99
|
+
return m - (1.0 - z) * (m - footprint.lower(x))
|
|
100
|
+
|
|
101
|
+
def upper(x, z=z):
|
|
102
|
+
m = mid(x)
|
|
103
|
+
return m + (1.0 - z) * (footprint.upper(x) - m)
|
|
104
|
+
|
|
105
|
+
slices.append(it2(lower, upper))
|
|
106
|
+
return GeneralType2MF(slices, zlevels, footprint)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def gt2_gauss_uncertain_mean(c1: float, c2: float, sigma: float,
|
|
110
|
+
n_slices: int = 5) -> GeneralType2MF:
|
|
111
|
+
"""GT2 Gaussian with an uncertain mean in ``[c1, c2]`` (triangular secondary)."""
|
|
112
|
+
from .sets import it2_gauss_uncertain_mean
|
|
113
|
+
return gt2_from_it2(it2_gauss_uncertain_mean(c1, c2, sigma), n_slices)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def gt2_scale(mf, scale: float, n_slices: int = 5) -> GeneralType2MF:
|
|
117
|
+
"""GT2 set from a height-uncertainty FOU (UMF ``mf``, LMF ``scale * mf``)."""
|
|
118
|
+
from .sets import it2_scale
|
|
119
|
+
return gt2_from_it2(it2_scale(mf, scale), n_slices)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def centroid_gt2(gt2mf: GeneralType2MF, universe) -> float:
|
|
123
|
+
"""zSlices centroid of a GT2 set: ``z``-weighted mean of per-slice IT2 centroids."""
|
|
124
|
+
z = gt2mf.zlevels
|
|
125
|
+
mids = np.array([sum(centroid_it2(gt2mf.slice(i), universe)) / 2.0
|
|
126
|
+
for i in range(gt2mf.n_slices)])
|
|
127
|
+
return float(np.sum(z * mids) / np.sum(z))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _antecedent_variables(node, out: dict) -> None:
|
|
131
|
+
if hasattr(node, "variable"):
|
|
132
|
+
out.setdefault(id(node.variable), node.variable)
|
|
133
|
+
if hasattr(node, "left"):
|
|
134
|
+
_antecedent_variables(node.left, out)
|
|
135
|
+
_antecedent_variables(node.right, out)
|
|
136
|
+
if hasattr(node, "operand"):
|
|
137
|
+
_antecedent_variables(node.operand, out)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class GeneralType2Mamdani:
|
|
141
|
+
"""General type-2 Mamdani inference via the zSlices decomposition.
|
|
142
|
+
|
|
143
|
+
At each ``z`` level the GT2 terms collapse to their IT2 ``z``-slice and the
|
|
144
|
+
existing :class:`~fuzzytool.type2.inference.IT2Mamdani` engine
|
|
145
|
+
(center-of-sets type reduction) produces a crisp output ``y(z)``. The final
|
|
146
|
+
result is the ``z``-weighted average ``Σ z·y(z) / Σ z`` — the centroid of the
|
|
147
|
+
type-reduced general type-2 output.
|
|
148
|
+
|
|
149
|
+
Variables may mix GT2 and IT2/type-1 terms; only the GT2 terms are sliced.
|
|
150
|
+
All GT2 terms must share the same ``z`` levels (same ``n_slices``).
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def __init__(self, tnorm="min", snorm="max") -> None:
|
|
154
|
+
self._it2 = IT2Mamdani(tnorm, snorm)
|
|
155
|
+
|
|
156
|
+
def rule(self, antecedent, consequent, weight: float = 1.0) -> GeneralType2Mamdani:
|
|
157
|
+
"""Add ``IF antecedent THEN output is term`` and return ``self``."""
|
|
158
|
+
self._it2.rule(antecedent, consequent, weight)
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def rules(self):
|
|
163
|
+
return self._it2.rules
|
|
164
|
+
|
|
165
|
+
def _gt2_variables(self) -> list[Variable]:
|
|
166
|
+
found: dict[int, Variable] = {}
|
|
167
|
+
for r in self._it2.rules:
|
|
168
|
+
_antecedent_variables(r.antecedent, found)
|
|
169
|
+
found.setdefault(id(r.consequent.variable), r.consequent.variable)
|
|
170
|
+
return [v for v in found.values()
|
|
171
|
+
if any(isinstance(t, GeneralType2MF) for t in v.terms.values())]
|
|
172
|
+
|
|
173
|
+
def __call__(self, **inputs: float):
|
|
174
|
+
"""Run inference. Returns a float for one output, else a dict by name."""
|
|
175
|
+
if not self._it2.rules:
|
|
176
|
+
raise RuntimeError("no rules defined")
|
|
177
|
+
variables = self._gt2_variables()
|
|
178
|
+
if not variables:
|
|
179
|
+
raise RuntimeError("no general type-2 terms found; use IT2Mamdani instead")
|
|
180
|
+
zlevels = next(t.zlevels for v in variables for t in v.terms.values()
|
|
181
|
+
if isinstance(t, GeneralType2MF))
|
|
182
|
+
|
|
183
|
+
z_acc, y_acc = [], []
|
|
184
|
+
for i, z in enumerate(zlevels):
|
|
185
|
+
saved = {id(v): v.terms for v in variables}
|
|
186
|
+
try:
|
|
187
|
+
for v in variables:
|
|
188
|
+
v.terms = {name: (t.slice(i) if isinstance(t, GeneralType2MF) else t)
|
|
189
|
+
for name, t in v.terms.items()}
|
|
190
|
+
self._it2._centroids.clear()
|
|
191
|
+
y = self._it2(**inputs)
|
|
192
|
+
finally:
|
|
193
|
+
for v in variables:
|
|
194
|
+
v.terms = saved[id(v)]
|
|
195
|
+
z_acc.append(float(z))
|
|
196
|
+
y_acc.append(y)
|
|
197
|
+
|
|
198
|
+
z = np.asarray(z_acc)
|
|
199
|
+
if isinstance(y_acc[0], dict):
|
|
200
|
+
return {k: float(np.sum(z * np.array([y[k] for y in y_acc])) / np.sum(z))
|
|
201
|
+
for k in y_acc[0]}
|
|
202
|
+
return float(np.sum(z * np.asarray(y_acc)) / np.sum(z))
|
|
203
|
+
|
|
204
|
+
def __repr__(self) -> str:
|
|
205
|
+
return f"GeneralType2Mamdani(rules={len(self._it2.rules)})"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
__all__ = [
|
|
209
|
+
"GeneralType2MF", "gt2_from_it2", "gt2_gauss_uncertain_mean", "gt2_scale",
|
|
210
|
+
"centroid_gt2", "GeneralType2Mamdani",
|
|
211
|
+
]
|
|
@@ -75,3 +75,59 @@ def test_type1_and_it2_terms_mix_in_one_antecedent():
|
|
|
75
75
|
lo, hi = (a["hi"] & b["hi"]).eval_interval({"a": 0.8, "b": 1.0},
|
|
76
76
|
fz.norms.t_min, fz.norms.s_max)
|
|
77
77
|
assert lo <= hi
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# --- general type-2 (zSlices) -----------------------------------------------
|
|
81
|
+
|
|
82
|
+
def test_gt2_exposes_footprint_and_slices():
|
|
83
|
+
g = fz.gt2_gauss_uncertain_mean(740, 780, 50, n_slices=5)
|
|
84
|
+
x = np.linspace(300, 850, 200)
|
|
85
|
+
# degrades to its IT2 footprint via lower/upper
|
|
86
|
+
assert np.all(g.lower(x) <= g.upper(x) + 1e-12)
|
|
87
|
+
assert g.n_slices == 5 and len(g.zlevels) == 5
|
|
88
|
+
# the z=1 slice (last) collapses toward the principal MF: near-zero FOU
|
|
89
|
+
top = g.slice(g.n_slices - 1)
|
|
90
|
+
assert np.max(top.upper(x) - top.lower(x)) < 1e-9
|
|
91
|
+
# the lowest slice spans (almost) the full footprint
|
|
92
|
+
bottom = g.slice(0)
|
|
93
|
+
assert np.max(bottom.upper(x) - bottom.lower(x)) > 0.0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_gt2_mamdani_runs_and_is_bounded():
|
|
97
|
+
score = fz.Variable("score", (300, 850))
|
|
98
|
+
score["good"] = fz.gt2_gauss_uncertain_mean(740, 780, 50)
|
|
99
|
+
score["poor"] = fz.gt2_gauss_uncertain_mean(420, 480, 70)
|
|
100
|
+
premium = fz.Variable("premium", (0, 12))
|
|
101
|
+
premium["low"] = fz.gt2_gauss_uncertain_mean(1.5, 2.5, 1.5)
|
|
102
|
+
premium["high"] = fz.gt2_gauss_uncertain_mean(9.5, 10.5, 1.5)
|
|
103
|
+
|
|
104
|
+
sys = fz.GeneralType2Mamdani()
|
|
105
|
+
sys.rule(score["good"], premium["low"])
|
|
106
|
+
sys.rule(score["poor"], premium["high"])
|
|
107
|
+
# a good score gives a low premium, a poor score a high one
|
|
108
|
+
assert sys(score=780) < sys(score=450)
|
|
109
|
+
assert 0.0 <= sys(score=780) <= 12.0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_gt2_does_not_mutate_terms_after_call():
|
|
113
|
+
score = fz.Variable("score", (300, 850))
|
|
114
|
+
score["good"] = fz.gt2_gauss_uncertain_mean(740, 780, 50)
|
|
115
|
+
premium = fz.Variable("premium", (0, 12))
|
|
116
|
+
premium["low"] = fz.gt2_gauss_uncertain_mean(1.5, 2.5, 1.5)
|
|
117
|
+
sys = fz.GeneralType2Mamdani()
|
|
118
|
+
sys.rule(score["good"], premium["low"])
|
|
119
|
+
sys(score=760)
|
|
120
|
+
# terms are restored to the original GT2 objects (not left as a slice)
|
|
121
|
+
from fuzzytool.type2 import GeneralType2MF
|
|
122
|
+
assert isinstance(score.terms["good"], GeneralType2MF)
|
|
123
|
+
assert isinstance(premium.terms["low"], GeneralType2MF)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_centroid_gt2_matches_it2_order():
|
|
127
|
+
from fuzzytool.type2 import centroid_gt2
|
|
128
|
+
g = fz.gt2_gauss_uncertain_mean(740, 780, 50, n_slices=6)
|
|
129
|
+
uni = np.linspace(300, 850, 400)
|
|
130
|
+
c_gt2 = centroid_gt2(g, uni)
|
|
131
|
+
c_it2 = sum(centroid_it2(g, uni)) / 2.0
|
|
132
|
+
assert 700 < c_gt2 < 820
|
|
133
|
+
assert abs(c_gt2 - c_it2) < 20 # close, but GT2 weights the principal MF
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|