Home | History | Annotate | Download | only in firmware_TouchMTB
      1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Fuzzy comparisons and aggregations."""
      6 
      7 
      8 import logging
      9 import math
     10 
     11 from firmware_constants import MF
     12 
     13 
     14 DEFAULT_MEMBERSHIP_FUNCTION = {
     15     '<=': MF.Z_FUNCTION,
     16     '<': MF.Z_FUNCTION,
     17     '>=': MF.S_FUNCTION,
     18     '>': MF.S_FUNCTION,
     19     '==': MF.SINGLETON_FUNCTION,
     20     '~=': MF.PI_FUNCTION,
     21 }
     22 
     23 
     24 """Define possible score aggregators: average() and product().
     25 
     26 A score aggregator collects all scores from every tests, and calculate
     27 a final score.
     28 """
     29 
     30 def average(data):
     31     """The average of the elements in data."""
     32     number = len(data)
     33     return math.fsum(data) / number if number > 0 else None
     34 
     35 
     36 def product(data):
     37     """The product of the elements in data."""
     38     return math.exp(math.fsum([math.log(d) for d in data]))
     39 
     40 
     41 """Classes of various fuzzy member functions are defined below."""
     42 
     43 class FuzzyMemberFunctions(object):
     44     """The base class of membership functions."""
     45     def __init__(self, para):
     46         """Example of parameter: (0.1, 0.3)."""
     47         self.para_values = map(float, para)
     48 
     49 
     50 class FuzzySingletonMemberFunction(FuzzyMemberFunctions):
     51     """A class provides fuzzy Singleton Membership Function.
     52 
     53     Singleton Membership Function:
     54         parameters: (left, middle, right)
     55         grade(x) = 0.0,        when x <= left
     56                    0.0 to 1.0, when left <= x <= middle
     57                    1.0,        when x == middle
     58                    1.0 to 0.0, when middle <= x <= right
     59                    0.0,        when x >= right
     60         E.g., FuzzySingletonMemberFunction((1, 1, 1))
     61               Usage: when we want the x == 1 in the ideal condition.
     62                      grade = 1.0, when x == 1
     63                              0.0, when x != 1
     64 
     65         Note: - When x is near 'middle', the grade would be pretty close to 1.
     66               - When x becomes near 'left' or 'right', its grade may drop
     67                 faster and would approach 0.
     68               - A cosine function is used to implement this behavior.
     69     """
     70     def __init__(self, para):
     71         super(FuzzySingletonMemberFunction, self).__init__(para)
     72         self.left, self.middle, self.right = self.para_values
     73         self.width_right = self.right - self.middle
     74         self.width_left = self.middle - self.left
     75 
     76     def grade(self, x):
     77         """The grading method of the fuzzy membership function."""
     78         if x == self.middle:
     79             return 1
     80         elif x <= self.left or x >= self.right:
     81             return 0
     82         elif x > self.middle:
     83             return (0.5 + 0.5 * math.cos((x - self.middle) / self.width_right *
     84                     math.pi))
     85         elif x < self.middle:
     86             return (0.5 + 0.5 * math.cos((x - self.middle) / self.width_left *
     87                     math.pi))
     88 
     89 
     90 class FuzzySMemberFunction(FuzzyMemberFunctions):
     91     """A class provides fuzzy S Membership Function.
     92 
     93     S Membership Function:
     94         parameters: (left, right)
     95         grade(x) = 1  for x >= right
     96                    0  for x <= left
     97         E.g., FuzzySMemberFunction((0.1, 0.3))
     98               Usage: when we want the x >= 0.3 in the ideal condition.
     99                      grade = 1.0,                 when x >= 0.3
    100                              between 0.0 and 1.0, when 0.1 <= x <= 0.3
    101                              0.0,                 when x <= 0.1
    102 
    103         Note: - When x is less than but near 'right' value, the grade would be
    104                 pretty close to 1.
    105               - When x becomes near 'left' value, its grade may drop faster
    106                 and would approach 0.
    107               - A cosine function is used to implement this behavior.
    108     """
    109 
    110     def __init__(self, para):
    111         super(FuzzySMemberFunction, self).__init__(para)
    112         self.left, self.right = self.para_values
    113         self.width = self.right - self.left
    114 
    115     def grade(self, x):
    116         """The grading method of the fuzzy membership function."""
    117         if x >= self.right:
    118             return 1
    119         elif x <= self.left:
    120             return 0
    121         else:
    122             return 0.5 + 0.5 * math.cos((x - self.right) / self.width * math.pi)
    123 
    124 
    125 class FuzzyZMemberFunction(FuzzyMemberFunctions):
    126     """A class provides fuzzy Z Membership Function.
    127 
    128     Z Membership Function:
    129         parameters: (left, right)
    130         grade(x) = 1  for x <= left
    131                    0  for x >= right
    132         E.g., FuzzyZMemberFunction((0.1, 0.3))
    133               Usage: when we want the x <= 0.1 in the ideal condition.
    134                      grade = 1.0,                 when x <= 0.1
    135                              between 0.0 and 1.0, when 0.1 <= x <= 0.3
    136                              0.0,                 when x >= 0.3
    137 
    138         Note: - When x is greater than but near 'left' value, the grade would be
    139                 pretty close to 1.
    140               - When x becomes near 'right' value, its grade may drop faster
    141                 and would approach 0.
    142               - A cosine function is used to implement this behavior.
    143     """
    144 
    145     def __init__(self, para):
    146         super(FuzzyZMemberFunction, self).__init__(para)
    147         self.left, self.right = self.para_values
    148         self.width = self.right - self.left
    149 
    150     def grade(self, x):
    151         """The grading method of the fuzzy membership function."""
    152         if x <= self.left:
    153             return 1
    154         elif x >= self.right:
    155             return 0
    156         else:
    157             return 0.5 + 0.5 * math.cos((x - self.left) / self.width * math.pi)
    158 
    159 
    160 # Mapping from membership functions to the fuzzy member function classes.
    161 MF_DICT = {
    162     # TODO(josephsih): PI, TRAPEZ, and TRIANGLE functions are to be implemented.
    163     # MF.PI_FUNCTION: FuzzyPiMemberFunction,
    164     MF.SINGLETON_FUNCTION: FuzzySingletonMemberFunction,
    165     MF.S_FUNCTION: FuzzySMemberFunction,
    166     # MF.TRAPEZ_FUNCTION: FuzzyTrapezMemberFunction,
    167     # MF.TRIANGLE_FUNCTION: FuzzyTriangleMemberFunction
    168     MF.Z_FUNCTION: FuzzyZMemberFunction,
    169 }
    170 
    171 
    172 class FuzzyCriteria:
    173     """A class to parse criteria string and build the criteria object."""
    174 
    175     def __init__(self, criteria_str, mf=None):
    176         self.criteria_str = criteria_str
    177         self.mf_name = mf
    178         self.mf = None
    179         self.default_mf_name = None
    180         self.value_range = None
    181         self._parse_criteria_and_exit_on_failure()
    182         self._create_mf()
    183 
    184     def _parse_criteria(self, criteria_str):
    185         """Parse the criteria string.
    186 
    187         Example:
    188             Ex 1. '<= 0.05, ~ +0.07':
    189                   . The ideal input value should be <= 0.05. If so, it gets
    190                     the grade 1.0
    191                   . The allowable upper bound is 0.05 + 0.07 = 0.12. Anything
    192                     greater than or equal to 0.12 would get a grade 0.0
    193                   . Any input value falling between 0.05 and 0.12 would get a
    194                     score between 0.0 and 1.0 depending on which membership
    195                     function is used.
    196         """
    197         criteria_list = criteria_str.split(',')
    198         tolerable_delta = []
    199         op_value = None
    200         for c in criteria_list:
    201             op, value = c.split()
    202             # TODO(josephsih): should support and '~=' later.
    203             if op in ['<=', '<', '>=', '>', '==']:
    204                 primary_op = op
    205                 self.default_mf_name = DEFAULT_MEMBERSHIP_FUNCTION[op]
    206                 op_value = float(value)
    207             elif op == '~':
    208                 tolerable_delta.append(float(value))
    209             else:
    210                 return False
    211 
    212         # Syntax error in criteria string
    213         if op_value is None:
    214             return False
    215 
    216         # Calculate the allowable range of values
    217         range_max = range_min = op_value
    218         for delta in tolerable_delta:
    219             if delta >= 0:
    220                 range_max = op_value + delta
    221             else:
    222                 range_min = op_value + delta
    223 
    224         if primary_op in ['<=', '<', '>=', '>']:
    225             self.value_range = (range_min, range_max)
    226         elif primary_op == '==':
    227             self.value_range = (range_min, op_value, range_max)
    228         else:
    229             self.value_range = None
    230 
    231         return True
    232 
    233     def _parse_criteria_and_exit_on_failure(self):
    234         """Call _parse_critera and exit on failure."""
    235         if not self._parse_criteria(self.criteria_str):
    236             logging.error('Parsing criteria string error.')
    237             exit(1)
    238 
    239     def _create_mf(self):
    240         """Parse the criteria and create its membership function object."""
    241         # If a membership function is specified in the test_conf, use it.
    242         # Otherwise, use the default one.
    243         mf_name = self.mf_name if self.mf_name else self.default_mf_name
    244         mf_class = MF_DICT[mf_name]
    245         self.mf = mf_class(self.value_range)
    246 
    247     def get_criteria_value_range(self):
    248         """Parse the criteria and return its op value."""
    249         return self.value_range
    250