Riverscapes / gcd

Geomorphic Change Detection For Windows
http://gcd.riverscapes.xyz
GNU General Public License v3.0
25 stars 5 forks source link

FIS Files - Matlab vs. SciKit #174

Open joewheaton opened 6 years ago

joewheaton commented 6 years ago

Background: Way back when GCD was DoD and the whole thing was based off of Matlab, I used the fuzzy logic toolbox, which allows you to specify your fuzzy inference system based on a text *.fis file. The format of these is really simple, and we decided to use it in our own C++ (GCD 5 -> GCD 6), as well as our C# implementation of FIS. The advantage is that for Matlab users that have fuzzy logic toolbox, the *.fis file we use in GCD will also work in the toolbox.

However, in the decade that has followed, there are increasingly less Matlab users (because it is proprietary software) and many more Python scripters. As such, in some of our newer script tools that use FIS (e.g. RCAT and BRAT), we've come to rely on the scikit-fuzzy toolpbox, which has its own Git repo. The Request:

Thus, we'd really like to be able to support BOTH Matlab and scikit implementations of fuzzy inference systems. So the enhancement is to expand our FIS Library so that it includes:

  1. The *.fis files it always has that works in Matlab's fuzzy logic toolgox.
  2. An equivalent *.py scikit-fuzzy compatible FIS script for the model that would work with scikit
  3. As well as the appropriate documentation asked for in #164.

This is not a high priority, but something that would be nice in a future version of GCD and I'd like to flag up the thinking and a example here from Rebecca Rossi's thesis:

The Details: The actual 'model' part of SciKit also uses a text file (in this case, a *.py file typically` FIS_Rossi_SFM.txt

Scikit: Note that the scikit version, relies on various commands to define the model. In the header, you import the skfuzzy library.

import skfuzzy as fuzz
from skfuzzy import control as ctrl

In Becca's case, she defines the inputs and their corresponding membership functions using the .trapmf() command (here using the fuzz object she instatitated above. An example for a two input system is shown below:

#SLOPE
slope['low'] = fuzz.trapmf(slope.universe, [0.0, 0.0, 1.6, 6.0])
slope['moderate'] = fuzz.trapmf(slope.universe,[1.6, 6.0, 9.1, 18.1])
slope['high'] = fuzz.trapmf(slope.universe, [9.1, 18.1, 22.6, 38.6])
slope['extreme'] = fuzz.trapmf(slope.universe, [22.6, 38.6, 90.0, 90.0])

#ROUGHNESS
roughness['low'] = fuzz.trapmf(roughness.universe, [0.000, 0.000, 0.001, 0.006])
roughness['moderate'] = fuzz.trapmf(roughness.universe,[0.001, 0.006, 0.009, 0.020])
roughness['high'] = fuzz.trapmf(roughness.universe, [0.009, 0.020, 0.026, 0.044])
roughness['extreme'] = fuzz.trapmf(roughness.universe, [0.026, 0.044, 2.006, 2.006])

She was modelling DEM elevation error as her output membership function and defined it as follows:

#UNCERTAINTY
uncertainty['low'] = fuzz.trapmf(uncertainty.universe, [0.000, 0.000, 0.009, 0.019])
uncertainty['moderate'] = fuzz.trapmf(uncertainty.universe,[0.009, 0.019, 0.023, 0.046])
uncertainty['high'] = fuzz.trapmf(uncertainty.universe, [0.023, 0.046, 0.072, 0.154])
uncertainty['extreme'] = fuzz.trapmf(uncertainty.universe, [0.072, 0.154, 2.000, 2.000])

The rules are then used the .Rule() command out of the skfuzzy controls, to define the rule system (here using the cntrl object she instantiated above.

#FUZZY RULES

rule1 = ctrl.Rule(slope['low'] & roughness['low'], uncertainty['moderate'])
rule2 = ctrl.Rule(slope['low'] & roughness['moderate'], uncertainty['low'])
rule3 = ctrl.Rule(slope['low'] & roughness['high'], uncertainty['moderate'])
rule4 = ctrl.Rule(slope['low'] & roughness['extreme'], uncertainty['high'])
rule5 = ctrl.Rule(slope['moderate'] & roughness['low'], uncertainty['moderate'])
rule6 = ctrl.Rule(slope['moderate'] & roughness['moderate'], uncertainty['moderate'])
rule7 = ctrl.Rule(slope['moderate'] & roughness['high'], uncertainty['moderate'])
rule8 = ctrl.Rule(slope['moderate'] & roughness['extreme'], uncertainty['high'])
rule9 = ctrl.Rule(slope['high'] & roughness['low'], uncertainty['moderate'])
rule10 = ctrl.Rule(slope['high'] & roughness['moderate'], uncertainty['moderate'])
rule11 = ctrl.Rule(slope['high'] & roughness['high'], uncertainty['high'])
rule12 = ctrl.Rule(slope['high'] & roughness['extreme'], uncertainty['high'])
rule13 = ctrl.Rule(slope['extreme'] & roughness['extreme'], uncertainty['extreme'])
rule14 = ctrl.Rule(slope['extreme'] | roughness['extreme'], uncertainty['high'])

#CONTROL SYSTEM
uncertainty_ctrl = ctrl.ControlSystem([rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8, rule9, rule10, rule11, rule12, rule13, rule14])
uncertainty_simulation = ctrl.ControlSystemSimulation(uncertainty_ctrl)   

Matlab So this is different than in Matlab's Fuzzy Logic Toolbox where the whole model is saved in a separate *.fis text file. In that file there is a header section, an input membership function section, an output membership section and a rule table. Below, I show the translation of the scikit above, into each:

You can either read below, or just watch in this video the process of manual transformation. https://youtu.be/fY0AQMj7WGU

First, the header in Matlab:

[System]
Name='SFM-Pole_ZError_SLPdeg_SR_Meters'
Type='mamdani'
Version=2.0
NumInputs=2
NumOutputs=1
NumRules=16
AndMethod='min'
OrMethod='max'
ImpMethod='min'
AggMethod='max'
DefuzzMethod='centroid'

Next, we have the two inputs:

[Input1]
Name='SlopeDeg'
Range=[0 90.0]
NumMFs=4
MF1='Low':'trapmf',[0.0 0.0 1.6 6.0]
MF2='Moderate':'trapmf',[1.6 6.0 9.1 18.1]
MF3='High':'trapmf',[9.1 18.1 22.6 38.6]
MF4='Extreme':'trapmf',[22.6 38.6 90.0 90.0]

[Input2]
Name='Roughness'
Range=[0 2.006]
NumMFs=4
MF1='Low':'trapmf',[0.000 0.000 0.001 0.006]
MF2='Moderate':'trapmf',[0.001 0.006 0.009 0.020]
MF3='High':'trapmf',[0.009 0.020 0.026 0.044]
MF4='Extreme':'trapmf',[0.026 0.044 2.006 2.006]

Then the specification of the output membership functions:


[Output1]
Name='ElevUncertainty'
Range=[0 2.000]
NumMFs=4
MF1='Low':'trapmf',[0.000 0.000 0.009 0.019]
MF2='Moderate':'trapmf',[0.009 0.019 0.023 0.046]
MF3='High':'trapmf',[0.023 0.046 0.072 0.154]
MF4='Extreme':'trapmf',[0.072 0.154 2.000 2.000]

Finally, the translation of the rule table (note I added two rules here for logical consistency, that Becca probably omitted because they did not occur in her input data):

[Rules]
1 1, 2 (1) : 1
1 2, 1 (1) : 1
1 3, 2 (1) : 1
1 4, 3 (1) : 1
2 1, 2 (1) : 1
2 2, 2 (1) : 1
2 3, 2 (1) : 1
2 4, 3 (1) : 1
3 1, 2 (1) : 1
3 2, 2 (1) : 1
3 3, 3 (1) : 1
3 4, 3 (1) : 1
4 1, 3 (1) : 1
4 2, 3 (1) : 1
4 3, 4 (1) : 1
4 4, 4 (1) : 1

And here's the SFM-Pole_ZError_SLPdeg_SR_Meters.fis file: SFM-Pole_ZError_SLPdeg_SR_Meters.fis.txt

And that's all she wrote...

MattReimer commented 6 years ago

Just for completeness in my travels, I found that the International Electrotechnical Commission (IEC) has a dedicated language for fuzzy logic processing:

http://ffll.sourceforge.net/fcl.htm

They even have their own language which is in some ways an improvement over the Matlab FIS file specification we use.