gale.ai.fuzzy module

This file contains a small fuzzy logic toolkit: fuzzy sets (membership functions), FuzzyVariable (a named collection of linguistic values over a domain), FuzzyRule/FuzzyRuleSet (antecedent/consequent rules and their evaluation), and defuzzify (turning an aggregated fuzzy output back into a single crisp number). Useful for AI decisions that should change gradually instead of snapping between states – for instance, a guard’s alertness rising smoothly as a player gets closer and stays visible longer, instead of jumping straight from “unaware” to “alerted”.

Author: Alejandro Mujica (aledrums@gmail.com)

class gale.ai.fuzzy.FuzzySet[source]

Bases: object

Base class for a fuzzy set: a function from a crisp value to a degree of membership between 0 (not in the set at all) and 1 (fully in the set).

membership(x)[source]
Parameters:

x (float) – The crisp value to compute the membership degree of.

Returns:

The degree, between 0 and 1, to which x belongs to this set.

Return type:

float

class gale.ai.fuzzy.TriangularSet(left, peak, right)[source]

Bases: FuzzySet

A fuzzy set shaped like a triangle: membership is 0 at and beyond left/right, rises linearly to 1 at peak, then falls linearly back to 0.

Parameters:
  • left (float)

  • peak (float)

  • right (float)

membership(x)[source]
Parameters:

x (float) – The crisp value to compute the membership degree of.

Returns:

The degree, between 0 and 1, to which x belongs to this set.

Return type:

float

class gale.ai.fuzzy.TrapezoidalSet(left, left_peak, right_peak, right)[source]

Bases: FuzzySet

A fuzzy set shaped like a trapezoid: membership is 0 at and beyond left/right, rises linearly to 1 at left_peak, stays 1 until right_peak, then falls linearly back to 0.

Parameters:
  • left (float)

  • left_peak (float)

  • right_peak (float)

  • right (float)

membership(x)[source]
Parameters:

x (float) – The crisp value to compute the membership degree of.

Returns:

The degree, between 0 and 1, to which x belongs to this set.

Return type:

float

class gale.ai.fuzzy.LeftShoulderSet(peak, right)[source]

Bases: FuzzySet

A fuzzy set that is fully true (1) at and below peak, then falls linearly to 0 at and beyond right. Useful for the lowest linguistic value of a variable (e.g. “near”), where anything below the interesting range should be fully in the set.

Parameters:
  • peak (float)

  • right (float)

membership(x)[source]
Parameters:

x (float) – The crisp value to compute the membership degree of.

Returns:

The degree, between 0 and 1, to which x belongs to this set.

Return type:

float

class gale.ai.fuzzy.RightShoulderSet(left, peak)[source]

Bases: FuzzySet

A fuzzy set that is 0 at and below left, rises linearly to 1 at peak, and stays fully true (1) beyond it. Useful for the highest linguistic value of a variable (e.g. “far”), where anything beyond the interesting range should be fully in the set.

Parameters:
  • left (float)

  • peak (float)

membership(x)[source]
Parameters:

x (float) – The crisp value to compute the membership degree of.

Returns:

The degree, between 0 and 1, to which x belongs to this set.

Return type:

float

gale.ai.fuzzy.fuzzy_and(*degrees)[source]
Parameters:

degrees (float) – Membership degrees to combine.

Returns:

The fuzzy AND (minimum) of degrees.

Return type:

float

gale.ai.fuzzy.fuzzy_or(*degrees)[source]
Parameters:

degrees (float) – Membership degrees to combine.

Returns:

The fuzzy OR (maximum) of degrees.

Return type:

float

class gale.ai.fuzzy.FuzzyVariable(name, domain, sets, resolution=100)[source]

Bases: object

A named collection of fuzzy sets (linguistic values, such as “near”/”medium”/”far”) sharing a single domain. Used both to fuzzify a crisp input (turn a number into a dict of membership degrees, one per linguistic value) and, as a consequent, to defuzzify a rule set’s aggregated output back into a single crisp number.

Usage example:

distance = FuzzyVariable(

“distance”, domain=(0, 100), sets={

“near”: LeftShoulderSet(10, 30), “medium”: TriangularSet(10, 40, 70), “far”: RightShoulderSet(50, 90),

},

) distance.fuzzify(25) # {“near”: 0.25, “medium”: 0.5, “far”: 0.0}

Parameters:
  • name (str)

  • domain (Tuple[float, float])

  • sets (Dict[str, FuzzySet])

  • resolution (int)

fuzzify(x)[source]
Parameters:

x (float) – The crisp value to fuzzify.

Returns:

The membership degree of x in each of this variable’s linguistic values.

Return type:

Dict[str, float]

defuzzify(degrees)[source]

Turn an aggregated set of output degrees (one per linguistic value of this variable, as produced by FuzzyRuleSet.evaluate) back into a single crisp number, using the centroid method: sample the domain, clip each linguistic value’s membership at its degree, and average the samples weighted by their clipped membership.

Parameters:

degrees (Dict[str, float]) – Aggregated membership degree of each of this variable’s linguistic values.

Returns:

The crisp value at the centroid of the aggregated, clipped membership curve, or the midpoint of domain if every sample has zero membership.

Return type:

float

class gale.ai.fuzzy.FuzzyRule(antecedent, consequent_variable, consequent_set)[source]

Bases: object

A single fuzzy rule: an antecedent that computes a firing degree from a dict of fuzzified inputs (build it with fuzzy_and/fuzzy_or over the values fuzzify() produced), and a consequent – the linguistic value of an output FuzzyVariable that this rule votes for, weighted by how strongly it fires.

Parameters:
  • antecedent (Callable[[Dict[str, float]], float])

  • consequent_variable (str)

  • consequent_set (str)

class gale.ai.fuzzy.FuzzyRuleSet(rules)[source]

Bases: object

A collection of FuzzyRule evaluated together: each rule’s firing degree is computed from the fuzzified inputs, then rules voting for the same (variable, linguistic value) pair are aggregated by fuzzy_or (max), producing one degree per output linguistic value per variable – ready to hand to FuzzyVariable.defuzzify.

Usage example:

alertness = FuzzyVariable(“alertness”, (0, 1), {

“low”: LeftShoulderSet(0.2, 0.5), “high”: RightShoulderSet(0.5, 0.8),

}) rules = FuzzyRuleSet([

FuzzyRule(

lambda d: fuzzy_and(d[“distance”][“near”], d[“visible”][“yes”]), “alertness”, “high”,

), FuzzyRule(lambda d: d[“distance”][“far”], “alertness”, “low”),

]) fuzzified = {

“distance”: distance.fuzzify(25), “visible”: visible.fuzzify(1),

} output_degrees = rules.evaluate(fuzzified) alertness.defuzzify(output_degrees[“alertness”])

Parameters:

rules (List[FuzzyRule])

evaluate(fuzzified_inputs)[source]
Parameters:

fuzzified_inputs (Dict[str, Dict[str, float]]) – Fuzzified input variables, as name -> FuzzyVariable.fuzzify(x) result.

Returns:

For each consequent variable name affected by at least one rule, a dict of linguistic_value -> aggregated firing degree.

Return type:

Dict[str, Dict[str, float]]