Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: High-level fuzzy control system API #70

Merged
merged 29 commits into from
Feb 12, 2016

Conversation

JDWarner
Copy link
Collaborator

This PR adds what many people (see #69, #66, and multiple posts on the mailing list) have asked for: a much improved framework to implement fuzzy systems from end to end.

  • One major new dependency: networkx.
  • This API lives in skfuzzy.control, and primarily uses four classes
    • Antecedent
    • Consequent
    • Rule
    • ControlSystem

Presently it's functional but sill a WIP. It is stable enough for other people to kick the tires at this point. If there are things missing you would like to see, speak up!

The major benefit is a simple yet very powerful API to build a fuzzy system, from input/antecedents to output/consequents, including all of the rules. Documentation will improve over the course of this PR, but here is the tipping problem implemented with this PR:

import numpy as np
import skfuzzy as fuzz

# New Antecedent/Consequent objects hold universe variables and membership functions
quality = fuzz.Antecedent(np.arange(0, 11, 1), 'quality')
service = fuzz.Antecedent(np.arange(0, 11, 1), 'service')
tip = fuzz.Consequent(np.arange(0, 26, 1), 'tip')

# Auto-membership function population is possible with .automf(3, 5, or 7)
quality.automf(3)
service.automf(3)

# Custom membership functions can be built interactively with a familiar, Pythonic API
tip['poor'] = fuzz.trimf(tip.universe, [0, 0, 13])
tip['average'] = fuzz.trimf(tip.universe, [0, 13, 25])
tip['good'] = fuzz.trimf(tip.universe, [13, 25, 25])

# You can see how these look with .view()
quality.view()
service.view()
tip.view()

# Rule objects connect one or more antecedent membership functions with 
# one or more consequent membership functions, using 'or' or 'and' to combine the antecedents.
#   * rule1: "If food is poor OR services is poor, then tip will be poor
#   * rule2: "If service is average, then tip will be average
#   * rule3: "If service is good OR food is good, then tip will be good
rule1 = fuzz.Rule([quality['poor'], service['poor']], tip['poor'], kind='or')
rule2 = fuzz.Rule(service['average'], tip['average'])
rule3 = fuzz.Rule([service['good'], quality['good']], tip['good'])

# Create a new ControlSystem with these rules added
# Note: it is possible to create an empty ControlSystem() and build it up interactively.
tipping = fuzz.ControlSystem([rule1, rule2, rule3])

# Pass inputs to the ControlSystem using Antecedent labels with Pythonic API
# Note: if you like passing many inputs all at once, use .inputs(dict_of_data)
tipping.input['quality'] = 6.5
tipping.input['service'] = 9.8

# Crunch the numbers
tipping.compute()

# Output available as a dict, for arbitrary number of consequents
tipping.output

# Viewing the Consequent again after computation shows the calculated system
tip.view()

A lot shorter than the old way, and more intuitive!

@mohammedaljawad
Copy link

Thank so much for the example. Can you please explain, how possible to add those four classes:

Antecedent
Consequent
Rule
ControlSystem

I tried to redownload the package and reinstall it but it didn't work.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 2, 2015

@mohammedaljawad this currently requires Git and a development version as it is still being improved. If you don't have Git, you'll need that. Once you have Git, these commands run in order in a terminal will do the job:

git clone https://github.com/scikit-fuzzy/scikit-fuzzy.git
cd scikit-fuzzy
git remote add JDWarner https://github.com/JDWarner/scikit-fuzzy.git
git pull JDWarner fuzzycontrolsystem

There will be a merge commit you need to approve by pressing Shift+Z twice. Then

python setup.py develop

After that, restart your preferred Python shell and run import skfuzzy; skfuzzy.__version__. You should see "0.2dev" instead of "0.1.3", and these features will be available.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 2, 2015

You will need to install networkx. It is now a dependency.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 2, 2015

@mohammedaljawad Please report back your experience using this, especially including any problems!

@mohammedaljawad
Copy link

I customized the tutorial and now it supports four inputs with three intervals and one output with five intervals. And I got this message:

Traceback (most recent call last):
  File "mine.py", line 62, in <module>
    trusting.compute()
  File ".../scikit-fuzzy/skfuzzy/control/controlsystem.py", line 290, in compute
    consequent.compute()
  File ".../scikit-fuzzy/skfuzzy/control/antecedent_consequent.py", line 190, in compute
    self.output = defuzz(self.universe, self.output_mf, mode)
  File ".../scikit-fuzzy/skfuzzy/defuzzify/defuzz.py", line 132, in defuzz
    assert tot_area != 0, 'Total area is zero in defuzzification!'
AssertionError: Total area is zero in defuzzification!

I tried to use the automated membership with five intervals, however, I got this message:

>>> import numpy as np
>>> import skfuzzy as fuzz
>>> trust = fuzz.Consequent(np.arange(0, 1.1, 0.1), 'trust')
>>> trust.automf(5)
>>> trust.view()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../scikit-fuzzy/skfuzzy/control/antecedent_consequent.py", line 223, in view
    y = interp_membership(self.universe, self.output_mf, self.output)
  File ".../scikit-fuzzy/skfuzzy/fuzzymath/fuzzy_ops.py", line 647, in interp_membership
    x2 = x[x >= xx][0]
IndexError: index out of bounds

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 4, 2015

@mohammedaljawad For the first problem, it's either a real bug or a set of inputs that is outside the bounds of a sparse rule set. I need more info to know for sure; can you post your test script?

For the second problem, the automatic membership function creation works - the issue is in the visualization script. After you get that error, what is the result of trust.output? It should be None which would avoid execution of that problem line. I'll look into this further tonight.

@mohammedaljawad
Copy link

Thanks a lot. Below is my code:

import numpy as np
import skfuzzy as fuzz

# New Antecedent/Consequent objects hold universe variables and membership
#functions
hs = fuzz.Antecedent(np.arange(0, 1.1,0.1), 'hs')
prf = fuzz.Antecedent(np.arange(0, 1.1,0.1), 'prf')
abt = fuzz.Antecedent(np.arange(0, 1.1,0.1), 'abt')
cl = fuzz.Antecedent(np.arange(0, 1.1,0.1), 'cl')
tr = fuzz.Consequent(np.arange(0, 1.1, 0.1), 'tr')

# Auto-membership function population is possible with .automf(3, 5, or 7)
hs.automf(3)
prf.automf(3)
abt.automf(3)
cl.automf(3)
tr.automf(5)


# Custom membership functions can be built interactively with a familiar,
# Pythonic API
"""tr['low'] = fuzz.trimf(tr.universe, [0, 0, 0.25])
tr['mediumlow'] = fuzz.trimf(tr.universe, [0, 0.25, 0.5])
tr['medium'] = fuzz.trimf(tr.universe, [0.25, 0.5, 0.75])
tr['midiumhigh'] = fuzz.trimf(tr.universe, [0.5, 0.75, 1])
tr['high'] = fuzz.trimf(tr.universe, [0.75, 1, 1])
"""


# You can see how these look with .view()
hs.view()
prf.view()
abt.view()
cl.view()
tr.view()

# Rule objects connect one or more antecedent membership functions with
# one or more consequent membership functions, using 'or' or 'and' to combine
# the antecedents.
#   * rule1: "If food is poor OR services is poor, then tip will be poor
#   * rule2: "If service is average, then tip will be average
#   * rule3: "If service is good OR food is good, then tip will be good
rule1 = fuzz.Rule([hs['poor'], prf['poor'], abt['poor']
, cl['poor']], tr['poor'] ,kind='or')
rule2 = fuzz.Rule([hs['average'], prf['average']
, abt['average'], cl['average']], tr['average'])
rule3 = fuzz.Rule([hs['good'], prf['good'], abt['good']
, cl['good']], tr['good'])



# Create a new ControlSystem with these rules added
# Note: it is possible to create an empty ControlSystem() and build it up
#interactively.
tring = fuzz.ControlSystem([rule1, rule2, rule3])

# Pass inputs to the ControlSystem using Antecedent labels with Pythonic API
# Note: if you like passing many inputs all at once, use .inputs(dict_of_data)
tring.input['hs'] = 0.9
tring.input['prf'] = 0.9
tring.input['abt'] = 0.9
tring.input['cl'] = 0.9

# Crunch the numbers
tring.compute()

# Output available as a dict, for arbitrary number of consequents
tring.output

# Viewing the Consequent again after computation shows the calculated system
tr.view()

@mohammedaljawad
Copy link

I tried this:

tr['low'] = fuzz.trimf(tr.universe, [0, 0, 0.25])
tr['mediumlow'] = fuzz.trimf(tr.universe, [0, 0.25, 0.5])
tr['medium'] = fuzz.trimf(tr.universe, [0.25, 0.5, 0.75])
tr['midiumhigh'] = fuzz.trimf(tr.universe, [0.5, 0.75, 1])
tr['high'] = fuzz.trimf(tr.universe, [0.75, 1, 1])

but it didn't work, then I used the automated one.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 5, 2015

@mohammedaljawad The first issue was a real bug, now fixed - automf cleared the output incorrectly for Consequents. This was also the root cause of the second issue. See how it works now. You can update your code by running git pull JDWarner fuzzycontrolsystem and restarting any Python shell(s).

@mohammedaljawad
Copy link

Thanks a lot.
I got two of them with trapezoidal shape, however, I should get them Triangular shape

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 5, 2015

That's because your universe variable isn't high enough resolution. If the peaks aren't included as valid points, when matplotlib connects the dots, you'll see trapezoids.

@mohammedaljawad
Copy link

I already set the variables of all membership functions. Sorry, I don't get that. Is there any reference or explanation for that. This doesn't seem to be a problem on Matlab.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 5, 2015

There is no issue, it's in the visualization.

Think about this: If your universe only has points at [0, 2.5, 5, 7.5, 10], what happens when you want a triangular membership function starting at 2.5 and ending at 10 with a peak at 6? Membership functions exist only where their universes are defined, so there is no way to have a true peak between points in the universe.

Thus, you must choose your universe to be high enough resolution to include your peaks.

I've no idea what Matlab is doing; I don't use or look at their code. Best guess is they may automatically upsample sparse universes under the hood.

@mohammedaljawad
Copy link

Thank you so much for your help. But can I get some references talk deeply about that. Since, I need to justify every single step in my research.

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 5, 2015

This is self-evident, it is clear enough that it wouldn't be publishable. Make the example I just sketched out above with fuzz.trimf and plot it a few times to convince yourself.

@mohammedaljawad
Copy link

How can I get the result of tring.compute()?

@JDWarner
Copy link
Collaborator Author

JDWarner commented Dec 6, 2015

The output(s) from the system are available in the FuzzySystem.output variable as an OrderedDict. The label of each Consequent is the key, and it's attached to the resulting value.

Inputs can then be changed and .compute() will update the .output variable, with only the minimal amount of computations needed.

@JDWarner JDWarner force-pushed the fuzzycontrolsystem branch 3 times, most recently from 6827d20 to 3dd0c30 Compare December 6, 2015 02:25
@JDWarner
Copy link
Collaborator Author

Thanks to @jsexauer for an excellent overhaul of the framework! I'm merging this in a broken state so we can move forward, hammering out the API and test suite in separate PRs.

JDWarner added a commit that referenced this pull request Feb 12, 2016
FEAT: High-level fuzzy control system API
@JDWarner JDWarner merged commit d3c0768 into scikit-fuzzy:master Feb 12, 2016
@JDWarner JDWarner deleted the fuzzycontrolsystem branch February 12, 2016 04:47
@nilayrox18
Copy link

hello Mr. Warner. i am posting my code and error message please solve it for me i need you help.
import numpy as np
import skfuzzy as fuzz

quality = fuzz.Antecedent(np.arange(0,11,1),'quality')
service = fuzz.Antecedent(np.arange(0,11,1),'service')

tip = fuzz.Consequent(np.arange(0,26,1),'tip')

quality.automf(3)
service.automf(3)
tip.automf(3)

quality.view()
service.view()
tip.view()

rule1 = fuzz.Rule(quality['poor'] | service['poor'],tip['poor'])
rule2 = fuzz.Rule(quality['good'] | service['good'],tip['good'])

tip_cnt = fuzz.ControlSystem([rule1, rule2])

tipp = fuzz.ControlSystemSimulation(tip_cnt)

tipp.input['quality'] = 5
tipp.input['service'] = 5

tipp.compute()

print(tipp.output)

error msg is:
Traceback (most recent call last):
File "C:\Users\DarkEvill\Desktop\ex3456.py", line 29, in
tipp.compute()
File "C:\Python34\lib\site-packages\scikit_fuzzy-0.2dev-py3.4.egg\skfuzzy\control\controlsystem.py", line 147, in compute
if antecedent.terms.values()[0].membership_value[self] is not None:
TypeError: 'ValuesView' object does not support indexing

@jsexauer
Copy link
Collaborator

@nilayrox18 I am unable to reproduce your issue give the code above in python 2.7. However, this does appear to be an issue in Python 3 due to the fact that OrderedDict returns a ValuesView instead of new list. I will address in a separate PR.

@Raghavaraman
Copy link

hey i am trying to use the control module but am getting "AttributeError: 'module' object has no attribute 'Antecedent'" what should i do to make this attribute work??

@JDWarner
Copy link
Collaborator Author

@Raghavaraman The new API is merged in to the 0.2dev development branch in anticipation of the 0.2 release, however, that release has not yet happened. The new control system API still needs a few tweaks yet (working on it!).

However, we welcome you to experiment with the new control system API! Simply clone this repository and run python setup.py develop in the root directory - you'll then have version 0.2dev and can keep up to date running git pull origin master periodically.

We'd like as many people as possible to try it out before release, so please do!

@Raghavaraman
Copy link

thank you sir, i used git commands ava. in linux derbian based system and when i try to import the skfuzzy into python it says no lib present. do you have any suggestion as to what went wrong?

P.S i had to use sudo command to run the setup .

@JDWarner
Copy link
Collaborator Author

JDWarner commented Apr 8, 2016

@Raghavaraman Probably two issues.

First, if you have to run sudo generally there will be problems. Permissions on *NIX create issues with Python because Python compiles .py files to .pyc files at runtime. If these files can't be written, it's an issue. A good rule is any time you download a new Python package manually (without using the system apt-get or yum), put it in your current user folder and run python setup.py develop instead of python setup.py install.

The develop command keeps it there, where you have permission, updating only your PYTHONPATH. The install command tries to install it to your Python's site-packages, which may not be under your control.

The second possibility is that you may not have run python setup.py develop. If you didn't, then the package is unknown to your Python installation and it won't be able to import it.

@ManolisCh
Copy link

Hi and thanks for this very promising fuzzy package!

I find the new API very helpful and easy to use for quickly building fuzzy systems. However I haven't found yet how to have control (with the new API) over a few things such as, the fuzzy operators and the deffuzification methods to be used.

I was wondering if this is something that hasn't implemented yet or something that I have missed given I am new to fuzzy and python?

@JDWarner
Copy link
Collaborator Author

Hi @ManolisCh, that's a great question. We've built some internal functionality around these goals, but I don't think it is yet exposed. Mainly because we haven't yet found a simple, powerful way to do so.

This particular PR is already merged, and the discussion is lengthy, so I'm going to move your suggestion to a separate Issue as a feature request.

I fully expect we will make this functionality available, but we want to make sure we do so in the best way possible. Within the current framework, how you would like to control these options? What do you see as the most elegant and intuitive way to control them?

@ManolisCh
Copy link

Thanks for your quick answer! I will reply once I got some more experience using scikit-fuzzy in the newly open feature request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants