Пример #1
0
 def add_module(self, module):
     for fname, f in module.__dict__.items():
         if fname[0] == '_': continue
         if isinstance(f, collections.Callable):
             self.add_function(f)
         elif math2.is_number(f):
             self.add_constant(f, fname)
Пример #2
0
    def add(self, expr):
        ''' add expr
        :return: bool True if it has been added
        '''

        if expr is None:
            return False
        try:
            k = expr()  # the key is the numeric value of expr
        except (TypeError, ValueError):
            return False

        if not math2.is_number(k):
            return False

        if type(k) is complex:
            return False

        if self.int:
            # limit to integers... but maybe we're extremely close
            k = math2.int_or_float(k)
            if not isinstance(k, int): return False

        if k < 0:
            return self.add(-expr)

        if self.max is not None and k > self.max:
            return False

        if k in self:
            if self.improve and expr.complexity() >= self[k].complexity():
                return False

        self[k] = expr
        return True
Пример #3
0
 def isconstant(self):
     ''':return: True if Expr evaluates to a constant number or bool'''
     res = self()
     if math2.is_number(res):
         return True
     if isinstance(res, bool):
         return True
     return False
Пример #4
0
    def eval(self, node):
        '''safe eval of ast node : only functions and _operators listed above can be used

        :param node: ast.AST to evaluate
        :param ctx: dict of varname : value to substitute in node
        :return: number or expression string
        '''
        if isinstance(node, ast.Num):  # <number>
            return node.n
        elif isinstance(node, ast.Name):
            return self.variables.get(node.id, node.id)  # return value or var
        elif isinstance(node, ast.Attribute):
            return getattr(self.variables, [node.value.id], node.attr)
        elif isinstance(node, ast.Tuple):
            return tuple(self.eval(e) for e in node.elts)
        elif isinstance(node, ast.Call):
            params = [self.eval(arg) for arg in node.args]
            if node.func.id not in self.functions:
                raise NameError('%s function not allowed' % node.func.id)
            f = self.functions[node.func.id][0]
            res = f(*params)
            # try to correct small error
            return math2.int_or_float(res, 0, 1e-12)
        elif isinstance(node, ast.BinOp):  # <left> <operator> <right>
            op = self.operators[type(node.op)]
            left = self.eval(node.left)
            right = self.eval(node.right)
            if math2.is_number(left) and math2.is_number(right):
                res = op[0](left, right)
                # no correction here !
                return res
            else:
                return "%s%s%s" % (left, op[_dialect_python], right)
        elif isinstance(node, ast.UnaryOp):  # <operator> <operand> e.g., -1
            right = self.eval(node.operand)
            return self.operators[type(node.op)][0](right)
        elif isinstance(node, ast.Compare):
            left = self.eval(node.left)
            for op, right in zip(node.ops, node.comparators):
                # TODO: find what to do when multiple items in list
                return self.operators[type(op)][0](left, self.eval(right))
        elif isinstance(node, ast.NameConstant):
            return node.value
        else:
            logging.warning(ast.dump(node, False, False))
            return self.eval(node.body)  # last chance
Пример #5
0
 def add_module(self, module):
     for fname, f in module.__dict__.items():
         if fname[0] == '_':
             continue
         if isinstance(f, collections.Callable):
             self.add_function(f)
         elif math2.is_number(f):
             self.add_constant(f, fname)
Пример #6
0
    def eval(self, node):
        '''safe eval of ast node : only functions and _operators listed above can be used

        :param node: ast.AST to evaluate
        :param ctx: dict of varname : value to substitute in node
        :return: number or expression string
        '''
        if isinstance(node, ast.Num):  # <number>
            return node.n
        elif isinstance(node, ast.Name):
            return self.variables.get(node.id, node.id)  # return value or var
        elif isinstance(node, ast.Attribute):
            return getattr(self[node.value.id], node.attr)
        elif isinstance(node, ast.Tuple):
            return tuple(self.eval(e) for e in node.elts)
        elif isinstance(node, ast.Call):
            params = [self.eval(arg) for arg in node.args]
            if not node.func.id in self.functions:
                raise NameError('%s function not allowed' % node.func.id)
            f = self.functions[node.func.id][0]
            res = f(*params)
            return math2.int_or_float(res, 0, 1e-12)  # try to correct small error
        elif isinstance(node, ast.BinOp):  # <left> <operator> <right>
            op = self.operators[type(node.op)]
            left = self.eval(node.left)
            right = self.eval(node.right)
            if math2.is_number(left) and math2.is_number(right):
                res = op[0](left, right)
                # no correction here !
                return res
            else:
                return "%s%s%s" % (left, op[_dialect_python], right)
        elif isinstance(node, ast.UnaryOp):  # <operator> <operand> e.g., -1
            right = self.eval(node.operand)
            return self.operators[type(node.op)][0](right)
        elif isinstance(node, ast.Compare):
            left = self.eval(node.left)
            for op, right in zip(node.ops, node.comparators):
                # TODO: find what to do when multiple items in list
                return self.operators[type(op)][0](left, self.eval(right))
        elif isinstance(node, ast.NameConstant):
            return node.value
        else:
            logging.warning(ast.dump(node, False, False))
            return self.eval(node.body)  # last chance
Пример #7
0
 def __eq__(self, other):
     if math2.is_number(other):
         try:
             return self() == other
         except:
             return False
     if not isinstance(other, Expr):
         other = Expr(other, self.context)
     return str(self()) == str(other())
Пример #8
0
 def __lt__(self, other):
     if math2.is_number(other):
         try:
             return self() < other
         except:
             return False
     if not isinstance(other, Expr):
         other = Expr(other, self.context)
     return float(self()) < float(other())
Пример #9
0
 def __eq__(self, other):
     if math2.is_number(other):
         try:
             return self() == other
         except:
             return False
     if not isinstance(other, Expr):
         other = Expr(other, self.context)
     return str(self()) == str(other())
Пример #10
0
 def __lt__(self, other):
     if math2.is_number(other):
         try:
             return self() < other
         except:
             return False
     if not isinstance(other, Expr):
         other = Expr(other, self.context)
     return float(self()) < float(other())
Пример #11
0
 def __mul__(self, other):
     if math2.is_number(other):
         mean = self.mean
         var = other * self.variance
     else:  # it's a Stat
         mean = self.mean * other.mean
         # https://fr.wikipedia.org/wiki/Variance_(statistiques_et_probabilit%C3%A9s)#Produit
         var = self.variance * other.variance + \
             self.variance * other.mean ** 2 + other.variance * self.mean ** 2
     return Stats(mean=mean, var=var)
Пример #12
0
 def __add__(self, other):
     if math2.is_number(other):
         other = Stats([other])
     # https://fr.wikipedia.org/wiki/Variance_(statistiques_et_probabilit%C3%A9s)#Produit
     try:
         cov = covariance(self, other)
     except:
         cov = 0
     mean = (self.mean + other.mean) / 2  # TODO : improve
     var = self.variance + other.variance + 2 * cov
     return Stats(mean=mean, var=var)
Пример #13
0
def strftimedelta(t,fmt='%H:%M:%S'):
    """
    :param t: float seconds or timedelta
    """
    if not math2.is_number(t):
        t=t.total_seconds()
    hours, remainder = divmod(t, 3600)
    minutes, seconds = divmod(remainder, 60)
    res=fmt.replace('%H','%d'%hours)
    res=res.replace('%h','%d'%hours if hours else '')
    res=res.replace('%M','%02d'%minutes)
    res=res.replace('%m','%d'%minutes if minutes else '')
    res=res.replace('%S','%02d'%seconds)
    return res
Пример #14
0
 def __mul__(self,other):
     if isinstance(other,six.string_types):
         return self.colorize('black',other)
     if math2.is_number(other):
         return self.compose(None,other)
     if other.nchannels>self.nchannels:
         return other*self
     if other.nchannels==1:
         if self.nchannels==1:
             return self.compose(None,other.array)
         rgba=list(self.split('RGBA'))
         rgba[-1]=rgba[-1]*other
         return Image(rgba,'RGBA')
     raise NotImplemented('%s * %s'%(self,other))
Пример #15
0
def strftimedelta(t, fmt='%H:%M:%S'):
    """
    :param t: float seconds or timedelta
    """
    if not math2.is_number(t):
        t = t.total_seconds()
    hours, remainder = divmod(t, 3600)
    minutes, seconds = divmod(remainder, 60)
    res = fmt.replace('%H', '%d' % hours)
    res = res.replace('%h', '%d' % hours if hours else '')
    res = res.replace('%M', '%02d' % minutes)
    res = res.replace('%m', '%d' % minutes if minutes else '')
    res = res.replace('%S', '%02d' % seconds)
    return res
Пример #16
0
 def __mul__(self, other):
     if isinstance(other, str):
         return self.colorize('black', other)
     if math2.is_number(other):
         return self.compose(None, other)
     if other.nchannels > self.nchannels:
         return other * self
     if other.nchannels == 1:
         if self.nchannels == 1:
             return self.compose(None, other.array)
         rgba = list(self.split('RGBA'))
         rgba[-1] = rgba[-1] * other
         return Image(rgba, 'RGBA')
     raise NotImplementedError('%s * %s' % (self, other))
Пример #17
0
 def __call__(self, x=None, **kwargs):
     '''evaluate the Expr at x OR compose self(x())'''
     if isinstance(x, Expr):  # composition
         return self.applx(x)
     if itertools2.isiterable(x):
         return [self(x) for x in x]  # return a displayable list
     if x is not None:
         kwargs['x'] = x
     kwargs['self'] = self  # allows to call methods such as in Stats
     self.context.variables = kwargs
     try:
         e = self.context.eval(self.body)
     except TypeError:  # some params remain symbolic
         return self
     except Exception as error:  # ZeroDivisionError, OverflowError
         return None
     if math2.is_number(e):
         return e
     return Expr(e, self.context)
Пример #18
0
 def __call__(self, x=None, **kwargs):
     '''evaluate the Expr at x OR compose self(x())'''
     if isinstance(x, Expr):  # composition
         return self.applx(x)
     if itertools2.isiterable(x):
         return [self(x) for x in x]  # return a displayable list
     if x is not None:
         kwargs['x'] = x
     kwargs['self'] = self  # allows to call methods such as in Stats
     self.context.variables = kwargs
     try:
         e = self.context.eval(self.body)
     except TypeError:  # some params remain symbolic
         return self
     except Exception as error:  # ZeroDivisionError, OverflowError
         return None
     if math2.is_number(e):
         return e
     return Expr(e, self.context)
Пример #19
0
    def __init__(self, init=[], default=0, period=(-math2.inf, +math2.inf)):
        # Note : started by deriving a list of (point,value), but this leads to a problem:
        # the value is taken into account in sort order by bisect
        # so instead of defining one more class with a __cmp__ method, I split both lists
        if math2.is_number(period):
            period = (0, period)
        try:  # copy constructor ?
            self.x = list(init.x)
            self.y = list(init.y)
            self.period = period or init.period  # allow to force periodicity
        except AttributeError:
            self.x = []
            self.y = []
            self.period = period
            self.append(period[0], default)
            self.extend(init)

        # to initialize context and such stuff
        super(Piecewise, self).__init__(0)
        self.body = '?'  # should not happen
Пример #20
0
    def __init__(self, f, context=default_context):
        '''
        :param f: function or operator, Expr to copy construct, or formula string
        '''
        self.context = context

        if isinstance(f, Expr):  # copy constructor
            self.body = f.body
            return
        elif isinstance(f, ast.AST):
            self.body = f
            return
        elif inspect.isfunction(f):
            try:
                f = get_function_source(f)
            except ValueError:
                f = '%s(x)' % f.__name__
        elif isinstance(f, collections.Callable):  # builtin function
            f = '%s(x)' % f.__name__
        elif f in ('True', 'False'):
            f = bool(f == 'True')

        if type(f) is bool:
            self.body = ast.Num(f)
            return

        if type(f) is float:  # try to beautify it
            if math2.is_integer(f):
                f = math2.rint(f)
            else:
                f = plouffe(f)

        if math2.is_number(f):  # store it with full precision
            self.body = ast.Num(f)
            return

        # accept ^ as power operator rather than xor ...
        f = str(f).replace('^', '**')

        self.body = compile(f, 'Expr', 'eval', ast.PyCF_ONLY_AST).body
Пример #21
0
    def __init__(self, f, context=default_context):
        '''
        :param f: function or operator, Expr to copy construct, or formula string
        '''
        self.context = context

        if isinstance(f, Expr):  # copy constructor
            self.body = f.body
            return
        elif isinstance(f, ast.AST):
            self.body = f
            return
        elif inspect.isfunction(f):
            try:
                f = get_function_source(f)
            except ValueError:
                f = '%s(x)' % f.__name__
        elif isinstance(f, collections.Callable):  # builtin function
            f = '%s(x)' % f.__name__
        elif f in ('True', 'False'):
            f = bool(f == 'True')

        if type(f) is bool:
            self.body = ast.Num(f)
            return

        if type(f) is float:  # try to beautify it
            if math2.is_integer(f):
                f = math2.rint(f)
            else:
                f = plouffe(f)

        if math2.is_number(f):  # store it with full precision
            self.body = ast.Num(f)
            return

        f = str(f).replace('^', '**')  # accept ^ as power operator rather than xor ...

        self.body = compile(f, 'Expr', 'eval', ast.PyCF_ONLY_AST).body
Пример #22
0
    def add(self, expr):
        ''' add expr
        :return: bool True if it has been added
        '''

        if expr is None:
            return False
        try:
            k = expr()  # the key is the numeric value of expr
        except (TypeError, ValueError):
            return False

        if not math2.is_number(k):
            return False

        if type(k) is complex:
            return False

        if self.int:
            # limit to integers... but maybe we're extremely close
            k = math2.int_or_float(k)
            if not isinstance(k, int):
                return False

        if k < 0:
            return self.add(-expr)

        if self.max is not None and k > self.max:
            return False

        if k in self:
            if self.improve and expr.complexity() >= self[k].complexity():
                return False

        self[k] = expr
        return True
Пример #23
0
 def isconstant(self):
     ''':return: True if Expr evaluates to a constant number or bool'''
     res = self()
     if math2.is_number(res): return True
     if isinstance(res, bool): return True
     return False