Пример #1
0
    def __init__(self, source, b, a):
        # Automatically duplicate mono input to fit the desired output shape
        if b.shape[0]!=source.nchannels:
            if source.nchannels!=1:
                raise ValueError('Can only automatically duplicate source channels for mono sources, use RestructureFilterbank.')
            source = RestructureFilterbank(source, b.shape[0])
        Filterbank.__init__(self, source)
        # Weave version of filtering requires Fortran ordering of filter params
        if len(b.shape)==2 and len(a.shape)==2:
            b = np.reshape(b, b.shape+(1,))
            a = np.reshape(a, a.shape+(1,))
        self.filt_b = np.array(b, order='F')
        self.filt_a = np.array(a, order='F')
        self.filt_state = np.zeros((b.shape[0], b.shape[1], b.shape[2]), order='F')
        # Check not only for the availability of weave/Cython, but also whether
        # they can successfully compile a simple test program
        from brian2.codegen.runtime.weave_rt.weave_rt import WeaveCodeObject
        from brian2.codegen.runtime.cython_rt.cython_rt import CythonCodeObject
        if weave is not None and WeaveCodeObject.is_available():
            self.use_weave = True
            self.use_cython = False
        else:
            self.use_weave = False
            if Cython is not None and CythonCodeObject.is_available():
                self.use_cython = True
            else:
                self.use_cython = False

        if self.use_weave:
            logger.debug("Using weave for LinearFilterbank")
            self.cpp_compiler, self.extra_compile_args = get_compiler_and_args()
        elif self.use_cython:
            logger.debug("Using Cython for LinearFilterbank")
            self.cython_func = CythonLinearFilterbankApply()
Пример #2
0
    def __init__(self):
        self.compiler, self.extra_compile_args = get_compiler_and_args()
        code = '''
#cython: language_level=3
#cython: boundscheck=False
#cython: wraparound=False
#cython: infer_types=True

import numpy as _numpy
cimport numpy as _numpy

cpdef parallel_lfilter(_numpy.ndarray[_numpy.float64_t, ndim=3] b,
                       _numpy.ndarray[_numpy.float64_t, ndim=3] a,
                       _numpy.ndarray[_numpy.float64_t, ndim=2] x,
                       _numpy.ndarray[_numpy.float64_t, ndim=3] zi,
                       _numpy.ndarray[_numpy.float64_t, ndim=2] y):
    cdef int n, m, p, n1, m1, p1, numsamples, s, k, i, j
    cdef double* py
    cdef double* px
    cdef double* pa
    cdef double* pb
    cdef double* pzi
    cdef double* pzi2
    n = b.shape[0]
    m = b.shape[1]
    p = b.shape[2]
    numsamples = x.shape[0]
    for s in range(numsamples):
        py = &(y[s, 0]) 
        px = &(x[s, 0])
        for k in range(p):
            pb = &(b[0, 0, k])
            pzi = &(zi[0, 0, k])
            for j in range(n):
                y[s, j] =   b[j, 0, k]*x[s, j] + zi[j, 0, k]
                # py[j] =   pb[j]*px[j] + pzi[j]
            for i in range(m-2):
                pa = &(a[0, i+1, k])
                pb = &(b[0, i+1, k])
                pzi = &(zi[0, i, k])                
                pzi2 = &(zi[0, i+1, k])
                for j in range(n):
                    # zi[j, i, k] = b[j, i+1, k]*x[s, j] + zi[j, i+1, k] - a[j, i+1, k]*y[s,j]
                    pzi[j] = pb[j]*px[j] + pzi2[j] - pa[j]*py[j]
            pa = &(a[0, m-1, k])
            pb = &(b[0, m-1, k])
            pzi = &(zi[0, m-2, k])                
            for j in range(n):
                # zi[j, m-2, k] = b[j, m-1, k]*x[s,j] - a[j, m-1, k]*y[s,j]
                pzi[j] = pb[j]*px[j] - pa[j]*py[j]
            if k<p-1:
                for j in range(n):
                    # x[s, j] = y[s, j]
                    px[j] = py[j]
        '''
        self.compiled_code = cython_extension_manager.create_extension(code,
                                                                       compiler=self.compiler,
                                                                       extra_compile_args=self.extra_compile_args)
Пример #3
0
def test_cpp_flags_support():
    from distutils.ccompiler import get_default_compiler
    compiler = get_default_compiler()
    if compiler == 'msvc':
        pytest.skip('No flag support check for msvc')
    old_prefs = prefs['codegen.cpp.extra_compile_args']

    # Should always be supported
    prefs['codegen.cpp.extra_compile_args'] = ['-w']
    _, compile_args = get_compiler_and_args()
    assert compile_args == prefs['codegen.cpp.extra_compile_args']

    # Should never be supported and raise a warning
    prefs['codegen.cpp.extra_compile_args'] = ['-invalidxyz']
    with catch_logs() as l:
        _, compile_args = get_compiler_and_args()
    assert len(l) == 1 and l[0][0] == 'WARNING'
    assert compile_args == []

    prefs['codegen.cpp.extra_compile_args'] = old_prefs
Пример #4
0
    def build(self, directory='output',
              compile=True, run=True, debug=False, clean=True,
              with_output=True, native=True,
              additional_source_files=None, additional_header_files=None,
              main_includes=None, run_includes=None,
              run_args=None, **kwds):
        '''
        Build the project
        
        TODO: more details
        
        Parameters
        ----------
        directory : str
            The output directory to write the project to, any existing files will be overwritten.
        compile : bool
            Whether or not to attempt to compile the project
        run : bool
            Whether or not to attempt to run the built project if it successfully builds.
        debug : bool
            Whether to compile in debug mode.
        with_output : bool
            Whether or not to show the ``stdout`` of the built program when run.
        native : bool
            Whether or not to compile for the current machine's architecture (best for speed, but not portable)
        clean : bool
            Whether or not to clean the project before building
        additional_source_files : list of str
            A list of additional ``.cpp`` files to include in the build.
        additional_header_files : list of str
            A list of additional ``.h`` files to include in the build.
        main_includes : list of str
            A list of additional header files to include in ``main.cpp``.
        run_includes : list of str
            A list of additional header files to include in ``run.cpp``.
        '''
        renames = {'project_dir': 'directory',
                   'compile_project': 'compile',
                   'run_project': 'run'}
        if len(kwds):
            msg = ''
            for kwd in kwds:
                if kwd in renames:
                    msg += ("Keyword argument '%s' has been renamed to "
                            "'%s'. ") % (kwd, renames[kwd])
                else:
                    msg += "Unknown keyword argument '%s'. " % kwd
            raise TypeError(msg)

        if additional_source_files is None:
            additional_source_files = []
        if additional_header_files is None:
            additional_header_files = []
        if main_includes is None:
            main_includes = []
        if run_includes is None:
            run_includes = []
        if run_args is None:
            run_args = []

        compiler, extra_compile_args = get_compiler_and_args()
        compiler_flags = ' '.join(extra_compile_args)
        self.project_dir = directory
        ensure_directory(directory)
        
        for d in ['code_objects', 'results', 'static_arrays']:
            ensure_directory(os.path.join(directory, d))
            
        writer = CUDAWriter(directory)
        
        logger.debug("Writing CUDA standalone project to directory "+os.path.normpath(directory))
        arange_arrays = sorted([(var, start)
                                for var, start in self.arange_arrays.iteritems()],
                               key=lambda (var, start): var.name)

        self.write_static_arrays(directory)
        self.find_synapses()

        # Not sure what the best place is to call Network.after_run -- at the
        # moment the only important thing it does is to clear the objects stored
        # in magic_network. If this is not done, this might lead to problems
        # for repeated runs of standalone (e.g. in the test suite).
        for net in self.networks:
            net.after_run()
            
        self.generate_main_source(writer, main_includes)
        self.generate_codeobj_source(writer)        
        self.generate_objects_source(writer, arange_arrays, self.net_synapses, self.static_array_specs, self.networks)
        self.generate_network_source(writer, compiler)
        self.generate_synapses_classes_source(writer)
        self.generate_run_source(writer, run_includes)
        self.generate_rand_source(writer)
        self.copy_source_files(writer, directory)
        
        writer.source_files.extend(additional_source_files)
        writer.header_files.extend(additional_header_files)
        
        self.generate_makefile(writer, compiler, native, compiler_flags, nb_threads=0)
        
        if compile:
            self.compile_source(directory, compiler, debug, clean, native)
            if run:
                self.run(directory, with_output, run_args)
Пример #5
0
    def build(self,
              directory='output',
              compile=True,
              run=True,
              debug=False,
              clean=True,
              with_output=True,
              native=True,
              additional_source_files=None,
              run_args=None,
              **kwds):
        '''
        Build the project

        TODO: more details

        Parameters
        ----------
        directory : str
            The output directory to write the project to, any existing files will be overwritten.
        compile : bool
            Whether or not to attempt to compile the project
        run : bool
            Whether or not to attempt to run the built project if it successfully builds.
        debug : bool
            Whether to compile in debug mode.
        with_output : bool
            Whether or not to show the ``stdout`` of the built program when run.
        native : bool
            Whether or not to compile for the current machine's architecture (best for speed, but not portable)
        clean : bool
            Whether or not to clean the project before building
        additional_source_files : list of str
            A list of additional ``.cpp`` files to include in the build.
        '''
        renames = {
            'project_dir': 'directory',
            'compile_project': 'compile',
            'run_project': 'run'
        }
        if len(kwds):
            msg = ''
            for kwd in kwds:
                if kwd in renames:
                    msg += ("Keyword argument '%s' has been renamed to "
                            "'%s'. ") % (kwd, renames[kwd])
                else:
                    msg += "Unknown keyword argument '%s'. " % kwd
            raise TypeError(msg)

        if additional_source_files is None:
            additional_source_files = []
        if run_args is None:
            run_args = []
        self.project_dir = directory
        ensure_directory(directory)

        compiler, extra_compile_args = get_compiler_and_args()
        compiler_obj = ccompiler.new_compiler(compiler=compiler)
        compiler_flags = (ccompiler.gen_preprocess_options(
            prefs['codegen.cpp.define_macros'],
            prefs['codegen.cpp.include_dirs']) + extra_compile_args)
        linker_flags = (ccompiler.gen_lib_options(
            compiler_obj,
            library_dirs=prefs['codegen.cpp.library_dirs'],
            runtime_library_dirs=prefs['codegen.cpp.runtime_library_dirs'],
            libraries=prefs['codegen.cpp.libraries']) +
                        prefs['codegen.cpp.extra_link_args'])

        for d in ['code_objects', 'results', 'static_arrays']:
            ensure_directory(os.path.join(directory, d))

        writer = CPPWriter(directory)

        # Get the number of threads if specified in an openmp context
        nb_threads = prefs.devices.cpp_standalone.openmp_threads
        # If the number is negative, we need to throw an error
        if (nb_threads < 0):
            raise ValueError(
                'The number of OpenMP threads can not be negative !')

        logger.debug("Writing C++ standalone project to directory " +
                     os.path.normpath(directory))

        self.check_openmp_compatible(nb_threads)

        arange_arrays = sorted(
            [(var, start) for var, start in self.arange_arrays.iteritems()],
            key=lambda (var, start): var.name)

        self.write_static_arrays(directory)
        self.find_synapses()

        # Not sure what the best place is to call Network.after_run -- at the
        # moment the only important thing it does is to clear the objects stored
        # in magic_network. If this is not done, this might lead to problems
        # for repeated runs of standalone (e.g. in the test suite).
        for net in self.networks:
            net.after_run()

        # Check that all names are globally unique
        names = [obj.name for net in self.networks for obj in net.objects]
        non_unique_names = [
            name for name, count in Counter(names).iteritems() if count > 1
        ]
        if len(non_unique_names):
            formatted_names = ', '.join("'%s'" % name
                                        for name in non_unique_names)
            raise ValueError(
                'All objects need to have unique names in '
                'standalone mode, the following name(s) were used '
                'more than once: %s' % formatted_names)

        self.generate_objects_source(writer, arange_arrays, self.net_synapses,
                                     self.static_array_specs, self.networks)
        self.generate_main_source(writer)
        self.generate_codeobj_source(writer)
        self.generate_network_source(writer, compiler)
        self.generate_synapses_classes_source(writer)
        self.generate_run_source(writer)
        self.copy_source_files(writer, directory)

        writer.source_files.extend(additional_source_files)

        self.generate_makefile(writer,
                               compiler,
                               native=native,
                               compiler_flags=' '.join(compiler_flags),
                               linker_flags=' '.join(linker_flags),
                               nb_threads=nb_threads)

        if compile:
            self.compile_source(directory, compiler, debug, clean, native)
            if run:
                self.run(directory, with_output, run_args)
Пример #6
0
    def build(self, directory='output',
              compile=True, run=True, debug=False, clean=True,
              with_output=True, native=True,
              additional_source_files=None,
              run_args=None, **kwds):
        '''
        Build the project

        TODO: more details

        Parameters
        ----------
        directory : str
            The output directory to write the project to, any existing files will be overwritten.
        compile : bool
            Whether or not to attempt to compile the project
        run : bool
            Whether or not to attempt to run the built project if it successfully builds.
        debug : bool
            Whether to compile in debug mode.
        with_output : bool
            Whether or not to show the ``stdout`` of the built program when run. Output will be shown in case
            of compilation or runtime error.
        native : bool
            Whether or not to compile for the current machine's architecture (best for speed, but not portable)
        clean : bool
            Whether or not to clean the project before building
        additional_source_files : list of str
            A list of additional ``.cpp`` files to include in the build.
        '''
        renames = {'project_dir': 'directory',
                   'compile_project': 'compile',
                   'run_project': 'run'}
        if len(kwds):
            msg = ''
            for kwd in kwds:
                if kwd in renames:
                    msg += ("Keyword argument '%s' has been renamed to "
                            "'%s'. ") % (kwd, renames[kwd])
                else:
                    msg += "Unknown keyword argument '%s'. " % kwd
            raise TypeError(msg)

        if additional_source_files is None:
            additional_source_files = []
        if run_args is None:
            run_args = []
        self.project_dir = directory
        ensure_directory(directory)

        compiler, extra_compile_args = get_compiler_and_args()
        compiler_obj = ccompiler.new_compiler(compiler=compiler)
        compiler_flags = (ccompiler.gen_preprocess_options(prefs['codegen.cpp.define_macros'],
                                                           prefs['codegen.cpp.include_dirs']) +
                          extra_compile_args)
        linker_flags = (ccompiler.gen_lib_options(compiler_obj,
                                                  library_dirs=prefs['codegen.cpp.library_dirs'],
                                                  runtime_library_dirs=prefs['codegen.cpp.runtime_library_dirs'],
                                                  libraries=prefs['codegen.cpp.libraries']) +
                        prefs['codegen.cpp.extra_link_args'])

        for d in ['code_objects', 'results', 'static_arrays']:
            ensure_directory(os.path.join(directory, d))

        writer = CPPWriter(directory)

        # Get the number of threads if specified in an openmp context
        nb_threads = prefs.devices.cpp_standalone.openmp_threads
        # If the number is negative, we need to throw an error
        if (nb_threads < 0):
            raise ValueError('The number of OpenMP threads can not be negative !')

        logger.debug("Writing C++ standalone project to directory "+os.path.normpath(directory))

        self.check_openmp_compatible(nb_threads)

        arange_arrays = sorted([(var, start)
                                for var, start in self.arange_arrays.iteritems()],
                               key=lambda (var, start): var.name)

        self.write_static_arrays(directory)
        self.find_synapses()

        # Not sure what the best place is to call Network.after_run -- at the
        # moment the only important thing it does is to clear the objects stored
        # in magic_network. If this is not done, this might lead to problems
        # for repeated runs of standalone (e.g. in the test suite).
        for net in self.networks:
            net.after_run()

        # Check that all names are globally unique
        names = [obj.name for net in self.networks for obj in net.objects]
        non_unique_names = [name for name, count in Counter(names).iteritems()
                            if count > 1]
        if len(non_unique_names):
            formatted_names = ', '.join("'%s'" % name
                                        for name in non_unique_names)
            raise ValueError('All objects need to have unique names in '
                             'standalone mode, the following name(s) were used '
                             'more than once: %s' % formatted_names)

        self.generate_objects_source(writer, arange_arrays, self.net_synapses, self.static_array_specs, self.networks)
        self.generate_main_source(writer)
        self.generate_codeobj_source(writer)
        self.generate_network_source(writer, compiler)
        self.generate_synapses_classes_source(writer)
        self.generate_run_source(writer)
        self.copy_source_files(writer, directory)

        writer.source_files.extend(additional_source_files)

        self.generate_makefile(writer, compiler, native=native,
                               compiler_flags=' '.join(compiler_flags),
                               linker_flags=' '.join(linker_flags),
                               nb_threads=nb_threads)

        if compile:
            self.compile_source(directory, compiler, debug, clean, native)
            if run:
                self.run(directory, with_output, run_args)
Пример #7
0
    def build(self,
              directory='output',
              compile=True,
              run=True,
              debug=False,
              clean=True,
              with_output=True,
              additional_source_files=None,
              additional_header_files=None,
              main_includes=None,
              run_includes=None,
              run_args=None,
              direct_call=True,
              **kwds):
        '''
        Build the project
        
        TODO: more details
        
        Parameters
        ----------
        directory : str, optional
            The output directory to write the project to, any existing files
            will be overwritten. If the given directory name is ``None``, then
            a temporary directory will be used (used in the test suite to avoid
            problems when running several tests in parallel). Defaults to
            ``'output'``.
        compile : bool, optional
            Whether or not to attempt to compile the project. Defaults to
            ``True``.
        run : bool, optional
            Whether or not to attempt to run the built project if it
            successfully builds. Defaults to ``True``.
        debug : bool, optional
            Whether to compile in debug mode. Defaults to ``False``.
        with_output : bool, optional
            Whether or not to show the ``stdout`` of the built program when run.
            Output will be shown in case of compilation or runtime error.
            Defaults to ``True``.
        clean : bool, optional
            Whether or not to clean the project before building. Defaults to
            ``True``.
        additional_source_files : list of str, optional
            A list of additional ``.cu`` files to include in the build.
        additional_header_files : list of str
            A list of additional ``.h`` files to include in the build.
        main_includes : list of str
            A list of additional header files to include in ``main.cu``.
        run_includes : list of str
            A list of additional header files to include in ``run.cu``.
        direct_call : bool, optional
            Whether this function was called directly. Is used internally to
            distinguish an automatic build due to the ``build_on_run`` option
            from a manual ``device.build`` call.
        '''
        if self.build_on_run and direct_call:
            raise RuntimeError(
                'You used set_device with build_on_run=True '
                '(the default option), which will automatically '
                'build the simulation at the first encountered '
                'run call - do not call device.build manually '
                'in this case. If you want to call it manually, '
                'e.g. because you have multiple run calls, use '
                'set_device with build_on_run=False.')
        if self.has_been_run:
            raise RuntimeError('The network has already been built and run '
                               'before. To build several simulations in '
                               'the same script, call "device.reinit()" '
                               'and "device.activate()". Note that you '
                               'will have to set build options (e.g. the '
                               'directory) and defaultclock.dt again.')
        renames = {
            'project_dir': 'directory',
            'compile_project': 'compile',
            'run_project': 'run'
        }
        if len(kwds):
            msg = ''
            for kwd in kwds:
                if kwd in renames:
                    msg += ("Keyword argument '%s' has been renamed to "
                            "'%s'. ") % (kwd, renames[kwd])
                else:
                    msg += "Unknown keyword argument '%s'. " % kwd
            raise TypeError(msg)

        if additional_source_files is None:
            additional_source_files = []
        if additional_header_files is None:
            additional_header_files = []
        if main_includes is None:
            main_includes = []
        if run_includes is None:
            run_includes = []
        if run_args is None:
            run_args = []
        if directory is None:
            directory = tempfile.mkdtemp()

        cpp_compiler, cpp_extra_compile_args = get_compiler_and_args()
        cpp_compiler_flags = ' '.join(cpp_extra_compile_args)
        self.project_dir = directory
        ensure_directory(directory)

        for d in ['code_objects', 'results', 'static_arrays']:
            ensure_directory(os.path.join(directory, d))

        writer = CUDAWriter(directory)

        logger.diagnostic("Writing CUDA standalone project to directory " +
                          os.path.normpath(directory))
        arange_arrays = sorted(
            [(var, start) for var, start in self.arange_arrays.iteritems()],
            key=lambda (var, start): var.name)

        self.write_static_arrays(directory)
        self.find_synapses()

        # Not sure what the best place is to call Network.after_run -- at the
        # moment the only important thing it does is to clear the objects stored
        # in magic_network. If this is not done, this might lead to problems
        # for repeated runs of standalone (e.g. in the test suite).
        for net in self.networks:
            net.after_run()

        self.generate_main_source(writer, main_includes)
        self.generate_codeobj_source(writer)
        self.generate_objects_source(writer, arange_arrays, self.net_synapses,
                                     self.static_array_specs, self.networks)
        self.generate_network_source(writer)
        self.generate_synapses_classes_source(writer)
        self.generate_run_source(writer, run_includes)
        self.generate_rand_source(writer)
        self.copy_source_files(writer, directory)

        writer.source_files.extend(additional_source_files)
        writer.header_files.extend(additional_header_files)

        self.generate_makefile(writer,
                               cpp_compiler,
                               cpp_compiler_flags,
                               nb_threads=0)

        if compile:
            self.compile_source(directory, cpp_compiler, debug, clean)
            if run:
                self.run(directory, with_output, run_args)
Пример #8
0
    def build(self, directory='output',
              compile=True, run=True, debug=False, clean=True,
              with_output=True, native=True,
              additional_source_files=None, additional_header_files=None,
              main_includes=None, run_includes=None,
              run_args=None, **kwds):
        '''
        Build the project
        
        TODO: more details
        
        Parameters
        ----------
        directory : str
            The output directory to write the project to, any existing files will be overwritten.
        compile : bool
            Whether or not to attempt to compile the project
        run : bool
            Whether or not to attempt to run the built project if it successfully builds.
        debug : bool
            Whether to compile in debug mode.
        with_output : bool
            Whether or not to show the ``stdout`` of the built program when run.
        native : bool
            Whether or not to compile for the current machine's architecture (best for speed, but not portable)
        clean : bool
            Whether or not to clean the project before building
        additional_source_files : list of str
            A list of additional ``.cpp`` files to include in the build.
        additional_header_files : list of str
            A list of additional ``.h`` files to include in the build.
        main_includes : list of str
            A list of additional header files to include in ``main.cpp``.
        run_includes : list of str
            A list of additional header files to include in ``run.cpp``.
        '''
        renames = {'project_dir': 'directory',
                   'compile_project': 'compile',
                   'run_project': 'run'}
        if len(kwds):
            msg = ''
            for kwd in kwds:
                if kwd in renames:
                    msg += ("Keyword argument '%s' has been renamed to "
                            "'%s'. ") % (kwd, renames[kwd])
                else:
                    msg += "Unknown keyword argument '%s'. " % kwd
            raise TypeError(msg)

        if additional_source_files is None:
            additional_source_files = []
        if additional_header_files is None:
            additional_header_files = []
        if main_includes is None:
            main_includes = []
        if run_includes is None:
            run_includes = []
        if run_args is None:
            run_args = []
        self.project_dir = directory
        ensure_directory(directory)

        compiler, extra_compile_args = get_compiler_and_args()
        compiler_flags = ' '.join(extra_compile_args)
        
        for d in ['code_objects', 'results', 'static_arrays']:
            ensure_directory(os.path.join(directory, d))
            
        writer = CPPWriter(directory)
        
        # Get the number of threads if specified in an openmp context
        nb_threads = prefs.devices.cpp_standalone.openmp_threads
        # If the number is negative, we need to throw an error
        if (nb_threads < 0):
            raise ValueError('The number of OpenMP threads can not be negative !') 

        logger.debug("Writing C++ standalone project to directory "+os.path.normpath(directory))
        if nb_threads > 0:
            logger.warn("OpenMP code is not yet well tested, and may be inaccurate.", "openmp", once=True)
            logger.debug("Using OpenMP with %d threads " % nb_threads)
            for codeobj in self.code_objects.itervalues():
                if not 'IS_OPENMP_COMPATIBLE' in codeobj.template_source:
                    raise RuntimeError(("Code object '%s' uses the template %s "
                                        "which is not compatible with "
                                        "OpenMP.") % (codeobj.name,
                                                      codeobj.template_name))
        arange_arrays = sorted([(var, start)
                                for var, start in self.arange_arrays.iteritems()],
                               key=lambda (var, start): var.name)

        # # Find np arrays in the namespaces and convert them into static
        # # arrays. Hopefully they are correctly used in the code: For example,
        # # this works for the namespaces for functions with C++ (e.g. TimedArray
        # # treats it as a C array) but does not work in places that are
        # # implicitly vectorized (state updaters, resets, etc.). But arrays
        # # shouldn't be used there anyway.
        for code_object in self.code_objects.itervalues():
            for name, value in code_object.variables.iteritems():
                if isinstance(value, np.ndarray):
                    self.static_arrays[name] = value

        # write the static arrays
        logger.debug("static arrays: "+str(sorted(self.static_arrays.keys())))
        static_array_specs = []
        for name, arr in sorted(self.static_arrays.items()):
            arr.tofile(os.path.join(directory, 'static_arrays', name))
            static_array_specs.append((name, c_data_type(arr.dtype), arr.size, name))

        # Write the global objects
        networks = [net() for net in Network.__instances__()
                    if net().name != '_fake_network']
        synapses = []
        for net in networks:
            net_synapses = [s for s in net.objects if isinstance(s, Synapses)]
            synapses.extend(net_synapses)
            # We don't currently support pathways with scalar delays
            for synapse_obj in net_synapses:
                for pathway in synapse_obj._pathways:
                    if not isinstance(pathway.variables['delay'],
                                      DynamicArrayVariable):
                        error_msg = ('The "%s" pathway  uses a scalar '
                                     'delay (instead of a delay per synapse). '
                                     'This is not yet supported. Do not '
                                     'specify a delay in the Synapses(...) '
                                     'call but instead set its delay attribute '
                                     'afterwards.') % (pathway.name)
                        raise NotImplementedError(error_msg)

        # Not sure what the best place is to call Network.after_run -- at the
        # moment the only important thing it does is to clear the objects stored
        # in magic_network. If this is not done, this might lead to problems
        # for repeated runs of standalone (e.g. in the test suite).
        for net in networks:
            net.after_run()

        arr_tmp = CPPStandaloneCodeObject.templater.objects(
                        None, None,
                        array_specs=self.arrays,
                        dynamic_array_specs=self.dynamic_arrays,
                        dynamic_array_2d_specs=self.dynamic_arrays_2d,
                        zero_arrays=self.zero_arrays,
                        arange_arrays=arange_arrays,
                        synapses=synapses,
                        clocks=self.clocks,
                        static_array_specs=static_array_specs,
                        networks=networks)
        writer.write('objects.*', arr_tmp)

        main_lines = []
        procedures = [('', main_lines)]
        runfuncs = {}
        for func, args in self.main_queue:
            if func=='run_code_object':
                codeobj, = args
                main_lines.append('_run_%s();' % codeobj.name)
            elif func=='run_network':
                net, netcode = args
                main_lines.extend(netcode)
            elif func=='set_by_array':
                arrayname, staticarrayname = args
                code = '''
                {pragma}
                for(int i=0; i<_num_{staticarrayname}; i++)
                {{
                    {arrayname}[i] = {staticarrayname}[i];
                }}
                '''.format(arrayname=arrayname, staticarrayname=staticarrayname, pragma=openmp_pragma('static'))
                main_lines.extend(code.split('\n'))
            elif func=='set_by_single_value':
                arrayname, item, value = args
                code = '{arrayname}[{item}] = {value};'.format(arrayname=arrayname,
                                                               item=item,
                                                               value=value)
                main_lines.extend([code])
            elif func=='set_array_by_array':
                arrayname, staticarrayname_index, staticarrayname_value = args
                code = '''
                {pragma}
                for(int i=0; i<_num_{staticarrayname_index}; i++)
                {{
                    {arrayname}[{staticarrayname_index}[i]] = {staticarrayname_value}[i];
                }}
                '''.format(arrayname=arrayname, staticarrayname_index=staticarrayname_index,
                           staticarrayname_value=staticarrayname_value, pragma=openmp_pragma('static'))
                main_lines.extend(code.split('\n'))
            elif func=='insert_code':
                main_lines.append(args)
            elif func=='start_run_func':
                name, include_in_parent = args
                if include_in_parent:
                    main_lines.append('%s();' % name)
                main_lines = []
                procedures.append((name, main_lines))
            elif func=='end_run_func':
                name, include_in_parent = args
                name, main_lines = procedures.pop(-1)
                runfuncs[name] = main_lines
                name, main_lines = procedures[-1]
            else:
                raise NotImplementedError("Unknown main queue function type "+func)

        # generate the finalisations
        for codeobj in self.code_objects.itervalues():
            if hasattr(codeobj.code, 'main_finalise'):
                main_lines.append(codeobj.code.main_finalise)

        # Generate data for non-constant values
        code_object_defs = defaultdict(list)
        for codeobj in self.code_objects.itervalues():
            lines = []
            for k, v in codeobj.variables.iteritems():
                if isinstance(v, AttributeVariable):
                    # We assume all attributes are implemented as property-like methods
                    line = 'const {c_type} {varname} = {objname}.{attrname}();'
                    lines.append(line.format(c_type=c_data_type(v.dtype), varname=k, objname=v.obj.name,
                                             attrname=v.attribute))
                elif isinstance(v, ArrayVariable):
                    try:
                        if isinstance(v, DynamicArrayVariable):
                            if v.dimensions == 1:
                                dyn_array_name = self.dynamic_arrays[v]
                                array_name = self.arrays[v]
                                line = '{c_type}* const {array_name} = &{dyn_array_name}[0];'
                                line = line.format(c_type=c_data_type(v.dtype), array_name=array_name,
                                                   dyn_array_name=dyn_array_name)
                                lines.append(line)
                                line = 'const int _num{k} = {dyn_array_name}.size();'
                                line = line.format(k=k, dyn_array_name=dyn_array_name)
                                lines.append(line)
                        else:
                            lines.append('const int _num%s = %s;' % (k, v.size))
                    except TypeError:
                        pass
            for line in lines:
                # Sometimes an array is referred to by to different keys in our
                # dictionary -- make sure to never add a line twice
                if not line in code_object_defs[codeobj.name]:
                    code_object_defs[codeobj.name].append(line)

        # Generate the code objects
        for codeobj in self.code_objects.itervalues():
            ns = codeobj.variables
            # TODO: fix these freeze/CONSTANTS hacks somehow - they work but not elegant.
            code = freeze(codeobj.code.cpp_file, ns)
            code = code.replace('%CONSTANTS%', '\n'.join(code_object_defs[codeobj.name]))
            code = '#include "objects.h"\n'+code
            
            writer.write('code_objects/'+codeobj.name+'.cpp', code)
            writer.write('code_objects/'+codeobj.name+'.h', codeobj.code.h_file)
                    
        # The code_objects are passed in the right order to run them because they were
        # sorted by the Network object. To support multiple clocks we'll need to be
        # smarter about that.
        main_tmp = CPPStandaloneCodeObject.templater.main(None, None,
                                                          main_lines=main_lines,
                                                          code_objects=self.code_objects.values(),
                                                          report_func=self.report_func,
                                                          dt=float(defaultclock.dt),
                                                          additional_headers=main_includes,
                                                          )
        writer.write('main.cpp', main_tmp)

        if compiler=='msvc':
            std_move = 'std::move'
        else:
            std_move = ''
        network_tmp = CPPStandaloneCodeObject.templater.network(None, None,
                                                             std_move=std_move)
        writer.write('network.*', network_tmp)

        synapses_classes_tmp = CPPStandaloneCodeObject.templater.synapses_classes(None, None)
        writer.write('synapses_classes.*', synapses_classes_tmp)
        
        # Generate the run functions
        run_tmp = CPPStandaloneCodeObject.templater.run(None, None, run_funcs=runfuncs,
                                                        code_objects=self.code_objects.values(),
                                                        additional_headers=run_includes,
                                                        )
        writer.write('run.*', run_tmp)

        # Copy the brianlibdirectory
        brianlib_dir = os.path.join(os.path.split(inspect.getsourcefile(CPPStandaloneCodeObject))[0],
                                    'brianlib')
        brianlib_files = copy_directory(brianlib_dir, os.path.join(directory, 'brianlib'))
        for file in brianlib_files:
            if file.lower().endswith('.cpp'):
                writer.source_files.append('brianlib/'+file)
            elif file.lower().endswith('.h'):
                writer.header_files.append('brianlib/'+file)

        # Copy the CSpikeQueue implementation
        shutil.copy2(os.path.join(os.path.split(inspect.getsourcefile(Synapses))[0], 'cspikequeue.cpp'),
                     os.path.join(directory, 'brianlib', 'spikequeue.h'))
        shutil.copy2(os.path.join(os.path.split(inspect.getsourcefile(Synapses))[0], 'stdint_compat.h'),
                     os.path.join(directory, 'brianlib', 'stdint_compat.h'))
        
        writer.source_files.extend(additional_source_files)
        writer.header_files.extend(additional_header_files)

        if compiler=='msvc':
            if native:
                arch_flag = ''
                try:
                    from cpuinfo import cpuinfo
                    res = cpuinfo.get_cpu_info()
                    if 'sse' in res['flags']:
                        arch_flag = '/arch:SSE'
                    if 'sse2' in res['flags']:
                        arch_flag = '/arch:SSE2'
                except ImportError:
                    logger.warn('Native flag for MSVC compiler requires installation of the py-cpuinfo module')
                compiler_flags += ' '+arch_flag
            
            if nb_threads>1:
                openmp_flag = '/openmp'
            else:
                openmp_flag = ''
            # Generate the visual studio makefile
            source_bases = [fname.replace('.cpp', '').replace('/', '\\') for fname in writer.source_files]
            win_makefile_tmp = CPPStandaloneCodeObject.templater.win_makefile(
                None, None,
                source_bases=source_bases,
                compiler_flags=compiler_flags,
                openmp_flag=openmp_flag,
                )
            writer.write('win_makefile', win_makefile_tmp)
        else:
            # Generate the makefile
            if os.name=='nt':
                rm_cmd = 'del *.o /s\n\tdel main.exe $(DEPS)'
            else:
                rm_cmd = 'rm $(OBJS) $(PROGRAM) $(DEPS)'
            makefile_tmp = CPPStandaloneCodeObject.templater.makefile(None, None,
                source_files=' '.join(writer.source_files),
                header_files=' '.join(writer.header_files),
                compiler_flags=compiler_flags,
                rm_cmd=rm_cmd)
            writer.write('makefile', makefile_tmp)
        
        # build the project
        if compile:
            with in_directory(directory):
                if compiler=='msvc':
                    # TODO: handle debug
                    if debug:
                        logger.warn('Debug flag currently ignored for MSVC')
                    vcvars_search_paths = [
                        # futureproofing!
                        r'c:\Program Files\Microsoft Visual Studio 15.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 15.0\VC\vcvarsall.bat',
                        r'c:\Program Files\Microsoft Visual Studio 14.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat',
                        r'c:\Program Files\Microsoft Visual Studio 13.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 13.0\VC\vcvarsall.bat',
                        r'c:\Program Files\Microsoft Visual Studio 12.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat',
                        r'c:\Program Files\Microsoft Visual Studio 11.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat',
                        r'c:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat',
                        r'c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat',
                        ]
                    vcvars_loc = prefs['codegen.cpp.msvc_vars_location']
                    if vcvars_loc=='':
                        for fname in vcvars_search_paths:
                            if os.path.exists(fname):
                                vcvars_loc = fname
                                break
                    if vcvars_loc=='':
                        raise IOError("Cannot find vcvarsall.bat on standard search path.")
                    # TODO: copy vcvars and make replacements for 64 bit automatically
                    arch_name = prefs['codegen.cpp.msvc_architecture']
                    if arch_name=='':
                        mach = platform.machine()
                        if mach=='AMD64':
                            arch_name = 'x86_amd64'
                        else:
                            arch_name = 'x86'
                    
                    vcvars_cmd = '"{vcvars_loc}" {arch_name}'.format(
                            vcvars_loc=vcvars_loc, arch_name=arch_name)
                    make_cmd = 'nmake /f win_makefile'
                    if os.path.exists('winmake.log'):
                        os.remove('winmake.log')
                    with std_silent(debug):
                        if clean:
                            os.system('%s >>winmake.log 2>&1 && %s clean >>winmake.log 2>&1' % (vcvars_cmd, make_cmd))
                        x = os.system('%s >>winmake.log 2>&1 && %s >>winmake.log 2>&1' % (vcvars_cmd, make_cmd))
                        if x!=0:
                            raise RuntimeError("Project compilation failed")
                else:
                    with std_silent(debug):
                        if clean:
                            os.system('make clean')
                        if debug:
                            x = os.system('make debug')
                        elif native:
                            x = os.system('make native')
                        else:
                            x = os.system('make')
                        if x!=0:
                            raise RuntimeError("Project compilation failed")
                if run:
                    if not with_output:
                        stdout = open(os.devnull, 'w')
                    else:
                        stdout = None
                    if os.name=='nt':
                        x = subprocess.call(['main'] + run_args, stdout=stdout)
                    else:
                        x = subprocess.call(['./main'] + run_args, stdout=stdout)
                    if x:
                        raise RuntimeError("Project run failed")
                    self.has_been_run = True