Contributing to Elephant

You are here to help with Elephant? Awesome, feel welcome to get in touch with us by asking questions, proposing features and improvements to Elephant.

For guidelines on code documentation, please see the Documentation Guide below.

Note

We highly recommend to get in touch with us before starting to implement a new feature in Elephant. This way, we can point out synergies with complementary efforts and help in designing your implementation such that its integration into Elephant will be an easy process.

Get in touch

Using the mailing list

General discussion of Elephant development takes place in the NeuralEnsemble Google group.

Please raise concrete issues, bugs, and feature requests on the issue tracker.

Using the issue tracker

If you find a bug in Elephant, please create a new ticket on the issue tracker. Choose one of the available templates - “Bug report”, “Feature request”, or “Questions”. Choose an issue title that is as specific as possible to the problem you’ve found, and in the description give as much information as you think is necessary to recreate the problem. The best way to do this is to create the shortest possible Python script that demonstrates the problem, and attach the file to the ticket.

If you have an idea for an improvement to Elephant, create a ticket with type “enhancement”. If you already have an implementation of the idea, open a pull request.

Setting up a development environment

In order to contribute to the Elephant code, you must set up a Python environment on your local machine.

  1. Fork Elephant as described in Fork a repo. Download Elephant source code from your forked repo:

    $ git clone git://github.com/<your-github-profile>/elephant.git
    $ cd elephant
    
  2. Set up the virtual environment (either via pip or conda):

conda env create -f requirements/environment.yml
conda activate elephant
pip install -r requirements/requirements-tests.txt

Install elephant development version:

pip install -e .
  1. Before you make any changes, run the test suite to make sure all the tests pass on your system:

    $ pytest .
    

    You can specify a particular module to test, for example test_statistics.py:

    $ pytest elephant/test/test_statistics.py
    

    At the end, if you see “OK”, then all the tests passed (or were skipped because certain dependencies are not installed), otherwise it will report on tests that failed or produced errors.

Adding new functionality

After you’ve set up a development environment, implement a functionality you want to add in Elephant. This includes, in particular:

  • fixing a bug;

  • improving the documentation;

  • adding a new functionality.

Writing code

Imagine that you want to add a novel statistical measure of a list of spike trains that returns an analog signal in the existing module elephant/statistics.py. Let’s call it statistics_x.

Elephant relies on Neo structures that use Quantities extensively, allowing the users to conveniently specify physical units (e.g., seconds and milliseconds) of input objects. Typically, to avoid computationally expensive quantities rescaling operation on large input arrays, we check that the main data objects - spike trains or analog signals - share the same units and rescale additional parameters (t_start, t_stop, bin_size, etc.) to the units of input objects.

import neo
import quantities as pq

from elephant.utils import check_same_units


def statistics_x(spiketrains, t_start=None, t_stop=None):
    """
    Compute the X statistics of spike trains.

    Parameters
    ----------
    spiketrains : list of neo.SpikeTrain
        Input spike trains.
    t_start, t_stop : pq.Quantity or None
        Start and stop times to compute the statistics over the specified
        interval. If None, extracted from the input spike trains.

    Returns
    -------
    signal : neo.AnalogSignal
        The X statistics of input spike trains.
        (More description follows.)

    """
    check_same_units(spiketrains, object_type=neo.SpikeTrain)

    # alternatively, if spiketrains are required to be aligned in time,
    # when t_start and t_stop are not specified, use 'check_neo_consistency'
    # check_neo_consistency(spiketrains, object_type=neo.SpikeTrain, t_start=t_start, t_stop=t_stop)

    # convert everything to spiketrain units and strip off the units
    if t_start is None:
        t_start = spiketrains[0].t_start
    if t_stop is None:
        t_stop = spiketrains[0].t_stop
    units = spiketrains[0].units
    t_start = t_start.rescale(units).item()
    t_stop = t_stop.rescale(units).item()
    spiketrains = [spiketrain.magnitude for spiketrain in spiketrains]

    # do the analysis here on unit-less spike train arrays
    x = ...

    signal = neo.AnalogSignal(x,
                              units=...,
                              t_start=t_start,
                              sampling_rate=...,
                              name="X statistics of spiketrains",
                              ...)
    return signal

Testing code

Write at least one test in elephant/test/test_module_name.py file that covers the functionality.

For example, to check the correctness of the implemented statistics_x function, we add unittest code in elephant/test/test_statistics.py, something like

import unittest

import neo
import quantities as pq
from numpy.testing import assert_array_almost_equal

from elephant.statistics import statistics_x


class StatisticsXTestCase(unittest.TestCase):
    def test_statistics_x_correctness(self):
        spiketrain1 = neo.SpikeTrain([0.3, 4.5, 7.8], t_stop=10, units='s')
        spiketrain2 = neo.SpikeTrain([2.4, 5.6], t_stop=10, units='s')
        result = statistics_x([spiketrain1, spiketrain2])
        self.assertIsInstance(result, neo.AnalogSignal)
        self.assertEqual(result.t_start, 0 * pq.s)
        expected_magnitude = [0, 1, 2]
        assert_array_almost_equal(result.magnitude, expected_magnitude)
        ...  # more checking

Pushing the changes and creating a pull request

Now you’re ready to share the code publicly.

  1. Commit your changes:

    git add .
    git commit -m "informative commit message"
    git push
    

    If this is your first commitment to Elephant, please add your name and affiliation/employer in doc/authors.rst

  2. Open a pull request. Then we will guide you through the process of merging your code into Elephant.

That’s all! We’re happy to assist you throughout the process of contributing.

If you experience any problems during one of the steps below, please contact us and we’ll help you.

Documentation Guide

Writing documentation

Each module (python source file) should start with a short description of the listed functionality. Class and function docstrings should conform to the NumPy docstring standard.

Note

Please refer to our Coding Style Guide with Examples.

Building documentation

The documentation in doc/ folder is written in reStructuredText, using the Sphinx documentation system. To build the documentation locally on a Linux system, follow these steps:

  1. Install requirements-docs.txt and requirements-tutorials.txt the same way it’s explained in Setting up a development environment step 3:

    pip install -r requirements/requirements-docs.txt
    pip install -r requirements/requirements-tutorials.txt
    
  2. Build the documentation:

    cd doc
    export PYTHONPATH=${PYTHONPATH}.:../..
    make html
    

    PYTHONPATH environmental variable is set in order to find Elephant package while executing jupyter notebooks that are part of the documentation. You may also need to install LaTeX support:

    sudo apt-get install texlive-full
    
  3. Open _build/html/index.html in your browser.

  4. (Optional) To check that all URLs in the documentation are correct, run:

    make linkcheck
    

Citations

The citations are in BibTeX format, stored in doc/bib/elephant.bib.

Entries in the .bib are built according to the following specification:

{Last name of first author}+{last two digits of publication year}+"_"+{first page of article}

Example

According to this specification, the following article:

  1. Grün. Data-driven significance estimation of precise spike correlation. J. Neurophysiol., pages 1126–1140, 2009. doi:10.1152/jn.00093.2008.

is added to the .bib-file as Gruen09_1126. The complete entry for this example is:

@article{Gruen09_1126,
  title={Data-driven significance estimation of precise spike correlation},
  author={Gr\"{u}n, S.},
  year={2009},
  journal={J. Neurophysiol.},
  number={101},
  pages={1126--1140},
  doi={10.1152/jn.00093.2008}
}

Each module in doc/reference folder ends with the reference section:

References
----------

.. bibliography::
    :keyprefix: <module name>-

where <module name> is (by convention) the Python source file name, and <module name shortcut> is what will be displayed to the users.

For example, :cite:'spade-Torre2013_132' will be rendered as sp1 in the built HTML documentation, if <module name shortcut> is set to sp and <module name> - to spade.

To cite Elephant itself, refer to Citing Elephant.