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

Implementing a Mean 3Pi aggregation operator with Pyfuzzylite #13

Open
YamatoFr opened this issue Apr 20, 2024 · 10 comments
Open

Implementing a Mean 3Pi aggregation operator with Pyfuzzylite #13

YamatoFr opened this issue Apr 20, 2024 · 10 comments

Comments

@YamatoFr
Copy link

Hello!

I'm trying to implement a Mean 3Pi (M3P) aggregation operator:

$M3\Pi(x_1, \cdots, x_n) = \frac{\Pi_{j=1}^n (x_j)^{(1/n)}}{\Pi_{j=1}^n (x_j)^{(1/n)} + \Pi_{j=1}^n (1 - x_j)^{(1/n)}}$

I understand that a custom aggregation operator would be defined like this:

import fuzzylite as fl
from fuzzylite.types import Scalar

class Mean3Pi(fl.SNorm):
    def compute(self, a: Scalar, b: Scalar) -> Scalar:

        a = fl.scalar(a)
        b = fl.scalar(b)

        result = ###

        return result

I know that $a$ and $b$ are the membership functions values, however, despite looking into the library I still do not understand how the compute function retrieves those values and how I can manipulate them to implement the operator.

I will this article that explains the M3P better than I can: Analysis_of_New_Aggregation_Operators.pdf

I hope my explanation was not too confusing, feel free to ask if you need more information.

@jcrada
Copy link
Member

jcrada commented Apr 20, 2024

Hi Theo

Thanks for your post and interesting article.

You are almost there! In fuzzylite, $a=x_1$ and $b=x_2$ so you just need to expand the equation you have for $n=2$.

Does that make sense?

Cheers

@YamatoFr
Copy link
Author

Hello,

If I'm not mistaken, the new equation is:

$M3\Pi(x_1, x_2) = \frac{(x_1)^(1/2) * (x_2)^(1/2)}{(x_1)^(1/2) * (x_2)^(1/2) + (1 - x_1)^(1/2)*(1 - x_2)^(1/2)}$

$M3\Pi(x_1, x_2) = \frac{\sqrt(x_1) * \sqrt(x_2)}{\sqrt(x_1) * \sqrt(x_2) + \sqrt(1 - x_1) * \sqrt(1 - x_2)}$

Which gives:

class Mean3Pi(fl.SNorm):
    def compute(self, a: Scalar, b: Scalar) -> Scalar:

        a = fl.scalar(a)
        b = fl.scalar(b)

        # for testing purpose
        print("a: ", a, "\nb: ", b, "\n")

        numerator = np.sqrt(a) * np.sqrt(b)
        denominator = np.sqrt(a) * np.sqrt(b) + np.sqrt(1 - a) * np.sqrt(1-  b)

        return numerator / denominator

But in some cases there is "nan" in the values of $a$. Here's an exemple.
image

Those appear during the processing of the engine, so I'm thinking that maybe $\sqrt(1 - a)$ is causing the issue.

Even when there's no "nan", the output variables has no value, both discrete and fuzzy (except in very rare cases).
image

I don't understand what could be the source this problem, I'll do some further testing.

Cheers

@jcrada
Copy link
Member

jcrada commented Apr 24, 2024

Hi,

The equations seem fine, but your problem is happening before the aggregation.

Would you like to share your engine in the fuzzylite language? I just want to check with QtFuzzyLite how your engine is working without the aggregation.

You could also get QtFuzzyLite at: https://fuzzylite.com/buy

@YamatoFr
Copy link
Author

YamatoFr commented Apr 24, 2024

Hello,

Sure, the code is available here: https://github.com/YamatoFr/FOG_computing_tasks_scheduling/tree/main/scripts/fuzzy

The engine and the code for the M3P are in fuzzyengine.py, the processing is done in fuzzyprocessing.py.

And here's the engine in the fuzzylite language:

Edit: there's a .fll file available too.

Engine: TaskOffloading
InputVariable: Bandwidth
 enabled: true
 range: 0.000 100.000
 lock-range: false
 term: bw_low Trapezoid 0.000 20.000 30.000 40.000
 term: bw_medium Trapezoid 35.000 45.000 60.000 70.000
 term: bw_high Trapezoid 65.000 75.000 90.000 100.000
InputVariable: Datasize
 enabled: true
 range: 0.000 600.000
 lock-range: false
 term: data_low Trapezoid 0.000 0.000 230.000 360.000
 term: data_medium Trapezoid 250.000 350.000 470.000 590.000
 term: data_high Trapezoid 450.000 540.000 600.000 600.000
InputVariable: Load
 enabled: true
 range: 0.000 100.000
 lock-range: false
 term: load_low Trapezoid 0.000 0.000 25.000 40.000
 term: load_medium Trapezoid 35.000 45.000 60.000 70.000
 term: load_high Trapezoid 65.000 80.000 100.000 100.000
InputVariable: Memory
 enabled: true
 range: 0.000 100.000
 lock-range: false
 term: mem_low Trapezoid 0.000 0.000 25.000 40.000
 term: mem_medium Trapezoid 35.000 45.000 60.000 70.000
 term: mem_high Trapezoid 65.000 80.000 100.000 100.000
InputVariable: NB_concurrent_users
 enabled: true
 range: 0.000 100.000
 lock-range: false
 term: user_low Trapezoid 0.000 0.000 25.000 40.000
 term: user_medium Trapezoid 30.000 40.000 60.000 70.000
 term: user_high Trapezoid 60.000 75.000 100.000 100.000
OutputVariable: Processing
 enabled: true
 range: 0.000 100.000
 lock-range: false
 aggregation: none
 defuzzifier: Centroid 200
 default: nan
 lock-previous: false
 term: local_processing Trapezoid 0.000 12.000 24.000 48.000
 term: remote_processing Trapezoid 36.000 60.000 72.000 100.000
RuleBlock: Mamdani
 enabled: true
 conjunction: Minimum
 disjunction: Maximum
 implication: Minimum
 activation: General
 rule: if Bandwidth is bw_low then Processing is local_processing
 rule: if Bandwidth is not bw_low and Datasize is not data_low then Processing is remote_processing
 rule: if Bandwidth is not bw_low and Datasize is data_low and (Load is load_high or Memory is mem_low) then Processing is remote_processing
 rule: if Bandwidth is not bw_low and Datasize is data_low and (NB_concurrent_users is user_low or Memory is mem_low) then Processing is remote_processing
 rule: if Bandwidth is not bw_low and Datasize is data_low and NB_concurrent_users is not user_low and Memory is not mem_low and Load is load_low then Processing is local_processing
 rule: if Bandwidth is not bw_low and Datasize is data_low and NB_concurrent_users is not user_low and Memory is not mem_low and Load is not load_low then Processing is remote_processing

@jcrada
Copy link
Member

jcrada commented Apr 26, 2024

Hi,

I think your problem could be that you are not setting the values of the input variables correctly.
Maybe create a Google Collab notebook and share the code that makes it fail?

Cheers.

@jcrada
Copy link
Member

jcrada commented Apr 26, 2024

Also, I noticed maybe a bug somewhere in your code, maybe unrelated to this issue:

if engine.output_variable("Processing").fuzzy_value() != nan:

you should compare nan values using fl.Op.isnan(...).

And also, that fuzzy_value() returns an array of strings.

@YamatoFr
Copy link
Author

YamatoFr commented Apr 26, 2024

Hi,

I've created a Colab notebook, I'll send the link to you via mail.

Cheers.

@jcrada
Copy link
Member

jcrada commented Apr 29, 2024

Hi, I took a look.

The problem is the numerator becoming zero, which happens whenever a or b are zero.

Also, the expansion of the equation may not be the solution for using fuzzylite. I did not read the article fully, but it may be the case that you need to do it differently. In fuzzylite, you need only a and b because of the way it is aggregated. See the following:

https://github.com/fuzzylite/pyfuzzylite/blob/0fc7c5747934b53b6a35ff830fcc02e8ae38196e/fuzzylite/term.py#L482-L485

But the 3MeanPi may not work with this approach because $n$ refers to the total number of activated terms. In this case, you would need instead to create your own Aggregated term, re-implement the lines above to consider the full equation properly (not the expansion), and then your Aggregation operator is irrelevant. Still, with this approach the numerator will result in zero unless you skip every $x_i=0$, which you should in this case anyway.

To summarise, I would do:

  • Implement class Aggregated3MeanPi(Aggregated):.
  • Override membership to compute the Mean3Pi properly based on the number of activated terms
  • Skip any zero-degree activations.

It is a bit more complex than what you have done so far. I particularly don't know:

  • how will you compute $n$ when ignoring zero-degree activations (is $n$ affected?)
  • how will you order $x$? Are the $x_i$ sorted by activation degree? Or by index in output variable?

I hope this helps.

@YamatoFr
Copy link
Author

Hello,

Thanks for the help, I've made some good progress thanks to you (new bugs/errors are still progress). I do have one last question, where should I call this new membership function? Is it in the compute function or elsewhere?

Cheers.

@jcrada
Copy link
Member

jcrada commented Apr 30, 2024

Cool. If you implemented the Aggregated class overriding the membership function, then you just need to set it in the output variable (eg output_variable.fuzzy = Aggregated3MeanPi(...); see
https://github.com/fuzzylite/pyfuzzylite/blob/0fc7c5747934b53b6a35ff830fcc02e8ae38196e/fuzzylite/variable.py#L379
)

Oh, by the way, on what I don't know that I mentioned before, you probably don't care about the order of x. I was confused that the exponent was related to the ordering, but it is just a constant.

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

No branches or pull requests

2 participants