示例#1
0
    def _store(self):

        if not isinstance(self._data, Quantity):
            return super(Brian2Parameter, self)._store()
        else:
            store_dict = {}
            unit = get_unit_fast(self._data)

            value = self._data / unit
            store_dict['data' +
                       Brian2Parameter.IDENTIFIER] = ObjectTable(data={
                           'value': [value],
                           'unit': [repr(unit)]
                       })

            if self.f_has_range():
                value_list = [
                    value_with_unit / unit
                    for value_with_unit in self._explored_range
                ]
                store_dict['explored_data' +
                           Brian2Parameter.IDENTIFIER] = ObjectTable(
                               data={'value': value_list})

            self._locked = True

            return store_dict
示例#2
0
def test_get_unit():
    '''
    Test get_unit and get_unit_fast
    '''
    values = [3 * mV, np.array([1, 2]) * mV, np.arange(12).reshape(4, 3) * mV]
    for value in values:
        assert get_unit(value) == volt
        assert_quantity(get_unit_fast(value), 1, volt)
示例#3
0
def test_get_unit():
    '''
    Test get_unit and get_unit_fast
    '''
    values = [3 * mV, np.array([1, 2]) * mV,
              np.arange(12).reshape(4, 3) * mV]
    for value in values:
        assert get_unit(value) == volt
        assert_quantity(get_unit_fast(value), 1, volt)
示例#4
0
def test_get_unit():
    '''
    Test get_unit and get_unit_fast
    '''
    values = [3 * mV, np.array([1, 2]) * mV,
              np.arange(12).reshape(4, 3) * mV]
    for value in values:
        unit = get_unit(value)
        assert isinstance(unit, Unit)
        assert unit == volt
        assert_quantity(get_unit_fast(value), 1, volt)

    values = [3 * amp/metre**2, np.array([1, 2]) * amp/metre**2,
              np.arange(12).reshape(4, 3) * amp/metre**2]
    for value in values:
        unit = get_unit(value)
        assert isinstance(unit, Unit)
        assert unit == amp/metre**2
        assert float(unit) == 1.
        assert_quantity(get_unit_fast(value), 1, amp/metre**2)
示例#5
0
def test_get_unit():
    '''
    Test get_unit and get_unit_fast
    '''
    values = [3 * mV, np.array([1, 2]) * mV,
              np.arange(12).reshape(4, 3) * mV]
    for value in values:
        unit = get_unit(value)
        assert isinstance(unit, Unit)
        assert unit == volt
        assert_quantity(get_unit_fast(value), 1, volt)

    values = [3 * amp/metre**2, np.array([1, 2]) * amp/metre**2,
              np.arange(12).reshape(4, 3) * amp/metre**2]
    for value in values:
        unit = get_unit(value)
        assert isinstance(unit, Unit)
        assert unit == amp/metre**2
        assert float(unit) == 1.
        assert_quantity(get_unit_fast(value), 1, amp/metre**2)
示例#6
0
    def _store(self):

        if not isinstance(self._data, Quantity):
            return super(Brian2Parameter, self)._store()
        else:
            store_dict = {}
            unit = get_unit_fast(self._data)

            value = self._data/unit
            store_dict['data' + Brian2Parameter.IDENTIFIER] = ObjectTable(data={'value': [value], 'unit': [repr(unit)]})

            if self.f_has_range():
                value_list = [value_with_unit/unit for value_with_unit in self._explored_range]
                store_dict['explored_data' + Brian2Parameter.IDENTIFIER] = ObjectTable(data={'value': value_list})

            self._locked = True

            return store_dict
示例#7
0
def unit_from_expression(expr):
    """Takes a unit string like ``'1. * volt'`` and returns the BRIAN2 unit."""
    if expr == '1':
        return get_unit_fast(1)
    elif isinstance(expr, str):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
        return unit_from_expression(expr)
    elif expr.__class__ is ast.Name:
        return ALLUNITS[expr.id]
    elif expr.__class__ is ast.Num:
        return expr.n
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        operand = unit_from_expression(expr.operand)
        if op == 'USub':
            return -operand
        else:
            raise SyntaxError("Unsupported operation " + op)
    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = unit_from_expression(expr.left)
        right = unit_from_expression(expr.right)
        if op == 'Add':
            u = left + right
        elif op == 'Sub':
            u = left - right
        elif op == 'Mult':
            u = left * right
        elif op == 'Div':
            u = left / right
        elif op == 'Pow':
            n = unit_from_expression(expr.right)
            u = left**n
        elif op == 'Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    else:
        raise RuntimeError('You shall not pass')
示例#8
0
def unit_from_expression(expr):
    """Takes a unit string like ``'1. * volt'`` and returns the BRIAN2 unit."""
    if expr == '1':
        return get_unit_fast(1)
    elif isinstance(expr, compat.base_type):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
        return unit_from_expression(expr)
    elif expr.__class__ is ast.Name:
        return ALLUNITS[expr.id]
    elif expr.__class__ is ast.Num:
        return expr.n
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        operand = unit_from_expression(expr.operand)
        if op=='USub':
            return -operand
        else:
            raise SyntaxError("Unsupported operation "+op)
    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = unit_from_expression(expr.left)
        right = unit_from_expression(expr.right)
        if op=='Add':
            u = left+right
        elif op=='Sub':
            u = left-right
        elif op=='Mult':
            u = left*right
        elif op=='Div':
            u = left/right
        elif op=='Pow':
            n = unit_from_expression(expr.right)
            u = left**n
        elif op=='Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation "+op)
        return u
    else:
        raise RuntimeError('You shall not pass')
示例#9
0
    def _store(self):

        store_dict = {}

        for key in self._data:
            val = self._data[key]
            if isinstance(val, Quantity):
                unit = get_unit_fast(val)
                value = val / unit
                # Potentially the results are very big in contrast to small parameters
                # Accordingly, an ObjectTable might not be the best choice after all for a result
                if isinstance(val, np.ndarray) and len(val.shape) == 0:
                    # Convert 0-dimensional arrays to regular numpy floats
                    value = np.float(value)
                store_dict[key + Brian2Result.IDENTIFIER + 'value'] = value
                store_dict[key + Brian2Result.IDENTIFIER + 'unit'] = repr(unit)

            else:
                store_dict[key] = val

        return store_dict
示例#10
0
    def _store(self):

        store_dict = {}

        for key in self._data:
            val = self._data[key]
            if isinstance(val, Quantity):
                unit = get_unit_fast(val)
                value = val/unit
                # Potentially the results are very big in contrast to small parameters
                # Accordingly, an ObjectTable might not be the best choice after all for a result
                if isinstance(val, np.ndarray) and len(val.shape) == 0:
                    # Convert 0-dimensional arrays to regular numpy floats
                    value = np.float(value)
                store_dict[key + Brian2Result.IDENTIFIER + 'value'] = value
                store_dict[key + Brian2Result.IDENTIFIER + 'unit'] = repr(unit)

            else:
                store_dict[key] = val

        return store_dict
示例#11
0
def parse_expression_unit(expr, namespace, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    namespace : dict-like
        The namespace of external variables.
    variables : dict of `Variable` objects
        The information about the internal variables
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
        
    Notes
    -----
    
    Currently, functions do not work, see comments in function.
    '''
    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is ast.Name:
        name = expr.id
        if name in variables:
            return variables[name].unit
        elif name in namespace:
            return get_unit_fast(namespace[name])
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise ValueError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, namespace, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left] + expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, namespace, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError(
                    "Comparison of expressions with different units",
                    *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif expr.starargs is not None:
            raise ValueError("Variable number of arguments not supported")
        elif expr.kwargs is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [
            parse_expression_unit(arg, namespace, variables)
            for arg in expr.args
        ]

        func = namespace.get(expr.func.id, variables.get(expr.func, None))
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(
                func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(
                    ('Argument number %d for function '
                     '%s does not have the correct '
                     'units' % (idx + 1, expr.func.id)), arg_unit,
                    func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, namespace, variables)
        right = parse_expression_unit(expr.right, namespace, variables)
        if op == 'Add' or op == 'Sub':
            u = left + right
        elif op == 'Mult':
            u = left * right
        elif op == 'Div':
            u = left / right
        elif op == 'Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(
                    right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, namespace, variables)
            u = left**n
        elif op == 'Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, namespace, variables)
        if op == 'Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#12
0
def parse_expression_unit(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node    
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return Unit(1)
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        if name in variables:
            return variables[name].unit
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left]+expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError("Comparison of expressions with different units",
                                             *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif expr.starargs is not None:
            raise ValueError("Variable number of arguments not supported")
        elif expr.kwargs is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [parse_expression_unit(arg, variables)
                     for arg in expr.args]

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(('Argument number %d for function '
                                              '%s does not have the correct '
                                              'units' % (idx + 1, expr.func.id)),
                                             arg_unit, func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, variables)
        right = parse_expression_unit(expr.right, variables)
        if op=='Add' or op=='Sub':
            u = left+right
        elif op=='Mult':
            u = left*right
        elif op=='Div':
            u = left/right
        elif op=='Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, variables)
            u = left**n
        elif op=='Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation "+op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, variables)
        if op=='Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#13
0
def parse_expression_unit(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return Unit(1)
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        # Raise an error if a function is called as if it were a variable
        # (most of the time this happens for a TimedArray)
        if name in variables and isinstance(variables[name], Function):
            raise SyntaxError(
                '%s was used like a variable/constant, but it is '
                'a function.' % name)
        if name in variables:
            return variables[name].unit
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left] + expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                raise DimensionMismatchError(
                    "Comparison of expressions with different units",
                    *[getattr(u, 'dim', 1) for u in subunits])
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif getattr(expr, 'starargs', None) is not None:
            raise ValueError("Variable number of arguments not supported")
        elif getattr(expr, 'kwargs', None) is not None:
            raise ValueError("Keyword arguments not supported")

        arg_units = [
            parse_expression_unit(arg, variables) for arg in expr.args
        ]

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(
                func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        if len(func._arg_units) != len(arg_units):
            raise SyntaxError(
                'Function %s was called with %d parameters, '
                'needs %d.' %
                (expr.func.id, len(arg_units), len(func._arg_units)))
        for idx, arg_unit in enumerate(arg_units):
            # A "None" in func._arg_units means: No matter what unit
            if (func._arg_units[idx] is not None and
                    not have_same_dimensions(arg_unit, func._arg_units[idx])):
                raise DimensionMismatchError(
                    ('Argument number %d for function '
                     '%s does not have the correct '
                     'units' % (idx + 1, expr.func.id)), arg_unit,
                    func._arg_units[idx])

        if isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, variables)
        right = parse_expression_unit(expr.right, variables)
        if op == 'Add' or op == 'Sub':
            u = left + right
        elif op == 'Mult':
            u = left * right
        elif op == 'Div':
            u = left / right
        elif op == 'Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(
                    right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, variables)
            u = left**n
        elif op == 'Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation " + op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, variables)
        if op == 'Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))
示例#14
0
def parse_expression_unit(expr, variables):
    '''
    Returns the unit value of an expression, and checks its validity
    
    Parameters
    ----------
    expr : str
        The expression to check.
    variables : dict
        Dictionary of all variables used in the `expr` (including `Constant`
        objects for external variables)
    
    Returns
    -------
    unit : Quantity
        The output unit of the expression
    
    Raises
    ------
    SyntaxError
        If the expression cannot be parsed, or if it uses ``a**b`` for ``b``
        anything other than a constant number.
    DimensionMismatchError
        If any part of the expression is dimensionally inconsistent.
    '''

    # If we are working on a string, convert to the top level node    
    if isinstance(expr, basestring):
        mod = ast.parse(expr, mode='eval')
        expr = mod.body
    if expr.__class__ is getattr(ast, 'NameConstant', None):
        # new class for True, False, None in Python 3.4
        value = expr.value
        if value is True or value is False:
            return Unit(1)
        else:
            raise ValueError('Do not know how to handle value %s' % value)
    if expr.__class__ is ast.Name:
        name = expr.id
        # Raise an error if a function is called as if it were a variable
        # (most of the time this happens for a TimedArray)
        if name in variables and isinstance(variables[name], Function):
            raise SyntaxError('%s was used like a variable/constant, but it is '
                              'a function.' % name)
        if name in variables:
            return variables[name].unit
        elif name in ['True', 'False']:
            return Unit(1)
        else:
            raise KeyError('Unknown identifier %s' % name)
    elif expr.__class__ is ast.Num:
        return get_unit_fast(1)
    elif expr.__class__ is ast.BoolOp:
        # check that the units are valid in each subexpression
        for node in expr.values:
            parse_expression_unit(node, variables)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Compare:
        # check that the units are consistent in each subexpression
        subexprs = [expr.left]+expr.comparators
        subunits = []
        for node in subexprs:
            subunits.append(parse_expression_unit(node, variables))
        for left, right in zip(subunits[:-1], subunits[1:]):
            if not have_same_dimensions(left, right):
                msg = ('Comparison of expressions with different units. Expression '
                       '"{}" has unit ({}), while expression "{}" has units ({})').format(
                            NodeRenderer().render_node(expr.left), get_dimensions(left),
                            NodeRenderer().render_node(expr.comparators[0]), get_dimensions(right))
                raise DimensionMismatchError(msg)
        # but the result is a bool, so we just return 1 as the unit
        return get_unit_fast(1)
    elif expr.__class__ is ast.Call:
        if len(expr.keywords):
            raise ValueError("Keyword arguments not supported.")
        elif getattr(expr, 'starargs', None) is not None:
            raise ValueError("Variable number of arguments not supported")
        elif getattr(expr, 'kwargs', None) is not None:
            raise ValueError("Keyword arguments not supported")

        func = variables.get(expr.func.id, None)
        if func is None:
            raise SyntaxError('Unknown function %s' % expr.func.id)
        if not hasattr(func, '_arg_units') or not hasattr(func, '_return_unit'):
            raise ValueError(('Function %s does not specify how it '
                              'deals with units.') % expr.func.id)

        if len(func._arg_units) != len(expr.args):
            raise SyntaxError('Function %s was called with %d parameters, '
                              'needs %d.' % (expr.func.id,
                                             len(expr.args),
                                             len(func._arg_units)))

        for idx, (arg, expected_unit) in enumerate(zip(expr.args,
                                                       func._arg_units)):
            # A "None" in func._arg_units means: No matter what unit
            if expected_unit is None:
                continue
            elif expected_unit == bool:
                if not is_boolean_expression(arg, variables):
                    raise TypeError(('Argument number %d for function %s was '
                                     'expected to be a boolean value, but is '
                                     '"%s".') % (idx + 1, expr.func.id,
                                                 NodeRenderer().render_node(arg)))
            else:
                arg_unit = parse_expression_unit(arg, variables)
                if not have_same_dimensions(arg_unit, expected_unit):
                    msg = ('Argument number {} for function {} does not have the '
                           'correct units. Expression "{}" has units ({}), but '
                           'should be ({}).').format(
                        idx+1, expr.func.id,
                        NodeRenderer().render_node(arg),
                        get_dimensions(arg_unit), get_dimensions(expected_unit))
                    raise DimensionMismatchError(msg)

        if func._return_unit == bool:
            return Unit(1)
        elif isinstance(func._return_unit, (Unit, int)):
            # Function always returns the same unit
            return get_unit_fast(func._return_unit)
        else:
            # Function returns a unit that depends on the arguments
            arg_units = [parse_expression_unit(arg, variables)
                         for arg in expr.args]
            return func._return_unit(*arg_units)

    elif expr.__class__ is ast.BinOp:
        op = expr.op.__class__.__name__
        left = parse_expression_unit(expr.left, variables)
        right = parse_expression_unit(expr.right, variables)
        if op=='Add' or op=='Sub':
            u = left+right
        elif op=='Mult':
            u = left*right
        elif op=='Div':
            u = left/right
        elif op=='Pow':
            if have_same_dimensions(left, 1) and have_same_dimensions(right, 1):
                return get_unit_fast(1)
            n = _get_value_from_expression(expr.right, variables)
            u = left**n
        elif op=='Mod':
            u = left % right
        else:
            raise SyntaxError("Unsupported operation "+op)
        return u
    elif expr.__class__ is ast.UnaryOp:
        op = expr.op.__class__.__name__
        # check validity of operand and get its unit
        u = parse_expression_unit(expr.operand, variables)
        if op=='Not':
            return get_unit_fast(1)
        else:
            return u
    else:
        raise SyntaxError('Unsupported operation ' + str(expr.__class__))