Skip to content
Snippets Groups Projects
Unverified Commit 45885c22 authored by Benjamin Cumming's avatar Benjamin Cumming Committed by GitHub
Browse files

pip / setuptools support (#977)

Add a `setup.py` for installing Arbor directly with pip/setuptools.

Implement a setuptools extension for CMake in `setup.py`. To be honest, I don't understand entirely how it works, but setuptools is obtuse enough that I don't feel at all guilty about this.

Define additional flags for optionally enabling GPUs, MPI, Vectorization and micro-architecture targets, for more adventurous users.

The documentation is updated with a "howto pip" for more casual users who don't want anything to do with CMake.

Fixes #958 .
parent 46cc831c
No related branches found
No related tags found
No related merge requests found
......@@ -29,10 +29,10 @@ with very few tools.
=========== ============================================
Git To check out the code, minimum version 2.0.
CMake To set up the build, minimum version 3.9
compiler A C++14 compiler. See `compilers <compilers_>`_.
compiler A C++14 compiler. See `compilers <install-compilers_>`_.
=========== ============================================
.. _compilers:
.. _install-compilers:
Compilers
~~~~~~~~~
......@@ -120,7 +120,7 @@ Python
Arbor has a Python frontend, for which a minimum of Python 3.6 is required.
In order to use MPI in combination with the python frontend the
`mpi4py <https://mpi4py.readthedocs.io/en/stable/install.html#>`_
Python package is recommended. See :ref:`pythonfrontend` for more information.
Python package is recommended. See :ref:`install-python` for more information.
Documentation
......@@ -129,7 +129,7 @@ Documentation
To build a local copy of the html documentation that you are reading now, you will need to
install `Sphinx <http://www.sphinx-doc.org/en/master/>`_.
.. _downloading:
.. _install-downloading:
Getting the Code
================
......@@ -217,13 +217,13 @@ CMake parameters and flags, follow links to the more detailed descriptions below
cmake -DARB_WITH_ASSERTIONS=ON -DCMAKE_BUILD_TYPE=debug
.. topic:: `Release <buildtarget_>`_ mode (compiler optimizations enabled) with the default
compiler, optimized for the local `system architecture <architecture_>`_.
compiler, optimized for the local `system architecture <install-architecture_>`_.
.. code-block:: bash
cmake -DARB_ARCH=native
.. topic:: `Release <buildtarget_>`_ mode with `Clang <compilers_>`_.
.. topic:: `Release <buildtarget_>`_ mode with `Clang <install-compilers_>`_.
.. code-block:: bash
......@@ -231,13 +231,13 @@ CMake parameters and flags, follow links to the more detailed descriptions below
export CXX=`which clang++`
cmake
.. topic:: `Release <buildtarget_>`_ mode for the `Haswell architecture <architecture_>`_ and `explicit vectorization <vectorize_>`_ of kernels.
.. topic:: `Release <buildtarget_>`_ mode for the `Haswell architecture <install-architecture_>`_ and `explicit vectorization <install-vectorize_>`_ of kernels.
.. code-block:: bash
cmake -DARB_VECTORIZE=ON -DARB_ARCH=haswell
.. topic:: `Release <buildtarget_>`_ mode with `explicit vectorization <vectorize_>`_, targeting the `Broadwell architecture <vectorize_>`_, with support for `P100 GPUs <gpu_>`_, and building with `GCC 6 <compilers_>`_.
.. topic:: `Release <buildtarget_>`_ mode with `explicit vectorization <install-vectorize_>`_, targeting the `Broadwell architecture <install-vectorize_>`_, with support for `P100 GPUs <install-gpu_>`_, and building with `GCC 6 <install-compilers_>`_.
.. code-block:: bash
......@@ -245,7 +245,7 @@ CMake parameters and flags, follow links to the more detailed descriptions below
export CXX=g++-6
cmake -DARB_VECTORIZE=ON -DARB_ARCH=broadwell -DARB_WITH_GPU=ON
.. topic:: `Release <buildtarget_>`_ mode with `explicit vectorization <vectorize_>`_, optimized for the `local system architecture <architecture_>`_ and `install <install_>`_ in ``/opt/arbor``
.. topic:: `Release <buildtarget_>`_ mode with `explicit vectorization <install-vectorize_>`_, optimized for the `local system architecture <install-architecture_>`_ and `install <install_>`_ in ``/opt/arbor``
.. code-block:: bash
......@@ -264,7 +264,7 @@ with ``-g -O0`` flags), use the ``CMAKE_BUILD_TYPE`` CMake parameter.
cmake -DCMAKE_BUILD_TYPE={debug,release}
.. _architecture:
.. _install-architecture:
Architecture
------------
......@@ -306,7 +306,7 @@ and `ARM options <https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html>`_.
# IBM Power8
cmake -DARB_ARCH=power8
.. _vectorize:
.. _install-vectorize:
Vectorization
-------------
......@@ -324,7 +324,7 @@ With this flag set, the library will use architecture-specific vectorization int
to implement these kernels. Arbor currently has vectorization support for x86 architectures
with AVX, AVX2 or AVX512 ISA extensions, and for ARM architectures with support for AArch64 NEON intrinsics (first available on ARMv8-A).
.. _gpu:
.. _install-gpu:
GPU Backend
-----------
......@@ -355,7 +355,7 @@ example:
Arbor supports and has been tested on the Kepler (K20 & K80), Pascal (P100) and Volta (V100) GPUs
.. _pythonfrontend:
.. _install-python:
Python Frontend
----------------
......@@ -461,6 +461,8 @@ on your target system that are not covered here, please make an issue on the
Arbor `Github issues <https://github.com/arbor-sim/arbor/issues>`_ page.
We will do our best to help you directly, and update this guide to help other users.
.. _install-mpi:
MPI
---
......@@ -730,7 +732,7 @@ CMake Git Submodule Warnings
----------------------------
When running CMake, warnings like the following indicate that the Git submodules
need to be `updated <downloading_>`_.
need to be `updated <install-downloading_>`_.
.. code-block:: none
......
......@@ -9,15 +9,13 @@ The getting started guides will introduce Arbor via the Python interface.
To test that Arbor is available, try the following in a `Python 3 <python2_>`_ interpreter:
.. container:: example-code
.. code-block:: python
.. code-block:: python
>>> import arbor
>>> print(arbor.__config__)
{'mpi': True, 'mpi4py': True, 'gpu': False, 'version': '0.2.3-dev'}
>>> print(arbor.__version__)
0.2.3-dev
>>> import arbor
>>> print(arbor.__config__)
{'mpi': True, 'mpi4py': True, 'gpu': False, 'version': '0.2.3-dev'}
>>> print(arbor.__version__)
0.2.3-dev
The dictionary ``arbor.__config__`` contains information about the Arbor installation.
This can be used to check that Arbor supports features that you require to run your model,
......@@ -25,20 +23,11 @@ or even to dynamically decide how to run a model.
Single cell models like do not require parallelism like
that provided by MPI or GPUs, so the ``'mpi'`` and ``'gpu'`` fields can be ``False``.
Installing
-------------
Before starting Arbor needs to be installed with the Python interface enabled,
following the :ref:`Python configuration <pythonfrontend>` in
the
:ref:`installation guide <installarbor>`.
Performance
--------------
The Python interface can incur significant performance overheads relative to C++
during the *model building* phase, however simulation performance is be the same
The Python interface can incur significant memory and runtime overheads relative to C++
during the *model building* phase, however simulation performance is the same
for both interfaces.
.. _python2:
......@@ -50,6 +39,105 @@ Python 2 reached `end of life <https://pythonclock.org/>`_ in January 2020.
Arbor only officially supports Python 3.6 and later, and all examples in the
documentation are in Python 3. While it is possible to install and run Arbor
using Python 2.7 by setting the ``PYTHON_EXECUTABLE`` variable when
configuring CMake, support is only provided for using Arbor with Python 3.6
and later.
:ref:`configuring CMake <install-python>`, support is only provided for using
Arbor with Python 3.6 and later.
Installing
-------------
Before starting Arbor needs to be installed with the Python interface enabled.
The easiest way to get started with the Python interface is to install Arbor using
`pip <https://packaging.python.org/tutorials/installing-packages>`_.
Installing with pip requires two steps:
**(1)** Obtain Arbor source code from GitHub;
**(2)** then use pip to compile and install the Arbor package in one shot.
.. code-block:: bash
git clone https://github.com/arbor-sim/arbor.git --recursive
# use pip (recommended)
pip3 install ./arbor
# use setuptools and python directly
python3 install ./arbor/setup.py
This will install Arbor as a site-wide package with only multi-threading enabled.
To enable more advanced forms of parallelism, the following flags can optionally
be passed to the pip install command:
* ``--mpi``: enable mpi support (requires MPI library).
* ``--gpu``: enable nvidia cuda support (requires cudaruntime and nvcc).
* ``--vec``: enable vectorization. This might require carefully choosing the ``--arch`` flag.
* ``--arch``: cpu micro-architecture to target. By default this is set to ``native``.
If calling ``setup.py`` the flags must to come after ``install`` on the command line,
and if being passed to pip they must be passed via ``--install-option``. The examples
below demonstrate this for both pip and ``setup.py``, with pip being our recommend method.
Vanilla install with no additional options/features enabled:
.. code-block:: bash
pip3 install ./arbor
python3 ./arbor/setup.py install
Enable MPI support. This might require loading an MPI module or setting the ``CC`` and ``CXX``
:ref:`environment variables <install-mpi>`.
.. code-block:: bash
pip3 install --install-option='--mpi' ./arbor
python3 ./arbor/setup.py install --mpi
Compile with :ref:`vectorization <install-vectorize>` on a system with SkyLake
:ref:`architecture <install-architecture>`:
.. code-block:: bash
pip3 install --install-option='--vec' --install-option='--arch=skylake' ./arbor
python3 ./arbor/setup.py install --vec --arch=skylake
Compile with support for NVIDIA GPUs. This requires that the :ref:`CUDA toolkit <install-gpu>`
is installed and the CUDA compiler nvcc is available:
.. code-block:: bash
pip3 install --install-option='--gpu' ./arbor
python3 ./arbor/setup.py install --gpu
.. Note::
Installation takes a while because pip has to compile the Arbor C++ library and
wrapper, which takes a few minutes. Pass the ``--verbose`` flag to pip
to see the individual steps being preformed if concerned that progress
is halting.
.. Note::
Detailed instructions on how to install using CMake are in the
:ref:`Python configuration <install-python>` section of the
:ref:`installation guide <installarbor>`.
CMake is recommended for developers, integration with package managers such as
Spack and EasyBuild, and users who require fine grained control over compilation
and installation.
.. Note::
If there is an error installing with pip you want to report,
run pip with the ``--verbose`` flag, and attach the output (along with
the pip command itself) to a ticket on the
`Arbor GitHub <https://github.com/arbor-sim/arbor/issues>`_.
For example, ``pip3 install --install-option='--mpi' --verbose .``.
Dependencies
^^^^^^^^^^^^^
If a downstream dependency of Arbor that requires Arbor be built with
a specific feature enabled, use ``requirements.txt`` to
`define the constraints <https://pip.pypa.io/en/stable/reference/pip_install/#per-requirement-overrides>`_.
For example, a package that depends on `arbor` would version 0.3 or later
with MPI support would add the following to its requirements.
.. code-block:: python
arbor >= 0.3 --install-option='--gpu' \
--install-option='--mpi'
......@@ -6,10 +6,15 @@
from ._arbor import *
import os
# Parse VERSION file for the Arbor version string.
def get_version():
import os
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'VERSION')) as version_file:
return version_file.read().strip()
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'VERSION')) as version_file:
__version__ = version_file.read().strip()
__version__ = get_version()
__config__ = config()
__config__ = config()
# Remove get_version from arbor module.
del get_version
setup.py 0 → 100644
import os
import sys
import setuptools
import pathlib
from setuptools import Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install
import subprocess
# VERSION is in the same path as setup.py
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'VERSION')) as version_file:
version_ = version_file.read().strip()
def check_cmake():
try:
out = subprocess.check_output(['cmake', '--version'])
return True
except OSError:
return False
# Extend the command line options available to the install phase.
# These arguments must come after `install` on the command line, e.g.:
# python3 setup.py install --mpi --arch=skylake
# pip3 install --install-option '--mpi' --install-option '--arch=skylake' .
class install_command(install):
user_options = install.user_options + [
('mpi', None, 'enable mpi support (requires MPI library)'),
('gpu', None, 'enable nvidia cuda support (requires cudaruntime and nvcc)'),
('vec', None, 'enable vectorization'),
('arch=', None, 'cpu architecture, e.g. haswell, skylake, armv8-a'),
]
def initialize_options(self):
install.initialize_options(self)
self.mpi = None
self.gpu = None
self.arch = None
self.vec = None
def finalize_options(self):
install.finalize_options(self)
def run(self):
# The options are stored in a dictionary cl_opt, with the following keys:
# 'mpi' : build with MPI support (boolean).
# 'gpu' : build with CUDA support (boolean).
# 'vec' : generate SIMD vectorized kernels for CPU micro-architecture (boolean).
# 'arch' : target CPU micro-architecture (string).
global cl_opt
cl_opt = {
'mpi' : self.mpi is not None,
'gpu' : self.gpu is not None,
'vec' : self.vec is not None,
'arch': "native" if self.arch is None else self.arch
}
install.run(self)
class cmake_extension(Extension):
def __init__(self, name):
Extension.__init__(self, name, sources=[])
class cmake_build(build_ext):
def run(self):
if not check_cmake():
raise RuntimeError('CMake is not available. CMake 3.12 is required.')
# cl_opt contains the command line arguments passed to install, via
# --install-option if using pip.
# pip skips building wheels when --install-option flags are set.
# However, when no --install-options are passed, it runs build_ext
# without running install_command, required to create and set cl_opt.
# This hack works around this. I think that the upshot of this is
# that only wheels built with default configuration will be possible.
if 'cl_opt' not in globals():
cl_opt = {
'mpi': False,
'gpu': False,
'vec': False,
'arch': 'native'
}
# The path where CMake will be configured and Arbor will be built.
build_directory = os.path.abspath(self.build_temp)
# The path where the package will be copied after building.
lib_directory = os.path.abspath(self.build_lib)
# The path where the Python package will be compiled.
source_path = build_directory + '/python/arbor'
# Where to copy the package after it is built, so that whatever the next phase is
# can copy it into the target 'prefix' path.
dest_path = lib_directory + '/arbor'
cmake_args = [
'-DARB_WITH_PYTHON=on',
'-DPYTHON_EXECUTABLE=' + sys.executable,
'-DARB_WITH_MPI={}'.format('on' if cl_opt['mpi'] else 'off'),
'-DARB_WITH_GPU={}'.format('on' if cl_opt['gpu'] else 'off'),
'-DARB_VECTORIZE={}'.format('on' if cl_opt['vec'] else 'off'),
'-DARB_ARCH={}'.format(cl_opt['arch']),
]
print('-'*5, 'command line options: {}'.format(cl_opt))
print('-'*5, 'cmake arguments: {}'.format(cmake_args))
cfg = 'Debug' if self.debug else 'Release'
build_args = ['--config', cfg]
cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg]
# Assuming Makefiles
build_args += ['--', '-j4']
self.build_args = build_args
env = os.environ.copy()
env['CXXFLAGS'] = '{}'.format(env.get('CXXFLAGS', ''))
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
# CMakeLists.txt is in the same directory as this setup.py file
cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print('-'*20, 'Configure CMake')
subprocess.check_call(['cmake', cmake_list_dir] + cmake_args,
cwd=self.build_temp, env=env)
print('-'*20, 'Build')
cmake_cmd = ['cmake', '--build', '.'] + self.build_args
subprocess.check_call(cmake_cmd,
cwd=self.build_temp)
# Copy from build path to some other place from whence it will later be installed.
# ... or something like that
# ... setuptools is an enigma monkey patched on a mystery
if not os.path.exists(dest_path):
os.makedirs(dest_path, exist_ok=True)
self.copy_tree(source_path, dest_path)
setuptools.setup(
name='arbor',
version=version_,
python_requires='>=3.6',
install_requires=[],
setup_requires=[],
zip_safe=False,
ext_modules=[cmake_extension('arbor')],
cmdclass={
'build_ext': cmake_build,
'install': install_command,
},
author='The lovely Arbor devs.',
url='https://github.com/arbor-sim/arbor',
description='High performance simulation of networks of multicompartment neurons.',
long_description='',
classifiers=[
'Development Status :: 4 - Beta', # Upgrade to "5 - Production/Stable" on release of v0.3
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering :: Build Tools',
'License :: OSI Approved :: BSD License'
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
project_urls={
'Source': 'https://github.com/arbor-sim/arbor',
'Documentation': 'https://arbor.readthedocs.io',
'Bug Reports': 'https://github.com/arbor-sim/arbor/issues',
},
)
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment