Running Brian 2 on a Macbook with Apple M1

Description of problem

I am a PhD student and trying to run Brian 2 on a new Macbook pro built with an Apple M1 chip. I’ve successfully installed everything following the brian 2 document for “installation” :Installation — Brian 2 2.5.4 documentation. Everything seemed to work until I ran the brian.test(). The test fails and returns to the following (super long) error message. I am not sure whether this is due to any issue from Cython or due to my computer’s chip. Thanks for your help!

Minimal code to reproduce problem

import brian2
brian2.test()

What you have already tried

Expected output (if relevant)

Actual output (if relevant)

Full traceback of error (if relevant)

===================== FAILURES =========================
_______________________________ test_active_flag _______________________________

self = <distutils.unixccompiler.UnixCCompiler object at 0x7fdf9139ed60>
obj = ‘/Users/cathykuo/Library/Caches/cython/brian_extensions/Users/cathykuo/Library/Caches/cython/brian_extensions/_cython_magic_9dcc7799a45467bb394cbcbcb72df0e2.o’
src = ‘/Users/cathykuo/Library/Caches/cython/brian_extensions/_cython_magic_9dcc7799a45467bb394cbcbcb72df0e2.cpp’
ext = ‘.cpp’
cc_args = [‘-I/opt/anaconda3/lib/python3.8/site-packages/brian2/synapses’, ‘-I/opt/anaconda3/include’, ‘-I/opt/anaconda3/lib/pyt…e/include’, ‘-I/opt/anaconda3/lib/python3.8/site-packages/brian2/synapses’, ‘-I/opt/anaconda3/include/python3.8’, ‘-c’]
extra_postargs = [‘-w’, ‘-O3’, ‘-ffast-math’, ‘-fno-finite-math-only’, ‘-march=native’, ‘-std=c++11’, …]
pp_opts = [‘-I/opt/anaconda3/lib/python3.8/site-packages/brian2/synapses’, ‘-I/opt/anaconda3/include’, ‘-I/opt/anaconda3/lib/pyt…py/core/include’, ‘-I/opt/anaconda3/lib/python3.8/site-packages/brian2/synapses’, ‘-I/opt/anaconda3/include/python3.8’]

def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
    compiler_so = self.compiler_so
    if sys.platform == 'darwin':
        compiler_so = _osx_support.compiler_fixup(compiler_so,
                                                cc_args + extra_postargs)
    try:
      self.spawn(compiler_so + cc_args + [src, '-o', obj] +
                   extra_postargs)

/opt/anaconda3/lib/python3.8/distutils/unixccompiler.py:117:


self = <distutils.unixccompiler.UnixCCompiler object at 0x7fdf9139ed60>
cmd = [‘x86_64-apple-darwin13.4.0-clang’, ‘-fno-strict-aliasing’, ‘-Wsign-compare’, ‘-Wunreachable-code’, ‘-DNDEBUG’, ‘-fwrapv’, …]

def spawn(self, cmd):
  spawn(cmd, dry_run=self.dry_run)

/opt/anaconda3/lib/python3.8/distutils/ccompiler.py:910:


cmd = [‘x86_64-apple-darwin13.4.0-clang’, ‘-fno-strict-aliasing’, ‘-Wsign-compare’, ‘-Wunreachable-code’, ‘-DNDEBUG’, ‘-fwrapv’, …]
search_path = 1, verbose = 0, dry_run = 0

def spawn(cmd, search_path=1, verbose=0, dry_run=0):
    """Run another program, specified as a command list 'cmd', in a new process.

    'cmd' is just the argument list for the new process, ie.
    cmd[0] is the program to run and cmd[1:] are the rest of its arguments.
    There is no way to run a program with a name different from that of its
    executable.

    If 'search_path' is true (the default), the system's executable
    search path will be used to find the program; otherwise, cmd[0]
    must be the exact path to the executable.  If 'dry_run' is true,
    the command will not actually be run.

    Raise DistutilsExecError if running the program fails in any way; just
    return on success.
    """
    # cmd is documented as a list, but just in case some code passes a tuple
    # in, protect our %-formatting code against horrible death
    cmd = list(cmd)
    if os.name == 'posix':
      _spawn_posix(cmd, search_path, dry_run=dry_run)

/opt/anaconda3/lib/python3.8/distutils/spawn.py:36:


cmd = ‘x86_64-apple-darwin13.4.0-clang’, search_path = 1, verbose = 0
dry_run = 0

def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
    log.info(' '.join(cmd))
    if dry_run:
        return
    executable = cmd[0]
    exec_fn = search_path and os.execvp or os.execv
    env = None
    if sys.platform == 'darwin':
        global _cfg_target, _cfg_target_split
        if _cfg_target is None:
            from distutils import sysconfig
            _cfg_target = sysconfig.get_config_var(
                                  'MACOSX_DEPLOYMENT_TARGET') or ''
            if _cfg_target:
                _cfg_target_split = [int(x) for x in _cfg_target.split('.')]
        if _cfg_target:
            # ensure that the deployment target of build process is not less
            # than that used when the interpreter was built. This ensures
            # extension modules are built with correct compatibility values
            cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target)
            if _cfg_target_split > [int(x) for x in cur_target.split('.')]:
                my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: '
                          'now "%s" but "%s" during configure'
                                % (cur_target, _cfg_target))
                raise DistutilsPlatformError(my_msg)
            env = dict(os.environ,
                       MACOSX_DEPLOYMENT_TARGET=cur_target)
            exec_fn = search_path and os.execvpe or os.execve
    pid = os.fork()
    if pid == 0: # in the child
        try:
            if env is None:
                exec_fn(executable, cmd)
            else:
                exec_fn(executable, cmd, env)
        except OSError as e:
            if not DEBUG:
                cmd = executable
            sys.stderr.write("unable to execute %r: %s\n"
                             % (cmd, e.strerror))
            os._exit(1)

        if not DEBUG:
            cmd = executable
        sys.stderr.write("unable to execute %r for unknown reasons" % cmd)
        os._exit(1)
    else: # in the parent
        # Loop until the child either exits or is terminated by a signal
        # (ie. keep waiting if it's merely stopped)
        while True:
            try:
                pid, status = os.waitpid(pid, 0)
            except OSError as exc:
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError(
                      "command %r failed: %s" % (cmd, exc.args[-1]))
            if os.WIFSIGNALED(status):
                if not DEBUG:
                    cmd = executable
                raise DistutilsExecError(
                      "command %r terminated by signal %d"
                      % (cmd, os.WTERMSIG(status)))
            elif os.WIFEXITED(status):
                exit_status = os.WEXITSTATUS(status)
                if exit_status == 0:
                    return   # hey, it succeeded!
                else:
                    if not DEBUG:
                        cmd = executable
                  raise DistutilsExecError(
                          "command %r failed with exit status %d"

E distutils.errors.DistutilsExecError: command ‘x86_64-apple-darwin13.4.0-clang’ failed with exit status 1

/opt/anaconda3/lib/python3.8/distutils/spawn.py:157: DistutilsExecError

During handling of the above exception, another exception occurred:

self = MagicNetwork()
run_namespace = {‘@py_builtins’: <module ‘builtins’ (built-in)>, ‘@pytest_ar’: <module ‘_pytest.assertion.rewrite’ from ‘/opt/anaconda…e-packages/_pytest/assertion/rewrite.py’>, ‘ALLOW_THREADS’: 1, ‘Annotation’: <class ‘matplotlib.text.Annotation’>, …}

@device_override('network_before_run')
def before_run(self, run_namespace):
    '''
    before_run(namespace)

    Prepares the `Network` for a run.

    Objects in the `Network` are sorted into the correct running order, and
    their `BrianObject.before_run` methods are called.

    Parameters
    ----------
    run_namespace : dict-like, optional
        A namespace in which objects which do not define their own
        namespace will be run.
    '''
    all_objects = self.sorted_objects
    prefs.check_all_validated()

    # Check names in the network for uniqueness
    names = [obj.name for obj in all_objects]
    non_unique_names = [name for name, count in Counter(names).items()
                        if count > 1]
    if len(non_unique_names):
        formatted_names = ', '.join("'%s'" % name
                                    for name in non_unique_names)
        raise ValueError('All objects in a network need to have unique '
                         'names, the following name(s) were used more than '
                         'once: %s' % formatted_names)

    # Check that there are no SummedVariableUpdaters targeting the same
    # target variable
    _check_multiple_summed_updaters(all_objects)

    self._stopped = False
    Network._globally_stopped = False

    device = get_device()
    if device.network_schedule is not None:
        # The device defines a fixed network schedule
        if device.network_schedule != self.schedule:
            # TODO: The human-readable name of a device should be easier to get
            device_name = list(all_devices.keys())[list(all_devices.values()).index(device)]
            logger.warn(("The selected device '{device_name}' only "
                         "supports a fixed schedule, but this schedule is "
                         "not consistent with the network's schedule. The "
                         "simulation will use the device's schedule.\n"
                         "Device schedule: {device.network_schedule}\n"
                         "Network schedule: {net.schedule}\n"
                         "Set the network schedule explicitly or set the "
                         "core.network.default_schedule preference to "
                         "avoid this warning.").format(device_name=device_name,
                                                       device=device,
                                                       net=self),
                        name_suffix='schedule_conflict', once=True)

    logger.debug("Preparing network {self.name} with {numobj} "
                 "objects: {objnames}".format(self=self,
                    numobj=len(all_objects),
                    objnames=', '.join(obj.name for obj in all_objects)),
                 "before_run")

    self.check_dependencies()

    for obj in all_objects:
        if obj.active:
            try:
              obj.before_run(run_namespace)

/opt/anaconda3/lib/python3.8/site-packages/brian2/core/network.py:901:


self = StateUpdater(clock=Clock(dt=100. * usecond, name=‘defaultclock’), when=groups, order=0, name=‘neurongroup_stateupdater’)
run_namespace = {‘@py_builtins’: <module ‘builtins’ (built-in)>, ‘@pytest_ar’: <module ‘_pytest.assertion.rewrite’ from ‘/opt/anaconda…e-packages/_pytest/assertion/rewrite.py’>, ‘ALLOW_THREADS’: 1, ‘Annotation’: <class ‘matplotlib.text.Annotation’>, …}

def before_run(self, run_namespace):
  self.create_code_objects(run_namespace)

/opt/anaconda3/lib/python3.8/site-packages/brian2/groups/group.py:1142:


self = StateUpdater(clock=Clock(dt=100. * usecond, name=‘defaultclock’), when=groups, order=0, name=‘neurongroup_stateupdater’)
run_namespace = {‘@py_builtins’: <module ‘builtins’ (built-in)>, ‘@pytest_ar’: <module ‘_pytest.assertion.rewrite’ from ‘/opt/anaconda…e-packages/_pytest/assertion/rewrite.py’>, ‘ALLOW_THREADS’: 1, ‘Annotation’: <class ‘matplotlib.text.Annotation’>, …}

def create_code_objects(self, run_namespace):
    # By default, we only have one code object for each CodeRunner.
    # Overwrite this function to use more than one.
  code_object = self.create_default_code_object(run_namespace)

/opt/anaconda3/lib/python3.8/site-packages/brian2/groups/group.py:1135:


self = StateUpdater(clock=Clock(dt=100. * usecond, name=‘defaultclock’), when=groups, order=0, name=‘neurongroup_stateupdater’)
run_namespace = {‘@py_builtins’: <module ‘builtins’ (built-in)>, ‘@pytest_ar’: <module ‘_pytest.assertion.rewrite’ from ‘/opt/anaconda…e-packages/_pytest/assertion/rewrite.py’>, ‘ALLOW_THREADS’: 1, ‘Annotation’: <class ‘matplotlib.text.Annotation’>, …}

def create_default_code_object(self, run_namespace):
    self.update_abstract_code(run_namespace=run_namespace)
    # If the CodeRunner has variables, add them
    if hasattr(self, 'variables'):
        additional_variables = self.variables
    else:
        additional_variables = None

    if not self.generate_empty_code and len(self.abstract_code) == 0:
        self.codeobj = None
    else:
      self.codeobj = create_runner_codeobj(group=self.group,
                                             code=self.abstract_code,
                                             user_code=self.user_code,
                                             template_name=self.template,
                                             name=self.name + '_codeobject*',
                                             check_units=self.check_units,
                                             additional_variables=additional_variables,
                                             needed_variables=self.needed_variables,
                                             run_namespace=run_namespace,
                                             template_kwds=self.template_kwds,
                                             override_conditional_write=self.override_conditional_write,
                                             codeobj_class=self.codeobj_class
                                             )

/opt/anaconda3/lib/python3.8/site-packages/brian2/groups/group.py:1117:

(…the full error message is beyond the max limit of characters so I skipped some…)

/opt/anaconda3/lib/python3.8/site-packages/brian2/core/network.py:903: BrianObjectException
----------------------------- Captured stderr call -----------------------------
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
In file included from /Users/cathykuo/Library/Caches/cython/brian_extensions/_cython_magic_265d9c8c34e36a4acc03218bcadca636.cpp:32:
In file included from /opt/anaconda3/include/python3.8/Python.h:25:
/opt/anaconda3/bin/…/include/c++/v1/stdio.h:107:15: fatal error: ‘stdio.h’ file not found
#include_next <stdio.h>
^~~~~~~~~
1 error generated.
clang-9: error: unknown argument: ‘-invalidxyz’
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
clang-9: warning: -Wl,-export_dynamic: ‘linker’ input unused [-Wunused-command-line-argument]
In file included from /Users/cathykuo/Library/Caches/cython/brian_extensions/_cython_magic_9dcc7799a45467bb394cbcbcb72df0e2.cpp:47:
In file included from /opt/anaconda3/include/python3.8/Python.h:25:
/opt/anaconda3/bin/…/include/c++/v1/stdio.h:107:15: fatal error: ‘stdio.h’ file not found
#include_next <stdio.h>
^~~~~~~~~
1 error generated.

Hi @bwGiuhub,

We did some tests for Brian2 performance on the Apple M1 processor but inside a docker container. You can find the docker file for this image in Oleksii Leonov’s git repository. It seems docker is the best way to go until Linux will natively run on M1.

Not sure it helps, but you have an option to work around the problem at least.