Esempio n. 1
0
 def translate_statement(self, statement):
     # TODO: optimisation, translate arithmetic to a sequence of inplace
     # operations like a=b+c -> add(b, c, a)
     var, op, expr, comment = (statement.var, statement.op, statement.expr,
                               statement.comment)
     origop = op
     if op == ':=':
         op = '='
     # For numpy we replace complex expressions involving a single boolean variable into a
     # where(boolvar, expr_if_true, expr_if_false)
     if (statement.used_boolean_variables is not None
             and len(statement.used_boolean_variables) == 1
             and brian_dtype_from_dtype(statement.dtype) == 'float'
             and statement.complexity_std > sum(
                 statement.complexities.values())):
         used_boolvars = statement.used_boolean_variables
         bool_simp = statement.boolean_simplified_expressions
         boolvar = used_boolvars[0]
         for bool_assigns, simp_expr in bool_simp.items():
             _, boolval = bool_assigns[0]
             if boolval:
                 expr_true = simp_expr
             else:
                 expr_false = simp_expr
         code = '{var} {op} _numpy.where({boolvar}, {expr_true}, {expr_false})'.format(
             var=var,
             op=op,
             boolvar=boolvar,
             expr_true=expr_true,
             expr_false=expr_false)
     else:
         code = var + ' ' + op + ' ' + self.translate_expression(expr)
     if len(comment):
         code += ' # ' + comment
     return code
Esempio n. 2
0
 def translate_statement(self, statement):
     # TODO: optimisation, translate arithmetic to a sequence of inplace
     # operations like a=b+c -> add(b, c, a)
     var, op, expr, comment = (statement.var, statement.op,
                               statement.expr, statement.comment)
     origop = op
     if op == ':=':
         op = '='
     # For numpy we replace complex expressions involving a single boolean variable into a
     # where(boolvar, expr_if_true, expr_if_false)
     if (statement.used_boolean_variables is not None and len(statement.used_boolean_variables)==1
             and brian_dtype_from_dtype(statement.dtype)=='float'
             and statement.complexity_std>sum(statement.complexities.values())):
         used_boolvars = statement.used_boolean_variables
         bool_simp = statement.boolean_simplified_expressions
         boolvar = used_boolvars[0]
         for bool_assigns, simp_expr in bool_simp.iteritems():
             _, boolval = bool_assigns[0]
             if boolval:
                 expr_true = simp_expr
             else:
                 expr_false = simp_expr
         code = '{var} {op} _numpy.where({boolvar}, {expr_true}, {expr_false})'.format(
                     var=var, op=op, boolvar=boolvar, expr_true=expr_true, expr_false=expr_false)
     else:
         code = var + ' ' + op + ' ' + self.translate_expression(expr)
     if len(comment):
         code += ' # ' + comment
     return code
Esempio n. 3
0
 def translate_statement(self, statement):
     var, op, expr, comment = (statement.var, statement.op, statement.expr,
                               statement.comment)
     if op == ':=':  # make no distinction in Cython (declaration are done elsewhere)
         op = '='
     # For Cython we replace complex expressions involving boolean variables into a sequence of
     # if/then expressions with simpler expressions. This is provided by the optimise_statements
     # function.
     if (statement.used_boolean_variables is not None
             and len(statement.used_boolean_variables)
             # todo: improve dtype analysis so that this isn't necessary
             and brian_dtype_from_dtype(statement.dtype) == 'float'):
         used_boolvars = statement.used_boolean_variables
         bool_simp = statement.boolean_simplified_expressions
         codelines = []
         firstline = True
         # bool assigns is a sequence of (var, value) pairs giving the conditions under
         # which the simplified expression simp_expr holds
         for bool_assigns, simp_expr in bool_simp.iteritems():
             # generate a boolean expression like ``var1 and var2 and not var3``
             atomics = []
             for boolvar, boolval in bool_assigns:
                 if boolval:
                     atomics.append(boolvar)
                 else:
                     atomics.append('not ' + boolvar)
             # use if/else/elif correctly
             if firstline:
                 line = 'if ' + (' and '.join(atomics)) + ':'
             else:
                 if len(used_boolvars) > 1:
                     line = 'elif ' + (' and '.join(atomics)) + ':'
                 else:
                     line = 'else:'
             line += '\n    '
             line += var + ' ' + op + ' ' + self.translate_expression(
                 simp_expr)
             codelines.append(line)
             firstline = False
         code = '\n'.join(codelines)
     else:
         code = var + ' ' + op + ' ' + self.translate_expression(expr)
     if len(comment):
         code += ' # ' + comment
     return code
Esempio n. 4
0
 def translate_statement(self, statement):
     var, op, expr, comment = (statement.var, statement.op,
                               statement.expr, statement.comment)
     if op == ':=': # make no distinction in Cython (declaration are done elsewhere)
         op = '='
     # For Cython we replace complex expressions involving boolean variables into a sequence of
     # if/then expressions with simpler expressions. This is provided by the optimise_statements
     # function.
     if (statement.used_boolean_variables is not None and len(statement.used_boolean_variables)
             # todo: improve dtype analysis so that this isn't necessary
             and brian_dtype_from_dtype(statement.dtype)=='float'):
         used_boolvars = statement.used_boolean_variables
         bool_simp = statement.boolean_simplified_expressions
         codelines = []
         firstline = True
         # bool assigns is a sequence of (var, value) pairs giving the conditions under
         # which the simplified expression simp_expr holds
         for bool_assigns, simp_expr in bool_simp.iteritems():
             # generate a boolean expression like ``var1 and var2 and not var3``
             atomics = []
             for boolvar, boolval in bool_assigns:
                 if boolval:
                     atomics.append(boolvar)
                 else:
                     atomics.append('not '+boolvar)
             # use if/else/elif correctly
             if firstline:
                 line = 'if '+(' and '.join(atomics))+':'
             else:
                 if len(used_boolvars)>1:
                     line = 'elif '+(' and '.join(atomics))+':'
                 else:
                     line = 'else:'
             line += '\n    '
             line += var + ' ' + op + ' ' + self.translate_expression(simp_expr)
             codelines.append(line)
             firstline = False
         code = '\n'.join(codelines)
     else:
         code = var + ' ' + op + ' ' + self.translate_expression(expr)
     if len(comment):
         code += ' # ' + comment
     return code
Esempio n. 5
0
def optimise_statements(scalar_statements, vector_statements, variables, blockname=''):
    '''
    Optimise a sequence of scalar and vector statements

    Performs the following optimisations:

    1. Constant evaluations (e.g. exp(0) to 1). See `evaluate_expr`.
    2. Arithmetic simplifications (e.g. 0*x to 0). See `ArithmeticSimplifier`, `collect`.
    3. Pulling out loop invariants (e.g. v*exp(-dt/tau) to a=exp(-dt/tau) outside the loop and v*a inside).
       See `Simplifier`.
    4. Boolean simplifications (allowing the replacement of expressions with booleans with a sequence of if/thens).
       See `Simplifier`.

    Parameters
    ----------
    scalar_statements : sequence of Statement
        Statements that only involve scalar values and should be evaluated in the scalar block.
    vector_statements : sequence of Statement
        Statements that involve vector values and should be evaluated in the vector block.
    variables : dict of (str, Variable)
        Definition of the types of the variables.
    blockname : str, optional
        Name of the block (used for LIO constant prefixes to avoid name clashes)

    Returns
    -------
    new_scalar_statements : sequence of Statement
        As above but with loop invariants pulled out from vector statements
    new_vector_statements : sequence of Statement
        Simplified/optimised versions of statements
    '''
    boolvars = dict((k, v) for k, v in variables.iteritems()
                    if hasattr(v, 'dtype') and brian_dtype_from_dtype(v.dtype)=='boolean')
    # We use the Simplifier class by rendering each expression, which generates new scalar statements
    # stored in the Simplifier object, and these are then added to the scalar statements.
    simplifier = Simplifier(variables, scalar_statements, extra_lio_prefix=blockname)
    new_vector_statements = []
    for stmt in vector_statements:
        # Carry out constant evaluation, arithmetic simplification and loop invariants
        new_expr = simplifier.render_expr(stmt.expr)
        new_stmt = Statement(stmt.var, stmt.op, new_expr, stmt.comment,
                             dtype=stmt.dtype,
                             constant=stmt.constant,
                             subexpression=stmt.subexpression,
                             scalar=stmt.scalar)
        # Now check if boolean simplification can be carried out
        complexity_std = expression_complexity(new_expr, simplifier.variables)
        idents = get_identifiers(new_expr)
        used_boolvars = [var for var in boolvars.iterkeys() if var in idents]
        if len(used_boolvars):
            # We want to iterate over all the possible assignments of boolean variables to values in (True, False)
            bool_space = [[False, True] for var in used_boolvars]
            expanded_expressions = {}
            complexities = {}
            for bool_vals in itertools.product(*bool_space):
                # substitute those values into the expr and simplify (including potentially pulling out new
                # loop invariants)
                subs = dict((var, str(val)) for var, val in zip(used_boolvars, bool_vals))
                curexpr = word_substitute(new_expr, subs)
                curexpr = simplifier.render_expr(curexpr)
                key = tuple((var, val) for var, val in zip(used_boolvars, bool_vals))
                expanded_expressions[key] = curexpr
                complexities[key] = expression_complexity(curexpr, simplifier.variables)
            # See Statement for details on these
            new_stmt.used_boolean_variables = used_boolvars
            new_stmt.boolean_simplified_expressions = expanded_expressions
            new_stmt.complexity_std = complexity_std
            new_stmt.complexities = complexities
        new_vector_statements.append(new_stmt)
    # Generate additional scalar statements for the loop invariants
    new_scalar_statements = copy.copy(scalar_statements)
    for expr, name in simplifier.loop_invariants.iteritems():
        dtype_name = simplifier.loop_invariant_dtypes[name]
        if dtype_name=='boolean':
            dtype = bool
        elif dtype_name=='integer':
            dtype = int
        else:
            dtype = float
        new_stmt = Statement(name, ':=', expr, '',
                             dtype=dtype,
                             constant=True,
                             subexpression=False,
                             scalar=True)
        new_scalar_statements.append(new_stmt)
    return new_scalar_statements, new_vector_statements
Esempio n. 6
0
def optimise_statements(scalar_statements,
                        vector_statements,
                        variables,
                        blockname=''):
    '''
    Optimise a sequence of scalar and vector statements

    Performs the following optimisations:

    1. Constant evaluations (e.g. exp(0) to 1). See `evaluate_expr`.
    2. Arithmetic simplifications (e.g. 0*x to 0). See `ArithmeticSimplifier`, `collect`.
    3. Pulling out loop invariants (e.g. v*exp(-dt/tau) to a=exp(-dt/tau) outside the loop and v*a inside).
       See `Simplifier`.
    4. Boolean simplifications (allowing the replacement of expressions with booleans with a sequence of if/thens).
       See `Simplifier`.

    Parameters
    ----------
    scalar_statements : sequence of Statement
        Statements that only involve scalar values and should be evaluated in the scalar block.
    vector_statements : sequence of Statement
        Statements that involve vector values and should be evaluated in the vector block.
    variables : dict of (str, Variable)
        Definition of the types of the variables.
    blockname : str, optional
        Name of the block (used for LIO constant prefixes to avoid name clashes)

    Returns
    -------
    new_scalar_statements : sequence of Statement
        As above but with loop invariants pulled out from vector statements
    new_vector_statements : sequence of Statement
        Simplified/optimised versions of statements
    '''
    boolvars = dict((k, v) for k, v in variables.items()
                    if hasattr(v, 'dtype')
                    and brian_dtype_from_dtype(v.dtype) == 'boolean')
    # We use the Simplifier class by rendering each expression, which generates new scalar statements
    # stored in the Simplifier object, and these are then added to the scalar statements.
    simplifier = Simplifier(variables,
                            scalar_statements,
                            extra_lio_prefix=blockname)
    new_vector_statements = []
    for stmt in vector_statements:
        # Carry out constant evaluation, arithmetic simplification and loop invariants
        new_expr = simplifier.render_expr(stmt.expr)
        new_stmt = Statement(stmt.var,
                             stmt.op,
                             new_expr,
                             stmt.comment,
                             dtype=stmt.dtype,
                             constant=stmt.constant,
                             subexpression=stmt.subexpression,
                             scalar=stmt.scalar)
        # Now check if boolean simplification can be carried out
        complexity_std = expression_complexity(new_expr, simplifier.variables)
        idents = get_identifiers(new_expr)
        used_boolvars = [var for var in boolvars if var in idents]
        if len(used_boolvars):
            # We want to iterate over all the possible assignments of boolean variables to values in (True, False)
            bool_space = [[False, True] for var in used_boolvars]
            expanded_expressions = {}
            complexities = {}
            for bool_vals in itertools.product(*bool_space):
                # substitute those values into the expr and simplify (including potentially pulling out new
                # loop invariants)
                subs = dict((var, str(val))
                            for var, val in zip(used_boolvars, bool_vals))
                curexpr = word_substitute(new_expr, subs)
                curexpr = simplifier.render_expr(curexpr)
                key = tuple(
                    (var, val) for var, val in zip(used_boolvars, bool_vals))
                expanded_expressions[key] = curexpr
                complexities[key] = expression_complexity(
                    curexpr, simplifier.variables)
            # See Statement for details on these
            new_stmt.used_boolean_variables = used_boolvars
            new_stmt.boolean_simplified_expressions = expanded_expressions
            new_stmt.complexity_std = complexity_std
            new_stmt.complexities = complexities
        new_vector_statements.append(new_stmt)
    # Generate additional scalar statements for the loop invariants
    new_scalar_statements = copy.copy(scalar_statements)
    for expr, name in simplifier.loop_invariants.items():
        dtype_name = simplifier.loop_invariant_dtypes[name]
        if dtype_name == 'boolean':
            dtype = bool
        elif dtype_name == 'integer':
            dtype = int
        else:
            dtype = prefs.core.default_float_dtype
        new_stmt = Statement(name,
                             ':=',
                             expr,
                             '',
                             dtype=dtype,
                             constant=True,
                             subexpression=False,
                             scalar=True)
        new_scalar_statements.append(new_stmt)
    return new_scalar_statements, new_vector_statements