def extract_abstract_code_functions(code):
    '''
    Returns a set of abstract code functions from function definitions.
    
    Returns all functions defined at the top level and ignores any other
    code in the string.
    
    Parameters
    ----------
    code : str
        The code string defining some functions.
        
    Returns
    -------
    funcs : dict
        A mapping ``(name, func)`` for ``func`` an `AbstractCodeFunction`.
    '''
    code = deindent(code)
    nodes = ast.parse(code, mode='exec').body
    funcs = {}
    for node in nodes:
        if node.__class__ is ast.FunctionDef:
            func = abstract_code_from_function(node)
            funcs[func.name] = func
    return funcs
Example #2
0
 def __init__(self):
     self.prefs = {}
     self.backup_prefs = {}
     self.prefs_unvalidated = {}
     self.pref_register = {}
     self.eval_namespace = {}
     exec(
         deindent('''
         from numpy import *
         from angela2.units import *            
         from angela2.units.stdunits import *
         '''), self.eval_namespace)
Example #3
0
 def _as_pref_file(self, valuefunc):
     '''
     Helper function used to generate the preference file for the default or current preference values.
     '''
     s = ''
     for basename, (prefdefs, basedoc) in self.pref_register.items():
         s += '#' + '-' * 79 + '\n'
         s += '\n'.join([
             '# ' + line for line in deindent(
                 basedoc, docstring=True).strip().split('\n')
         ]) + '\n'
         s += '#' + '-' * 79 + '\n\n'
         s += '[' + basename + ']\n\n'
         for name in sorted(prefdefs.keys()):
             pref = prefdefs[name]
             s += '\n'.join([
                 '# ' + line for line in deindent(
                     pref.docs, docstring=True).strip().split('\n')
             ]) + '\n\n'
             s += name + ' = ' + pref.representor(
                 valuefunc(pref, basename + '.' + name)) + '\n\n'
     return s
def abstract_code_from_function(func):
    '''
    Converts the body of the function to abstract code
    
    Parameters
    ----------
    func : function, str or ast.FunctionDef
        The function object to convert. Note that the arguments to the
        function are ignored.
        
    Returns
    -------
    func : AbstractCodeFunction
        The corresponding abstract code function
        
    Raises
    ------
    SyntaxError
        If unsupported features are used such as if statements or indexing.
    '''
    if callable(func):
        code = deindent(inspect.getsource(func))
        funcnode = ast.parse(code, mode='exec').body[0]
    elif isinstance(func, str):
        funcnode = ast.parse(func, mode='exec').body[0]
    elif func.__class__ is ast.FunctionDef:
        funcnode = func
    else:
        raise TypeError("Unsupported function type")

    if funcnode.args.vararg is not None:
        raise SyntaxError("No support for variable number of arguments")
    if funcnode.args.kwarg is not None:
        raise SyntaxError("No support for arbitrary keyword arguments")
    if len(funcnode.args.defaults):
        raise SyntaxError("No support for default values in functions")

    nodes = funcnode.body
    nr = NodeRenderer()
    lines = []
    return_expr = None
    for node in nodes:
        if node.__class__ is ast.Return:
            return_expr = nr.render_node(node.value)
            break
        else:
            lines.append(nr.render_node(node))
    abstract_code = '\n'.join(lines)
    args = [arg.arg for arg in funcnode.args.args]
    name = funcnode.name
    return AbstractCodeFunction(name, args, abstract_code, return_expr)
Example #5
0
    def _get_one_documentation(self, basename, link_targets):
        '''
        Document a single category of preferences.
        '''

        s = ''
        if not basename in self.pref_register:
            raise ValueError(
                'No preferences under the name "%s" are registered' % basename)
        prefdefs, basedoc = self.pref_register[basename]
        s += deindent(basedoc, docstring=True).strip() + '\n\n'
        for name in sorted(prefdefs.keys()):
            pref = prefdefs[name]
            name = basename + '.' + name
            linkname = name.replace('_', '-').replace('.', '-')
            if link_targets:
                # Make a link target
                s += '.. _angela-pref-{name}:\n\n'.format(name=linkname)
            s += '``{name}`` = ``{default}``\n'.format(
                name=name, default=pref.representor(pref.default))
            s += indent(deindent(pref.docs, docstring=True))
            s += '\n\n'
        return s
    def _add_user_function(self, varname, var, added):
        user_functions = []
        load_namespace = []
        support_code = []
        impl = var.implementations[self.codeobj_class]
        if (impl.name, var) in added:
            return  # nothing to do
        else:
            added.add((impl.name, var))
        func_code = impl.get_code(self.owner)
        # Implementation can be None if the function is already
        # available in Cython (possibly under a different name)
        if func_code is not None:
            if isinstance(func_code, str):
                # Function is provided as Cython code
                # To make namespace variables available to functions, we
                # create global variables and assign to them in the main
                # code
                user_functions.append((varname, var))
                func_namespace = impl.get_namespace(self.owner) or {}
                for ns_key, ns_value in func_namespace.items():
                    load_namespace.append(
                        '# namespace for function %s' % varname)
                    if hasattr(ns_value, 'dtype'):
                        if ns_value.shape == ():
                            raise NotImplementedError((
                            'Directly replace scalar values in the function '
                            'instead of providing them via the namespace'))
                        newlines = [
                            "global _namespace{var_name}",
                            "global _namespace_num{var_name}",
                            "cdef _numpy.ndarray[{cpp_dtype}, ndim=1, mode='c'] _buf_{var_name} = _namespace['{var_name}']",
                            "_namespace{var_name} = <{cpp_dtype} *> _buf_{var_name}.data",
                            "_namespace_num{var_name} = len(_namespace['{var_name}'])"
                        ]
                        support_code.append(
                            "cdef {cpp_dtype} *_namespace{var_name}".format(
                                cpp_dtype=get_cpp_dtype(ns_value.dtype),
                                var_name=ns_key))

                    else:  # e.g. a function
                        newlines = [
                            "_namespace{var_name} = namespace['{var_name}']"
                        ]
                    for line in newlines:
                        load_namespace.append(
                            line.format(cpp_dtype=get_cpp_dtype(ns_value.dtype),
                                        numpy_dtype=get_numpy_dtype(
                                            ns_value.dtype),
                                        var_name=ns_key))
                # Rename references to any dependencies if necessary
                for dep_name, dep in impl.dependencies.items():
                    dep_impl = dep.implementations[self.codeobj_class]
                    dep_impl_name = dep_impl.name
                    if dep_impl_name is None:
                        dep_impl_name = dep.pyfunc.__name__
                    if dep_name != dep_impl_name:
                        func_code = word_substitute(func_code,
                                                    {dep_name: dep_impl_name})
                support_code.append(deindent(func_code))
            elif callable(func_code):
                self.variables[varname] = func_code
                line = '{0} = _namespace["{1}"]'.format(varname, varname)
                load_namespace.append(line)
            else:
                raise TypeError(('Provided function implementation '
                                 'for function %s is neither a string '
                                 'nor callable (is type %s instead)') % (
                                varname,
                                type(func_code)))

        dep_support_code = []
        dep_load_namespace = []
        dep_user_functions = []
        if impl.dependencies is not None:
            for dep_name, dep in impl.dependencies.items():
                if dep_name not in self.variables:
                    self.variables[dep_name] = dep
                    user_func = self._add_user_function(dep_name, dep, added)
                    if user_func is not None:
                        sc, ln, uf = user_func
                        dep_support_code.extend(sc)
                        dep_load_namespace.extend(ln)
                        dep_user_functions.extend(uf)

        return (support_code + dep_support_code,
                dep_load_namespace + load_namespace,
                dep_user_functions + user_functions)
    def create_extension(
        self,
        code,
        force=False,
        name=None,
        define_macros=None,
        include_dirs=None,
        library_dirs=None,
        runtime_library_dirs=None,
        extra_compile_args=None,
        extra_link_args=None,
        libraries=None,
        compiler=None,
        sources=None,
        owner_name='',
    ):
        if sources is None:
            sources = []
        self._simplify_paths()

        if Cython is None:
            raise ImportError('Cython is not available')

        code = deindent(code)

        lib_dir = get_cython_cache_dir()
        if '~' in lib_dir:
            lib_dir = os.path.expanduser(lib_dir)
        try:
            os.makedirs(lib_dir)
        except OSError:
            if not os.path.exists(lib_dir):
                raise IOError(
                    "Couldn't create Cython cache directory '%s', try setting the "
                    "cache directly with prefs.codegen.runtime.cython.cache_dir."
                    % lib_dir)

        numpy_version = '.'.join(
            numpy.__version__.split('.')[:2])  # Only use major.minor version
        key = code, sys.version_info, sys.executable, Cython.__version__, numpy_version

        if force:
            # Force a new module name by adding the current time to the
            # key which is hashed to determine the module name.
            key += time.time(),

        if key in self._code_cache:
            return self._code_cache[key]

        if name is not None:
            module_name = name  #py3compat.unicode_to_str(args.name)
        else:
            module_name = "_cython_magic_" + hashlib.md5(
                str(key).encode('utf-8')).hexdigest()
        if owner_name:
            logger.diagnostic(
                '"{owner_name}" using Cython module "{module_name}"'.format(
                    owner_name=owner_name, module_name=module_name))

        module_path = os.path.join(lib_dir, module_name + self.so_ext)

        if prefs['codegen.runtime.cython.multiprocess_safe']:
            lock = FileLock(os.path.join(lib_dir, module_name + '.lock'))
            with lock:
                module = self._load_module(
                    module_path,
                    define_macros=define_macros,
                    include_dirs=include_dirs,
                    library_dirs=library_dirs,
                    extra_compile_args=extra_compile_args,
                    extra_link_args=extra_link_args,
                    libraries=libraries,
                    code=code,
                    lib_dir=lib_dir,
                    module_name=module_name,
                    runtime_library_dirs=runtime_library_dirs,
                    compiler=compiler,
                    key=key,
                    sources=sources)
            return module
        else:
            return self._load_module(module_path,
                                     define_macros=define_macros,
                                     include_dirs=include_dirs,
                                     library_dirs=library_dirs,
                                     extra_compile_args=extra_compile_args,
                                     extra_link_args=extra_link_args,
                                     libraries=libraries,
                                     code=code,
                                     lib_dir=lib_dir,
                                     module_name=module_name,
                                     runtime_library_dirs=runtime_library_dirs,
                                     compiler=compiler,
                                     key=key,
                                     sources=sources)
def abstract_code_dependencies(code, known_vars=None, known_funcs=None):
    '''
    Analyses identifiers used in abstract code blocks
    
    Parameters
    ----------
    
    code : str
        The abstract code block.
    known_vars : set
        The set of known variable names.
    known_funcs : set
        The set of known function names.
    
    Returns
    -------
    
    results : namedtuple with the following fields
        ``all``
            The set of all identifiers that appear in this code block,
            including functions.
        ``read``
            The set of values that are read, excluding functions.
        ``write``
            The set of all values that are written to.
        ``funcs``
            The set of all function names.
        ``known_all``
            The set of all identifiers that appear in this code block and
            are known.
        ``known_read``
            The set of known values that are read, excluding functions.
        ``known_write``
            The set of known values that are written to.
        ``known_funcs``
            The set of known functions that are used.
        ``unknown_read``
            The set of all unknown variables whose values are read. Equal
            to ``read-known_vars``.
        ``unknown_write``
            The set of all unknown variables written to. Equal to
            ``write-known_vars``.
        ``unknown_funcs``
            The set of all unknown function names, equal to
            ``funcs-known_funcs``.
        ``undefined_read``
            The set of all unknown variables whose values are read before they
            are written to. If this set is nonempty it usually indicates an
            error, since a variable that is read should either have been
            defined in the code block (in which case it will appear in
            ``newly_defined``) or already be known.
        ``newly_defined``
            The set of all variable names which are newly defined in this
            abstract code block.
    '''
    if known_vars is None:
        known_vars = set([])
    if known_funcs is None:
        known_funcs = set([])
    if not isinstance(known_vars, set):
        known_vars = set(known_vars)
    if not isinstance(known_funcs, set):
        known_funcs = set(known_funcs)
    
    code = deindent(code, docstring=True)
    parsed_code = ast.parse(code, mode='exec')
    
    # Get the list of all variables that are read from and written to,
    # ignoring the order
    allids, read, write, funcs = get_read_write_funcs(parsed_code) 
    
    # Now check if there are any values that are unknown and read before
    # they are written to
    defined = known_vars.copy()
    newly_defined = set([])
    undefined_read = set([])
    for line in parsed_code.body:
        _, cur_read, cur_write, _ = get_read_write_funcs(line)
        undef = cur_read-defined
        undefined_read |= undef
        newly_defined |= (cur_write-defined)-undefined_read
        defined |= cur_write
    
    # Return the results as a named tuple
    results = dict(
        all=allids,
        read=read,
        write=write,
        funcs=funcs,
        known_all=allids.intersection(known_vars.union(known_funcs)),
        known_read=read.intersection(known_vars),
        known_write=write.intersection(known_vars),
        known_funcs=funcs.intersection(known_funcs),
        unknown_read=read-known_vars,
        unknown_write=write-known_vars,
        unknown_funcs=funcs-known_funcs,
        undefined_read=undefined_read,
        newly_defined=newly_defined,
        )
    return namedtuple('AbstractCodeDependencies', list(results.keys()))(**results)
def substitute_abstract_code_functions(code, funcs):
    '''
    Performs inline substitution of all the functions in the code
    
    Parameters
    ----------
    code : str
        The abstract code to make inline substitutions into.
    funcs : list, dict or set of AbstractCodeFunction
        The function substitutions to use, note in the case of a dict, the
        keys are ignored and the function name is used.
        
    Returns
    -------
    code : str
        The code with inline substitutions performed.
    '''
    if isinstance(funcs, (list, set)):
        newfuncs = dict()
        for f in funcs:
            newfuncs[f.name] = f
        funcs = newfuncs

    code = deindent(code)
    lines = ast.parse(code, mode='exec').body

    # This is a slightly nasty hack, but basically we just check by looking at
    # the existing identifiers how many inline operations have already been
    # performed by previous calls to this function
    ids = get_identifiers(code)
    funcstarts = {}
    for func in funcs.values():
        subids = {
            id
            for id in ids if id.startswith('_inline_' + func.name + '_')
        }
        subids = {
            id.replace('_inline_' + func.name + '_', '')
            for id in subids
        }
        alli = []
        for subid in subids:
            p = subid.find('_')
            if p > 0:
                subid = subid[:p]
            i = int(subid)
            alli.append(i)
        if len(alli) == 0:
            i = 0
        else:
            i = max(alli) + 1
        funcstarts[func.name] = i

    # Now we rewrite all the lines, replacing each line with a sequence of
    # lines performing the inlining
    newlines = []
    for line in lines:
        for func in funcs.values():
            rw = FunctionRewriter(func, funcstarts[func.name])
            line = rw.visit(line)
            newlines.extend(rw.pre)
            funcstarts[func.name] = rw.numcalls
        newlines.append(line)

    # Now we render to a code string
    nr = NodeRenderer()
    newcode = '\n'.join(nr.render_node(line) for line in newlines)

    # We recurse until no changes in the code to ensure that all functions
    # are expanded if one function refers to another, etc.
    if newcode == code:
        return newcode
    else:
        return substitute_abstract_code_functions(newcode, funcs)
    print('Saving notebook and converting to RST')
    exporter = NotebookExporter()
    output, _ = exporter.from_notebook_node(notebook)
    with codecs.open(output_ipynb_fname, 'w', encoding='utf-8') as f:
        f.write(output)

    # Insert a note about ipython notebooks with a download link
    note = deindent('''
    .. only:: html

        .. |launchbinder| image:: http://mybinder.org/badge.svg
        .. _launchbinder: https://mybinder.org/v2/gh/angela-team/angela2-binder/master?filepath=tutorials/{tutorial}.ipynb
    
        .. note::
           This tutorial is a static non-editable version. You can launch an
           interactive, editable version without installing any local files
           using the Binder service (although note that at some times this
           may be slow or fail to open): |launchbinder|_
    
           Alternatively, you can download a copy of the notebook file
           to use locally: :download:`{tutorial}.ipynb`
    
           See the :doc:`tutorial overview page <index>` for more details.

    '''.format(tutorial=basename))
    notebook.cells.insert(
        1, NotebookNode(cell_type='raw', metadata={}, source=note))
    exporter = RSTExporter()
    output, resources = exporter.from_notebook_node(
        notebook, resources={'unique_key': basename + '_image'})
    with codecs.open(output_rst_fname, 'w', encoding='utf-8') as f:
        f.write(output)
Example #11
0
def make_statements(code, variables, dtype, optimise=True, blockname=''):
    '''
    make_statements(code, variables, dtype, optimise=True, blockname='')

    Turn a series of abstract code statements into Statement objects, inferring
    whether each line is a set/declare operation, whether the variables are
    constant or not, and handling the cacheing of subexpressions.

    Parameters
    ----------
    code : str
        A (multi-line) string of statements.
    variables : dict-like
        A dictionary of with `Variable` and `Function` objects for every
        identifier used in the `code`.
    dtype : `dtype`
        The data type to use for temporary variables
    optimise : bool, optional
        Whether to optimise expressions, including
        pulling out loop invariant expressions and putting them in new
        scalar constants. Defaults to ``False``, since this function is also
        used just to in contexts where we are not interested by this kind of
        optimisation. For the main code generation stage, its value is set by
        the `codegen.loop_invariant_optimisations` preference.
    blockname : str, optional
        A name for the block (used to name intermediate variables to avoid
        name clashes when multiple blocks are used together)
    Returns
    -------
    scalar_statements, vector_statements : (list of `Statement`, list of `Statement`)
        Lists with statements that are to be executed once and statements that
        are to be executed once for every neuron/synapse/... (or in a vectorised
        way)

    Notes
    -----
    If ``optimise`` is ``True``, then the
    ``scalar_statements`` may include newly introduced scalar constants that
    have been identified as loop-invariant and have therefore been pulled out
    of the vector statements. The resulting statements will also use augmented
    assignments where possible, i.e. a statement such as ``w = w + 1`` will be
    replaced by ``w += 1``. Also, statements involving booleans will have
    additional information added to them (see `Statement` for details)
    describing how the statement can be reformulated as a sequence of if/then
    statements. Calls `~angela2.codegen.optimisation.optimise_statements`.
    '''
    code = strip_empty_lines(deindent(code))
    lines = re.split(r'[;\n]', code)
    lines = [LineInfo(code=line) for line in lines if len(line)]
    # Do a copy so we can add stuff without altering the original dict
    variables = dict(variables)
    # we will do inference to work out which lines are := and which are =
    defined = set(k for k, v in variables.items()
                  if not isinstance(v, AuxiliaryVariable))
    for line in lines:
        statement = None
        # parse statement into "var op expr"
        var, op, expr, comment = parse_statement(line.code)
        if var in variables and isinstance(variables[var], Subexpression):
            raise SyntaxError("Illegal line '{line}' in abstract code. "
                              "Cannot write to subexpression "
                              "'{var}'.".format(line=line.code, var=var))
        if op == '=':
            if var not in defined:
                op = ':='
                defined.add(var)
                if var not in variables:
                    annotated_ast = angela_ast(expr, variables)
                    is_scalar = annotated_ast.scalar
                    if annotated_ast.dtype == 'boolean':
                        use_dtype = bool
                    elif annotated_ast.dtype == 'integer':
                        use_dtype = int
                    else:
                        use_dtype = dtype
                    new_var = AuxiliaryVariable(var,
                                                dtype=use_dtype,
                                                scalar=is_scalar)
                    variables[var] = new_var
            elif not variables[var].is_boolean:
                sympy_expr = str_to_sympy(expr, variables)
                if variables[var].is_integer:
                    sympy_var = sympy.Symbol(var, integer=True)
                else:
                    sympy_var = sympy.Symbol(var, real=True)
                try:
                    collected = sympy.collect(sympy_expr,
                                              sympy_var,
                                              exact=True,
                                              evaluate=False)
                except AttributeError:
                    # If something goes wrong during collection, e.g. collect
                    # does not work for logical expressions
                    collected = {1: sympy_expr}

                if (len(collected) == 2
                        and set(collected.keys()) == {1, sympy_var}
                        and collected[sympy_var] == 1):
                    # We can replace this statement by a += assignment
                    statement = Statement(var,
                                          '+=',
                                          sympy_to_str(collected[1]),
                                          comment,
                                          dtype=variables[var].dtype,
                                          scalar=variables[var].scalar)
                elif len(collected) == 1 and sympy_var in collected:
                    # We can replace this statement by a *= assignment
                    statement = Statement(var,
                                          '*=',
                                          sympy_to_str(collected[sympy_var]),
                                          comment,
                                          dtype=variables[var].dtype,
                                          scalar=variables[var].scalar)
        if statement is None:
            statement = Statement(var,
                                  op,
                                  expr,
                                  comment,
                                  dtype=variables[var].dtype,
                                  scalar=variables[var].scalar)

        line.statement = statement
        # for each line will give the variable being written to
        line.write = var
        # each line will give a set of variables which are read
        line.read = get_identifiers_recursively([expr], variables)

    # All writes to scalar variables must happen before writes to vector
    # variables
    scalar_write_done = False
    for line in lines:
        stmt = line.statement
        if stmt.op != ':=' and variables[
                stmt.var].scalar and scalar_write_done:
            raise SyntaxError(
                ('All writes to scalar variables in a code block '
                 'have to be made before writes to vector '
                 'variables. Illegal write to %s.') % line.write)
        elif not variables[stmt.var].scalar:
            scalar_write_done = True

    # all variables which are written to at some point in the code block
    # used to determine whether they should be const or not
    all_write = set(line.write for line in lines)

    # backwards compute whether or not variables will be read again
    # note that will_read for a line gives the set of variables it will read
    # on the current line or subsequent ones. will_write gives the set of
    # variables that will be written after the current line
    will_read = set()
    will_write = set()
    for line in lines[::-1]:
        will_read = will_read.union(line.read)
        line.will_read = will_read.copy()
        line.will_write = will_write.copy()
        will_write.add(line.write)

    subexpressions = dict((name, val) for name, val in variables.items()
                          if isinstance(val, Subexpression))
    # Check that no scalar subexpression refers to a vectorised function
    # (e.g. rand()) -- otherwise it would be differently interpreted depending
    # on whether it is used in a scalar or a vector context (i.e., even though
    # the subexpression is supposed to be scalar, it would be vectorised when
    # used as part of non-scalar expressions)
    for name, subexpr in subexpressions.items():
        if subexpr.scalar:
            identifiers = get_identifiers(subexpr.expr)
            for identifier in identifiers:
                if (identifier in variables and getattr(
                        variables[identifier], 'auto_vectorise', False)):
                    raise SyntaxError(('The scalar subexpression {} refers to '
                                       'the implicitly vectorised function {} '
                                       '-- this is not allowed since it leads '
                                       'to different interpretations of this '
                                       'subexpression depending on whether it '
                                       'is used in a scalar or vector '
                                       'context.').format(name, identifier))

    # sort subexpressions into an order so that subexpressions that don't depend
    # on other subexpressions are first
    subexpr_deps = dict(
        (name, [dep for dep in subexpr.identifiers if dep in subexpressions])
        for name, subexpr in subexpressions.items())
    sorted_subexpr_vars = topsort(subexpr_deps)

    statements = []

    # none are yet defined (or declared)
    subdefined = dict((name, None) for name in subexpressions)
    for line in lines:
        stmt = line.statement
        read = line.read
        write = line.write
        will_read = line.will_read
        will_write = line.will_write
        # update/define all subexpressions needed by this statement
        for var in sorted_subexpr_vars:
            if var not in read:
                continue

            subexpression = subexpressions[var]
            # if already defined/declared
            if subdefined[var] == 'constant':
                continue
            elif subdefined[var] == 'variable':
                op = '='
                constant = False
            else:
                op = ':='
                # check if the referred variables ever change
                ids = subexpression.identifiers
                constant = all(v not in will_write for v in ids)
                subdefined[var] = 'constant' if constant else 'variable'

            statement = Statement(var,
                                  op,
                                  subexpression.expr,
                                  comment='',
                                  dtype=variables[var].dtype,
                                  constant=constant,
                                  subexpression=True,
                                  scalar=variables[var].scalar)
            statements.append(statement)

        var, op, expr, comment = stmt.var, stmt.op, stmt.expr, stmt.comment

        # constant only if we are declaring a new variable and we will not
        # write to it again
        constant = op == ':=' and var not in will_write
        statement = Statement(var,
                              op,
                              expr,
                              comment,
                              dtype=variables[var].dtype,
                              constant=constant,
                              scalar=variables[var].scalar)
        statements.append(statement)

    scalar_statements = [s for s in statements if s.scalar]
    vector_statements = [s for s in statements if not s.scalar]

    if optimise and prefs.codegen.loop_invariant_optimisations:
        scalar_statements, vector_statements = optimise_statements(
            scalar_statements,
            vector_statements,
            variables,
            blockname=blockname)

    return scalar_statements, vector_statements
    def _add_user_function(self, varname, variable, added):
        impl = variable.implementations[self.codeobj_class]
        if (impl.name, variable) in added:
            return  # nothing to do
        else:
            added.add((impl.name, variable))
        support_code = []
        hash_defines = []
        pointers = []
        user_functions = [(varname, variable)]
        funccode = impl.get_code(self.owner)
        if isinstance(funccode, str):
            # Rename references to any dependencies if necessary
            for dep_name, dep in impl.dependencies.items():
                dep_impl = dep.implementations[self.codeobj_class]
                dep_impl_name = dep_impl.name
                if dep_impl_name is None:
                    dep_impl_name = dep.pyfunc.__name__
                if dep_name != dep_impl_name:
                    funccode = word_substitute(funccode,
                                               {dep_name: dep_impl_name})
            funccode = {'support_code': funccode}
        if funccode is not None:
            # To make namespace variables available to functions, we
            # create global variables and assign to them in the main
            # code
            func_namespace = impl.get_namespace(self.owner) or {}
            for ns_key, ns_value in func_namespace.items():
                if hasattr(ns_value, 'dtype'):
                    if ns_value.shape == ():
                        raise NotImplementedError(
                            ('Directly replace scalar values in the function '
                             'instead of providing them via the namespace'))
                    type_str = self.c_data_type(ns_value.dtype) + '*'
                else:  # e.g. a function
                    type_str = 'py::object'
                support_code.append('static {0} _namespace{1};'.format(
                    type_str, ns_key))
                pointers.append('_namespace{0} = {1};'.format(ns_key, ns_key))
            support_code.append(deindent(funccode.get('support_code', '')))
            hash_defines.append(deindent(funccode.get('hashdefine_code', '')))

        dep_hash_defines = []
        dep_pointers = []
        dep_support_code = []
        if impl.dependencies is not None:
            for dep_name, dep in impl.dependencies.items():
                if dep_name not in self.variables:
                    self.variables[dep_name] = dep
                    dep_impl = dep.implementations[self.codeobj_class]
                    if dep_name != dep_impl.name:
                        self.func_name_replacements[dep_name] = dep_impl.name
                    user_function = self._add_user_function(
                        dep_name, dep, added)
                    if user_function is not None:
                        hd, ps, sc, uf = user_function
                        dep_hash_defines.extend(hd)
                        dep_pointers.extend(ps)
                        dep_support_code.extend(sc)
                        user_functions.extend(uf)

        return (dep_hash_defines + hash_defines, dep_pointers + pointers,
                dep_support_code + support_code, user_functions)