diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..5269b25b61337047c50af1856802cc9b51f16da5 --- /dev/null +++ b/.flake8 @@ -0,0 +1,24 @@ +[flake8] +max-line-length = 88 +extend-ignore = + # for black + E203, + # zealous line lengths + E501, + # ambiguous varnames I ./. l etc + E741, +select = C,E,F,W,B,B950 +max_complexity = 15 +extend-exclude = + # 3rd party + ext, + python/pybind11, + # auto-generated + doc/scripts/inputs.py + doc/scripts/make_images.py + # hidden + .*, + # artifacts + build, + # nah, don't care + spack/package.py diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b1c643f412bc3e6373fd6e248efebf637691a545..21ab7e3b61eb740b79b3d87fb1c6a486c5015148 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,6 +11,14 @@ jobs: strategy: fail-fast: false steps: + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.6 + - name: Get packages + run: | + python -m pip install --upgrade pip + pip install flake8 - name: Clone w/ submodules uses: actions/checkout@v2 with: @@ -18,5 +26,8 @@ jobs: - name: Python Formatting uses: psf/black@stable with: - options: --check --extend-exclude '/(ext|python/pybind11|doc/scripts/.*_theme)' + options: --check --extend-exclude '/(ext|python/pybind11|doc/scripts/.*_theme|doc/scripts/inputs.py)' src: scripts/build-catalogue.in . + - name: Python analysis + run: | + flake8 scripts/build-catalogue.in . diff --git a/doc/conf.py b/doc/conf.py index e6c2dd34cc97bd6d58fe75cae9300a90a58ced38..424686cf4477af1cf2269bef50db8072f2a5c09b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import sys, os -import subprocess as sp -from tempfile import TemporaryDirectory +import sys +import os # Add /scripts to path. Used for Furo theme and to generate images this_path = os.path.split(os.path.abspath(__file__))[0] @@ -58,7 +57,7 @@ img_path = this_path + "/gen-images" if not os.path.exists(img_path): os.mkdir(img_path) -import make_images +import make_images # noqa:E402 make_images.generate(img_path) diff --git a/doc/contrib/pr.rst b/doc/contrib/pr.rst index c22c59cddbfcefafa51b176aaa70e3ec3038e7ee..a47539ecb7eb920bd845d7b2e19d8353f940886f 100644 --- a/doc/contrib/pr.rst +++ b/doc/contrib/pr.rst @@ -129,6 +129,17 @@ Each pull request is reviewed according to these guidelines: summary as commit message. - Consider using Gitpod to review larger PRs, see under checks on the Github PR page. +.. _contribpr-lint: + +Pull requests will also be subject to a series of automated checks + +- Python formatting will be checked using the `black <https://black.readthedocs.io/en/stable/index.html>`__ formatter +- Python files will be checked for common errors and code smells using `flake8 <https://flake8.pycqa.org/en/latest/>`__ +- C++ code will be run against a suite of sanitizers under the `clang <https://clang.llvm.org/docs/index.html>`__ umbrella. The following checks are enabled + - `undefined behavior <https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html>`__: under/overflow, null-deref, ... + - `threads <https://clang.llvm.org/docs/ThreadSanitizer.html>`__: data races and other threading related issues + - `memory <https://clang.llvm.org/docs/AddressSanitizer.html>`__: illegal accesses, use-after-free, double free, ... + .. _contribpr-merge: Merging a PR diff --git a/doc/scripts/gen-labels.py b/doc/scripts/gen-labels.py index 8747e327c269a86a4ae7204539c9e3df403caa85..69dd8e362faca04cd13e12a75ef9c4072167e7a4 100644 --- a/doc/scripts/gen-labels.py +++ b/doc/scripts/gen-labels.py @@ -10,18 +10,16 @@ def is_collocated(l, r): def write_morphology(name, morph): - string = "tmp = [".format(name) + string = "tmp = [" for i in range(morph.num_branches): - first = True + last_dist = None sections = "[" for seg in morph.branch_segments(i): - if not first: + if last_dist is not None: if is_collocated((seg.prox.x, seg.prox.y), (last_dist.x, last_dist.y)): sections += ", " else: sections += "], [" - - first = False p = seg.prox d = seg.dist sections += "Segment(({}, {}, {}), ({}, {}, {}), {})".format( diff --git a/example/lfp/neuron_lfp_example.py b/example/lfp/neuron_lfp_example.py index d0953e8a5e2419088c74d849f6e47f5f72f28117..4b8b931566c0db76b4911d24c958a892a034581a 100755 --- a/example/lfp/neuron_lfp_example.py +++ b/example/lfp/neuron_lfp_example.py @@ -260,8 +260,8 @@ def plot_results(cell, electrode): xlim=[-150, 150], ylim=[-100, 600], title="morphology", - xlabel="x ($\mu$m)", - ylabel="y ($\mu$m)", + xlabel=r"x ($\mu$m)", + ylabel=r"y ($\mu$m)", ) ax_syn = fig.add_subplot( 332, ylabel="nA", title="synaptic current", xlabel="time (ms)" @@ -273,7 +273,7 @@ def plot_results(cell, electrode): 338, ylabel="nA", xlabel="time (ms)", title="membrane current" ) ax_ep = fig.add_subplot( - 133, ylabel="$\mu$V", xlabel="time (ms)", title="Extracellular potential" + 133, ylabel=r"$\mu$V", xlabel="time (ms)", title="Extracellular potential" ) plot_comp_idx = 0 diff --git a/python/__init__.py b/python/__init__.py index 17ccfc6569a84c8904a0d01bc944e0bb022cf1c1..cd72d942a3381f4d68fa081a81fcf970cbcbf55c 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4,7 +4,8 @@ # The library will be installed in the same path as this file, which will imports # the compiled part of the wrapper from the _arbor namespace. -from ._arbor import * +from ._arbor import * # noqa: F403 + # Parse VERSION file for the Arbor version string. def get_version(): @@ -16,7 +17,7 @@ def get_version(): __version__ = get_version() -__config__ = config() +__config__ = config() # noqa:F405 # Remove get_version from arbor module. del get_version diff --git a/python/example/brunel.py b/python/example/brunel.py index 5455ca69b25d5879487ff25384a451674de1f36d..a147e7378402a30567abe8891541652318cb9309 100755 --- a/python/example/brunel.py +++ b/python/example/brunel.py @@ -6,20 +6,22 @@ import numpy as np from numpy.random import RandomState """ -A Brunel network consists of nexc excitatory LIF neurons and ninh inhibitory LIF neurons. -Each neuron in the network receives in_degree_prop * nexc excitatory connections -chosen randomly, in_degree_prop * ninh inhibitory connections and next (external) Poisson connections. -All the connections have the same delay. The strenght of excitatory and Poisson connections is given by -parameter weight, whereas the strength of inhibitory connections is rel_inh_strength * weight. -Poisson neurons all spike independently with expected number of spikes given by parameter poiss_lambda. -Because of the refractory period, the activity is mostly driven by Poisson neurons and -recurrent connections have a small effect. +A Brunel network consists of nexc excitatory LIF neurons and ninh inhibitory +LIF neurons. Each neuron in the network receives in_degree_prop * nexc +excitatory connections chosen randomly, in_degree_prop * ninh inhibitory +connections and next (external) Poisson connections. All the connections have +the same delay. The strenght of excitatory and Poisson connections is given by +parameter weight, whereas the strength of inhibitory connections is +rel_inh_strength * weight. Poisson neurons all spike independently with expected +number of spikes given by parameter poiss_lambda. Because of the refractory +period, the activity is mostly driven by Poisson neurons and recurrent +connections have a small effect. Call with parameters, for example: ./brunel.py -n 400 -m 100 -e 20 -p 0.1 -w 1.2 -d 1 -g 0.5 -l 5 -t 100 -s 1 -G 50 -S 123 -f spikes.txt - """ + # Samples m unique values in interval [start, end) - gid. # We exclude gid because we don't want self-loops. def sample_subset(gen, gid, start, end, m): diff --git a/python/example/gap_junctions.py b/python/example/gap_junctions.py index 8d5ce0841de596619ac027bf51368490cb9f1ec7..2076f02a34850d19eaf752eedf2b9f79f379b1f7 100644 --- a/python/example/gap_junctions.py +++ b/python/example/gap_junctions.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 import arbor -import pandas, seaborn +import pandas +import seaborn import matplotlib.pyplot as plt # Construct chains of cells linked with gap junctions, @@ -28,7 +29,7 @@ def make_cable_cell(gid): ) # Single dendrite with radius 2 μm and length 40 μm, (tag = 2) - b = tree.append(s, arbor.mpoint(0, 0, 0, 2), arbor.mpoint(40, 0, 0, 2), tag=2) + tree.append(s, arbor.mpoint(0, 0, 0, 2), arbor.mpoint(40, 0, 0, 2), tag=2) # Label dictionary for cell components labels = arbor.label_dict() diff --git a/python/example/network_ring.py b/python/example/network_ring.py index 9cb97a3a72385886b2c0839eabef7b58cd390bc7..3de7bfb74effbc5a080b634587f1fc86a5fcd7f9 100755 --- a/python/example/network_ring.py +++ b/python/example/network_ring.py @@ -2,7 +2,8 @@ # This script is included in documentation. Adapt line numbers if touched. import arbor -import pandas, seaborn +import pandas +import seaborn from math import sqrt # Construct a cell with the following morphology. @@ -25,19 +26,19 @@ def make_cable_cell(gid): arbor.mnpos, arbor.mpoint(-12, 0, 0, 6), arbor.mpoint(0, 0, 0, 6), tag=1 ) - # Single dendrite (tag=3) of length 50 μm and radius 2 μm attached to soma. + # (b0) Single dendrite (tag=3) of length 50 μm and radius 2 μm attached to soma. b0 = tree.append(s, arbor.mpoint(0, 0, 0, 2), arbor.mpoint(50, 0, 0, 2), tag=3) # Attach two dendrites (tag=3) of length 50 μm to the end of the first dendrite. - # Radius tapers from 2 to 0.5 μm over the length of the dendrite. - b1 = tree.append( + # (b1) Radius tapers from 2 to 0.5 μm over the length of the dendrite. + tree.append( b0, arbor.mpoint(50, 0, 0, 2), arbor.mpoint(50 + 50 / sqrt(2), 50 / sqrt(2), 0, 0.5), tag=3, ) - # Constant radius of 1 μm over the length of the dendrite. - b2 = tree.append( + # (b2) Constant radius of 1 μm over the length of the dendrite. + tree.append( b0, arbor.mpoint(50, 0, 0, 1), arbor.mpoint(50 + 50 / sqrt(2), -50 / sqrt(2), 0, 1), diff --git a/python/example/network_ring_mpi.py b/python/example/network_ring_mpi.py index 7ccd2d53bd2dbbb7b4c25f4c85340ac643ebea3d..b56e81e8cd2699a177d76953eb9c77694b165ffa 100644 --- a/python/example/network_ring_mpi.py +++ b/python/example/network_ring_mpi.py @@ -2,7 +2,7 @@ # This script is included in documentation. Adapt line numbers if touched. import arbor -import pandas, seaborn +import pandas from math import sqrt # Run with srun -n NJOBS python network_ring_mpi.py @@ -31,15 +31,16 @@ def make_cable_cell(gid): b0 = tree.append(s, arbor.mpoint(0, 0, 0, 2), arbor.mpoint(50, 0, 0, 2), tag=3) # Attach two dendrites (tag=3) of length 50 μm to the end of the first dendrite. - # Radius tapers from 2 to 0.5 μm over the length of the dendrite. - b1 = tree.append( + # As there's no further use for them, we discard the returned handles. + # (b1) Radius tapers from 2 to 0.5 μm over the length of the dendrite. + _ = tree.append( b0, arbor.mpoint(50, 0, 0, 2), arbor.mpoint(50 + 50 / sqrt(2), 50 / sqrt(2), 0, 0.5), tag=3, ) - # Constant radius of 1 μm over the length of the dendrite. - b2 = tree.append( + # (b2) Constant radius of 1 μm over the length of the dendrite. + _ = tree.append( b0, arbor.mpoint(50, 0, 0, 1), arbor.mpoint(50 + 50 / sqrt(2), -50 / sqrt(2), 0, 1), diff --git a/python/example/network_ring_mpi_plot.py b/python/example/network_ring_mpi_plot.py index 594cbd655a36fc19471a4ec4a3838fa90f83621f..d5ea5d325b4dec805c9b2463f28ee7801b96be0a 100644 --- a/python/example/network_ring_mpi_plot.py +++ b/python/example/network_ring_mpi_plot.py @@ -2,7 +2,8 @@ # This script is included in documentation. Adapt line numbers if touched. import glob -import pandas, seaborn +import pandas +import seaborn results = glob.glob("result_mpi_*.csv") diff --git a/python/example/single_cell_allen.py b/python/example/single_cell_allen.py index 511fd33b7eba30888b5359988582342447078dfa..1127619907aec9606709caa3372ee3fc18941da0 100644 --- a/python/example/single_cell_allen.py +++ b/python/example/single_cell_allen.py @@ -7,6 +7,7 @@ import seaborn import pandas import matplotlib.pyplot as plt + # (3) A function that parses the Allen parameter fit file into components for an arbor.decor # NB. Needs to be adjusted when using a different model def load_allen_fit(fit): diff --git a/python/example/single_cell_detailed_recipe.py b/python/example/single_cell_detailed_recipe.py index dd658f66be94fa062b77f70ceacef5013faf98cc..ef1a4497e3a62e19c2506f8e1ee7d2dfa70d9f4e 100644 --- a/python/example/single_cell_detailed_recipe.py +++ b/python/example/single_cell_detailed_recipe.py @@ -88,6 +88,7 @@ cell = arbor.cable_cell(morph, labels, decor) probe = arbor.cable_probe_membrane_voltage('"custom_terminal"') + # (6) Create a class that inherits from arbor.recipe class single_recipe(arbor.recipe): diff --git a/python/example/single_cell_model.py b/python/example/single_cell_model.py index 74df6778d599f15b7311cd7e15b78ae013a1fbc0..9510178248812b67b955e1214ace4052e9fc8487 100755 --- a/python/example/single_cell_model.py +++ b/python/example/single_cell_model.py @@ -2,7 +2,8 @@ # This script is included in documentation. Adapt line numbers if touched. import arbor -import pandas, seaborn # You may have to pip install these. +import pandas # You may have to pip install these. +import seaborn # You may have to pip install these. # (1) Create a morphology with a single (cylindrical) segment of length=diameter=6 μm tree = arbor.segment_tree() diff --git a/python/example/single_cell_nml.py b/python/example/single_cell_nml.py index 395f1f73e8fa7e7e167011cfc7d8b1d6d05617de..7082cd6a293108e173ebcb99c65b80892d7703bd 100755 --- a/python/example/single_cell_nml.py +++ b/python/example/single_cell_nml.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import arbor from arbor import mechanism as mech -from arbor import location as loc -import pandas, seaborn +import pandas +import seaborn import sys # Load a cell morphology from an nml file. diff --git a/python/example/single_cell_recipe.py b/python/example/single_cell_recipe.py index facd5c8d171797bfe681c04ae13498d106eb0174..cace98de20d25ea1458307b906bdefe7dfd98564 100644 --- a/python/example/single_cell_recipe.py +++ b/python/example/single_cell_recipe.py @@ -2,7 +2,8 @@ # This script is included in documentation. Adapt line numbers if touched. import arbor -import pandas, seaborn # You may have to pip install these. +import pandas # You may have to pip install these. +import seaborn # You may have to pip install these. # The corresponding generic recipe version of `single_cell_model.py`. diff --git a/python/example/single_cell_swc.py b/python/example/single_cell_swc.py index 0a808b8507c575c3b9de524eafe80649e9df7462..06bdbf581944e26dd1f8f910a51ce138bd4f7ffe 100755 --- a/python/example/single_cell_swc.py +++ b/python/example/single_cell_swc.py @@ -12,8 +12,8 @@ import arbor from arbor import mechanism as mech -from arbor import location as loc -import pandas, seaborn +import pandas +import seaborn import sys # Load a cell morphology from an swc file. diff --git a/python/test/fixtures.py b/python/test/fixtures.py index 7885b12af3a76260548f849785bdf17a74e2428c..1e82a8b0b6860795523e87c8854ec52096163826 100644 --- a/python/test/fixtures.py +++ b/python/test/fixtures.py @@ -1,10 +1,8 @@ import arbor import functools from functools import lru_cache as cache -import unittest from pathlib import Path import subprocess -import warnings import atexit _mpi_enabled = arbor.__config__["mpi"] diff --git a/python/test/unit/test_cable_probes.py b/python/test/unit/test_cable_probes.py index d2a66057e16889120fbedf7e1d976a26d7dc4ccd..e49b3930efd611e44d00412f4c6837c3af03386c 100644 --- a/python/test/unit/test_cable_probes.py +++ b/python/test/unit/test_cable_probes.py @@ -2,7 +2,6 @@ import unittest import arbor as A -from .. import fixtures """ tests for cable probe wrappers diff --git a/python/test/unit/test_catalogues.py b/python/test/unit/test_catalogues.py index 6ef96f91f2022aa2948c521a9313b244d1c64fa4..7e9608dfd29deab1fe04ec92c43faf14c2744a4c 100644 --- a/python/test/unit/test_catalogues.py +++ b/python/test/unit/test_catalogues.py @@ -15,7 +15,7 @@ class recipe(arb.recipe): self.props = arb.neuron_cable_properties() try: self.props.catalogue = arb.load_catalogue("dummy-catalogue.so") - except: + except Exception: print("Catalogue not found. Are you running from build directory?") raise self.props.catalogue = arb.default_catalogue() diff --git a/python/test/unit/test_clear_samplers.py b/python/test/unit/test_clear_samplers.py index 062affb73cc35fb91ff47399f34ed97748fc3d38..f294c69f9aa834f42310aa3f53fd8c6badd5f6bc 100644 --- a/python/test/unit/test_clear_samplers.py +++ b/python/test/unit/test_clear_samplers.py @@ -6,9 +6,8 @@ import unittest import arbor as A import numpy as np -# to be able to run .py file from child directory -import sys, os -from .. import fixtures, cases +from .. import fixtures +from .. import cases """ all tests for the simulator wrapper diff --git a/python/test/unit/test_contexts.py b/python/test/unit/test_contexts.py index c5aecc7890a3a4d4c7bf9988c9a2fa2f0fae11dd..2f550c55298ae32b51f3739d8c45d9beb47eedc7 100644 --- a/python/test/unit/test_contexts.py +++ b/python/test/unit/test_contexts.py @@ -5,7 +5,6 @@ import unittest import arbor as arb -from .. import fixtures """ all tests for non-distributed arb.context diff --git a/python/test/unit/test_decor.py b/python/test/unit/test_decor.py index e51aec5168525f8b8afd5324e661a982b7405c0a..59dcf0d90081f590903f69fb12fc9e5757ecd223 100644 --- a/python/test/unit/test_decor.py +++ b/python/test/unit/test_decor.py @@ -3,8 +3,6 @@ import unittest import arbor as A -from .. import fixtures - """ Tests for decor and decoration wrappers. TODO: Coverage for more than just iclamp. diff --git a/python/test/unit/test_domain_decompositions.py b/python/test/unit/test_domain_decompositions.py index a7aa303ea7cd0c0455dfcd7d54d90a90ea83ed3e..b08ef2b1738bb0659d8cb0bf8a8190e1938bf549 100644 --- a/python/test/unit/test_domain_decompositions.py +++ b/python/test/unit/test_domain_decompositions.py @@ -5,7 +5,6 @@ import unittest import arbor as arb -from .. import fixtures # check Arbor's configuration of mpi and gpu gpu_enabled = arb.__config__["gpu"] @@ -14,6 +13,7 @@ gpu_enabled = arb.__config__["gpu"] all tests for non-distributed arb.domain_decomposition """ + # Dummy recipe class homo_recipe(arb.recipe): def __init__(self, n=4): @@ -76,7 +76,7 @@ class TestDomain_Decompositions(unittest.TestCase): self.assertEqual(grp.kind, arb.cell_kind.cable) # 1 cpu core, 1 gpu; assumes all cells will be placed on gpu in a single cell group - @unittest.skipIf(gpu_enabled == False, "GPU not enabled") + @unittest.skipIf(gpu_enabled is False, "GPU not enabled") def test_domain_decomposition_homogenous_GPU(self): n_cells = 10 recipe = homo_recipe(n_cells) @@ -139,7 +139,7 @@ class TestDomain_Decompositions(unittest.TestCase): self.assertEqual(k, recipe.cell_kind(gid)) # 1 cpu core, 1 gpu; assumes cable cells will be placed on gpu in a single cell group; spike cells are on cpu in cell groups of size 1 - @unittest.skipIf(gpu_enabled == False, "GPU not enabled") + @unittest.skipIf(gpu_enabled is False, "GPU not enabled") def test_domain_decomposition_heterogenous_GPU(self): n_cells = 10 recipe = hetero_recipe(n_cells) @@ -237,7 +237,7 @@ class TestDomain_Decompositions(unittest.TestCase): RuntimeError, "unable to perform load balancing because cell_kind::cable has invalid suggested cpu_cell_group size of 0", ): - decomp = arb.partition_load_balance(recipe, context, hints) + arb.partition_load_balance(recipe, context, hints) cable_hint = arb.partition_hint() cable_hint.prefer_gpu = False @@ -256,4 +256,4 @@ class TestDomain_Decompositions(unittest.TestCase): RuntimeError, "unable to perform load balancing because cell_kind::spike_source has invalid suggested gpu_cell_group size of 0", ): - decomp = arb.partition_load_balance(recipe, context, hints) + arb.partition_load_balance(recipe, context, hints) diff --git a/python/test/unit/test_event_generators.py b/python/test/unit/test_event_generators.py index b992e9e8ecc473341fd8d106524ca7e6ee7d1da1..b43387029ceed3777b9e9ebd5c5b060751984f5b 100644 --- a/python/test/unit/test_event_generators.py +++ b/python/test/unit/test_event_generators.py @@ -5,7 +5,6 @@ import unittest import arbor as arb -from .. import fixtures """ all tests for event generators (regular, explicit, poisson) diff --git a/python/test/unit/test_identifiers.py b/python/test/unit/test_identifiers.py index f682e56919102158408a5af12cafd6c7d8e265e7..69c2948ef584129dd9e46504ab800ad17eec224a 100644 --- a/python/test/unit/test_identifiers.py +++ b/python/test/unit/test_identifiers.py @@ -5,7 +5,7 @@ import unittest import arbor as arb -from .. import fixtures + """ all tests for identifiers, indexes, kinds diff --git a/python/test/unit/test_morphology.py b/python/test/unit/test_morphology.py index 90454ada6f0eab0754c37baf260d18f722b92352..4d0eb7a55189c80c3c4d1d0236a8f15148dfa325 100644 --- a/python/test/unit/test_morphology.py +++ b/python/test/unit/test_morphology.py @@ -6,7 +6,6 @@ import unittest import arbor as A import numpy as N import math -from .. import fixtures """ tests for morphology-related classes @@ -115,7 +114,6 @@ class TestPlacePwlin(unittest.TestCase): x0p = iso(s0p) x0d = iso(s0d) x1p = iso(s1p) - x1d = iso(s1d) L0 = place.at(A.location(0, 0)) L0s = place.all_at(A.location(0, 0)) diff --git a/python/test/unit/test_multiple_connections.py b/python/test/unit/test_multiple_connections.py index 37b0f16e20790602b0154e82fa7fc446699e752b..b23326c43ccab3a1f6ec6887cee964387dfa5b69 100644 --- a/python/test/unit/test_multiple_connections.py +++ b/python/test/unit/test_multiple_connections.py @@ -10,13 +10,20 @@ import arbor as arb from .. import fixtures """ -tests for multiple connections onto the same postsynaptic label and for one connection that has the same net impact as the multiple-connection paradigm, -thereby testing the selection policies 'round_robin', 'round_robin_halt', and 'univalent' - -NOTE: In principle, a plasticity (STDP) mechanism is employed here to test if a selected connection uses the correct instance of the mechanism. - Thus, the scenario in Test #1 is intentionally "a wrong one", as opposed to the scenario in Test #2. In Test #1, one presynaptic neuron effectively connects _via one synapse_ to two postsynaptic neurons, - and the spike at t=0.8ms in presynaptic neuron 0 will enhance potentiation in both the first and the second synapse mechanism. In Test #2, this is prevented by the 'round_robin_halt' policy, whereby the - potentiation in the second synapse mechanism is only enhanced by spikes of presynaptic neuron 1. +Tests for multiple connections onto the same postsynaptic label and for one +connection that has the same net impact as the multiple-connection paradigm, +thereby testing the selection policies 'round_robin', 'round_robin_halt', and +'univalent' + +NOTE: In principle, a plasticity (STDP) mechanism is employed here to test if a + selected connection uses the correct instance of the mechanism. Thus, the + scenario in Test #1 is intentionally "a wrong one", as opposed to the + scenario in Test #2. In Test #1, one presynaptic neuron effectively + connects _via one synapse_ to two postsynaptic neurons, and the spike at + t=0.8ms in presynaptic neuron 0 will enhance potentiation in both the + first and the second synapse mechanism. In Test #2, this is prevented by + the 'round_robin_halt' policy, whereby the potentiation in the second + synapse mechanism is only enhanced by spikes of presynaptic neuron 1. """ diff --git a/python/test/unit/test_profiling.py b/python/test/unit/test_profiling.py index 0c0d484d597a0d715f40a25779c352b931be3bc5..9249573c87c3b947f6d2f690a9aa4ef98bb69fff 100644 --- a/python/test/unit/test_profiling.py +++ b/python/test/unit/test_profiling.py @@ -6,7 +6,6 @@ import unittest import arbor as arb import functools -from .. import fixtures """ all tests for profiling diff --git a/python/test/unit/test_schedules.py b/python/test/unit/test_schedules.py index c3394bf49335921c23849e1f17f7c2166c6e86d1..0a7c747ec111c21b86d8d46c4f1dfdfc60941959 100644 --- a/python/test/unit/test_schedules.py +++ b/python/test/unit/test_schedules.py @@ -5,7 +5,6 @@ import unittest import arbor as arb -from .. import fixtures """ all tests for schedules (regular, explicit, poisson) diff --git a/python/test/unit_distributed/test_contexts_arbmpi.py b/python/test/unit_distributed/test_contexts_arbmpi.py index d1b980052f55469d8c117c0e00b2a3081c91b9d3..fca089bf414a1fa4759cc23dbb63674130adcb90 100644 --- a/python/test/unit_distributed/test_contexts_arbmpi.py +++ b/python/test/unit_distributed/test_contexts_arbmpi.py @@ -5,7 +5,7 @@ import unittest import arbor as arb -from .. import fixtures, cases +from .. import cases """ all tests for distributed arb.context using arbor mpi wrappers diff --git a/python/test/unit_distributed/test_contexts_mpi4py.py b/python/test/unit_distributed/test_contexts_mpi4py.py index 0d7a3554572f351d0d834d545c7ce0adc4865e14..335907f7f44a9f8a01c9e8da8ed785a7ec3dbdb5 100644 --- a/python/test/unit_distributed/test_contexts_mpi4py.py +++ b/python/test/unit_distributed/test_contexts_mpi4py.py @@ -5,7 +5,7 @@ import unittest import arbor as arb -from .. import fixtures, cases +from .. import cases # check Arbor's configuration of mpi mpi_enabled = arb.__config__["mpi"] @@ -17,6 +17,8 @@ if mpi_enabled and mpi4py_enabled: """ all tests for distributed arb.context using mpi4py """ + + # Only test class if env var ARB_WITH_MPI4PY=ON @cases.skipIfNotDistributed() class TestContexts_mpi4py(unittest.TestCase): diff --git a/python/test/unit_distributed/test_domain_decompositions.py b/python/test/unit_distributed/test_domain_decompositions.py index 29df3496e33ae3365ff16d088300d7835715e13d..e19bd55c0f67d1cc6fcafeb2b0d7ae677d272c67 100644 --- a/python/test/unit_distributed/test_domain_decompositions.py +++ b/python/test/unit_distributed/test_domain_decompositions.py @@ -5,7 +5,7 @@ import unittest import arbor as arb -from .. import fixtures, cases +from .. import cases # check Arbor's configuration of mpi and gpu mpi_enabled = arb.__config__["mpi"] @@ -15,6 +15,7 @@ gpu_enabled = arb.__config__["gpu"] all tests for distributed arb.domain_decomposition """ + # Dummy recipe class homo_recipe(arb.recipe): def __init__(self, n=4): @@ -173,7 +174,7 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): context = arb.context(threads=1, gpu_id=None) N = context.ranks - I = context.rank + R = context.rank # 10 cells per domain n_local = 10 @@ -186,12 +187,12 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): self.assertEqual(decomp.num_global_cells, n_global) self.assertEqual(len(decomp.groups), n_local) - b = I * n_local - e = (I + 1) * n_local + b = R * n_local + e = (R + 1) * n_local gids = list(range(b, e)) for gid in gids: - self.assertEqual(I, decomp.gid_domain(gid)) + self.assertEqual(R, decomp.gid_domain(gid)) # Each cell group contains 1 cell of kind cable # Each group should also be tagged for cpu execution @@ -206,7 +207,7 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): self.assertEqual(grp.kind, arb.cell_kind.cable) # 1 node with 1 cpu core, 1 gpu; assumes all cells will be placed on gpu in a single cell group - @unittest.skipIf(gpu_enabled == False, "GPU not enabled") + @unittest.skipIf(gpu_enabled is False, "GPU not enabled") def test_domain_decomposition_homogenous_GPU(self): if mpi_enabled: @@ -216,7 +217,7 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): context = arb.context(threads=1, gpu_id=0) N = context.ranks - I = context.rank + R = context.rank # 10 cells per domain n_local = 10 @@ -229,12 +230,13 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): self.assertEqual(decomp.num_global_cells, n_global) self.assertEqual(len(decomp.groups), 1) - b = I * n_local - e = (I + 1) * n_local + b = R * n_local + e = (R + 1) * n_local + gids = list(range(b, e)) for gid in gids: - self.assertEqual(I, decomp.gid_domain(gid)) + self.assertEqual(R, decomp.gid_domain(gid)) # Each cell group contains 1 cell of kind cable # Each group should also be tagged for gpu execution @@ -256,7 +258,7 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): context = arb.context(threads=1, gpu_id=None) N = context.ranks - I = context.rank + R = context.rank # 10 cells per domain n_local = 10 @@ -270,12 +272,13 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): self.assertEqual(decomp.num_global_cells, n_global) self.assertEqual(len(decomp.groups), n_local) - b = I * n_local - e = (I + 1) * n_local + b = R * n_local + e = (R + 1) * n_local + gids = list(range(b, e)) for gid in gids: - self.assertEqual(I, decomp.gid_domain(gid)) + self.assertEqual(R, decomp.gid_domain(gid)) # Each cell group contains 1 cell of kind cable # Each group should also be tagged for cpu execution @@ -422,12 +425,10 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): def test_domain_decomposition_exceptions(self): nranks = 1 - rank = 0 if mpi_enabled: comm = arb.mpi_comm() context = arb.context(threads=1, gpu_id=None, mpi=comm) nranks = context.ranks - rank = context.rank else: context = arb.context(threads=1, gpu_id=None) @@ -442,7 +443,7 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): RuntimeError, "unable to perform load balancing because cell_kind::cable has invalid suggested cpu_cell_group size of 0", ): - decomp1 = arb.partition_load_balance(recipe, context, hints1) + arb.partition_load_balance(recipe, context, hints1) hint2 = arb.partition_hint() hint2.prefer_gpu = True @@ -453,4 +454,4 @@ class TestDomain_Decompositions_Distributed(unittest.TestCase): RuntimeError, "unable to perform load balancing because cell_kind::cable has invalid suggested gpu_cell_group size of 0", ): - decomp2 = arb.partition_load_balance(recipe, context, hints2) + arb.partition_load_balance(recipe, context, hints2) diff --git a/python/test/unit_distributed/test_simulator.py b/python/test/unit_distributed/test_simulator.py index ea657a9526a4360b1680036de2d766b4e48a4659..50f3ca7b226c698898a006c2c26968a877b7d835 100644 --- a/python/test/unit_distributed/test_simulator.py +++ b/python/test/unit_distributed/test_simulator.py @@ -3,9 +3,8 @@ # test_simulator.py import unittest -import numpy as np import arbor as A -from .. import fixtures, cases +from .. import cases mpi_enabled = A.__config__["mpi"] @@ -56,7 +55,7 @@ class lifN_recipe(A.recipe): class TestSimulator(unittest.TestCase): def init_sim(self): comm = A.mpi_comm() - context = A.context(threads=1, gpu_id=None, mpi=A.mpi_comm()) + context = A.context(threads=1, gpu_id=None, mpi=comm) self.rank = context.rank self.ranks = context.ranks diff --git a/scripts/build-catalogue.in b/scripts/build-catalogue.in index 9e3384ebec2953e538a7cbe872887b8603874f04..0dc73ce73b5b420fab4180fe6ea722e52c570748 100755 --- a/scripts/build-catalogue.in +++ b/scripts/build-catalogue.in @@ -2,11 +2,10 @@ import subprocess as sp import sys -from tempfile import mkdtemp, TemporaryDirectory +from tempfile import mkdtemp import os from pathlib import Path import shutil -import string import argparse import re @@ -205,6 +204,8 @@ if debug: def __exit__(*args, **kwargs): pass +else: + from tempfile import TemporaryDirectory with TemporaryDirectory() as tmp: tmp = Path(tmp) @@ -246,7 +247,7 @@ with TemporaryDirectory() as tmp: sp.run(make_cmd, shell=True, check=True, stdout=out, stderr=err) shutil.copy2(f"{name}-catalogue.so", pwd) except sp.CalledProcessError as e: - import sys, traceback as tb + import traceback as tb if not verbose: # Not in verbose mode, so we have captured the diff --git a/scripts/patchwheel.py b/scripts/patchwheel.py index ef7262c4e04b11bf3a2e3c697e4c25373db244c3..c1c6ed46a65186509c54bfe864da6e24ff910a1f 100644 --- a/scripts/patchwheel.py +++ b/scripts/patchwheel.py @@ -1,4 +1,6 @@ -import shutil, subprocess, argparse +import shutil +import subprocess +import argparse from pathlib import Path diff --git a/scripts/where.py b/scripts/where.py index 08307a661f0aac33dcf39aa9ab2e02ca7c503cd8..24790ab67621447a0b4abe18296915f73970ceb3 100644 --- a/scripts/where.py +++ b/scripts/where.py @@ -1,4 +1,5 @@ -import sys, sysconfig +import sys +import sysconfig pfx = sys.stdin.read() try: diff --git a/setup.py b/setup.py index ecf8dbcc4d672004613967fc54f7ade4cb50946a..7511cd70a39c603025b58be2661f849b74003b78 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ from pathlib import Path from sys import executable as python from skbuild import setup -import os, platform # Hard coded options, because scikit-build does not do build options. # Override by instructing CMAKE, e.g.: diff --git a/validation/ref/neuron/ball_and_squiggle.py b/validation/ref/neuron/ball_and_squiggle.py index 63d015897ea16f095ec3f636d2c267aefd58ff4e..ae3aab67199d7df45cbafe95e318c4b6243f1c09 100644 --- a/validation/ref/neuron/ball_and_squiggle.py +++ b/validation/ref/neuron/ball_and_squiggle.py @@ -7,10 +7,15 @@ import nrn_validation as V V.override_defaults_from_args() + # dendrite geometry: 100 µm long, varying diameter. length = 100.0 npoints = 200 -radius = lambda x: math.exp(-x) * (math.sin(40 * x) * 0.05 + 0.1) + 0.1 + + +def radius(x): + return math.exp(-x) * (math.sin(40 * x) * 0.05 + 0.1) + 0.1 + xs = [float(i) / (npoints - 1) for i in range(npoints)] geom = [(length * x, 2.0 * radius(x)) for x in xs] diff --git a/validation/ref/neuron/nrn_validation.py b/validation/ref/neuron/nrn_validation.py index 5952b97842f2b8c63e7aa2edb38505062d795cc6..72df64e7533133db79c2bb3eee9b142c0a2435b4 100644 --- a/validation/ref/neuron/nrn_validation.py +++ b/validation/ref/neuron/nrn_validation.py @@ -5,7 +5,6 @@ import sys import os import re import numpy as np -import neuron from neuron import h # This is super annoying: without neuron.gui, need @@ -277,7 +276,6 @@ def run_nrn_sim(tend, sample_dt=0.025, report_t=None, report_dt=None, dt=None, * ) # and section reports too - vreport_t = list(vreport_t_hoc) for name, length, nseg, ps, vs in vreports: obs = np.column_stack([np.array(v) for v in vs]) xs = [length * p for p in ps]