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
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)
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)
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
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')
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')
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
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
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__))
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__))
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__))
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__))