1"""
2Tests of the Connector classes, using the pyNN.mock backend.
3
4:copyright: Copyright 2006-2021 by the PyNN team, see AUTHORS.
5:license: CeCILL, see LICENSE for details.
6"""
7
8import unittest
9
10from pyNN import connectors, random, errors, space, recording
11import numpy as np
12import os
13import sys
14from numpy.testing import assert_array_equal, assert_array_almost_equal
15from .mocks import MockRNG, MockRNG2
16import pyNN.mock as sim
17
18
19orig_mpi_get_config = random.get_mpi_config
20
21
22def setUp():
23    random.get_mpi_config = lambda: (0, 2)
24
25
26def tearDown():
27    random.get_mpi_config = orig_mpi_get_config
28
29
30class TestOneToOneConnector(unittest.TestCase):
31
32    def setUp(self, sim=sim, **extra):
33        sim.setup(num_processes=2, rank=0, **extra)
34        self.p1 = sim.Population(5, sim.IF_cond_exp())
35        self.p2 = sim.Population(5, sim.HH_cond_exp())
36        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
37
38    def tearDown(self, sim=sim):
39        sim.end()
40
41    def test_connect_with_scalar_weights_and_delays(self, sim=sim):
42        C = connectors.OneToOneConnector(safe=False)
43        syn = sim.StaticSynapse(weight=5.0, delay=0.5)
44        prj = sim.Projection(self.p1, self.p2, C, syn)
45        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
46                         [(1, 1, 5.0, 0.5),
47                          (3, 3, 5.0, 0.5)])
48
49    def test_connect_with_random_weights(self, sim=sim):
50        rd = random.RandomDistribution('uniform', (0, 1), rng=MockRNG(delta=1.0))
51        syn = sim.StaticSynapse(weight=rd, delay=0.5)
52        C = connectors.OneToOneConnector(safe=False)
53        prj = sim.Projection(self.p1, self.p2, C, syn)
54        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
55                         [(1, 1, 1.0, 0.5),
56                          (3, 3, 3.0, 0.5)])
57
58
59class TestAllToAllConnector(unittest.TestCase):
60
61    def setUp(self, sim=sim, **extra):
62        sim.setup(num_processes=2, rank=1, min_delay=0.123, **extra)
63        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
64        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
65        assert_array_equal(self.p1._mask_local, np.array([0, 1, 0, 1], dtype=bool))
66        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
67
68    def test_connect_with_scalar_weights_and_delays(self, sim=sim):
69        C = connectors.AllToAllConnector(safe=False)
70        syn = sim.StaticSynapse(weight=5.0, delay=0.5)
71        prj = sim.Projection(self.p1, self.p2, C, syn)
72        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
73                         [(0, 1, 5.0, 0.5),
74                          (1, 1, 5.0, 0.5),
75                          (2, 1, 5.0, 0.5),
76                          (3, 1, 5.0, 0.5),
77                          (0, 3, 5.0, 0.5),
78                          (1, 3, 5.0, 0.5),
79                          (2, 3, 5.0, 0.5),
80                          (3, 3, 5.0, 0.5)])
81        nan = np.nan
82        assert_array_equal(prj.get('weight', format='array', gather=False),
83                           np.array([[nan, 5.0, nan, 5.0, nan],
84                                        [nan, 5.0, nan, 5.0, nan],
85                                        [nan, 5.0, nan, 5.0, nan],
86                                        [nan, 5.0, nan, 5.0, nan]]))
87
88    def test_connect_with_array_weights(self, sim=sim):
89        C = connectors.AllToAllConnector(safe=False)
90        syn = sim.StaticSynapse(weight=np.arange(0.0, 2.0, 0.1).reshape(4, 5), delay=0.5)
91        prj = sim.Projection(self.p1, self.p2, C, syn)
92        assert_array_almost_equal(
93            # use gather False because we are faking the MPI
94            np.array(prj.get(["weight", "delay"], format='list', gather=False)),
95            np.array([(0, 1, 0.1, 0.5),
96                         (1, 1, 0.6, 0.5),
97                         (2, 1, 1.1, 0.5),
98                         (3, 1, 1.6, 0.5),
99                         (0, 3, 0.3, 0.5),
100                         (1, 3, 0.8, 0.5),
101                         (2, 3, 1.3, 0.5),
102                         (3, 3, 1.8, 0.5)]))
103        nan = np.nan
104        assert_array_almost_equal(prj.get('weight', format='array', gather=False),
105                                  np.array([[nan, 0.1, nan, 0.3, nan],
106                                               [nan, 0.6, nan, 0.8, nan],
107                                               [nan, 1.1, nan, 1.3, nan],
108                                               [nan, 1.6, nan, 1.8, nan]]),
109                                  9)
110
111    def test_connect_with_random_weights_parallel_safe(self, sim=sim):
112        rd = random.RandomDistribution(
113            'uniform', (0, 1), rng=MockRNG(delta=1.0, parallel_safe=True))
114        syn = sim.StaticSynapse(weight=rd, delay=0.5)
115        C = connectors.AllToAllConnector(safe=False)
116        prj = sim.Projection(self.p1, self.p2, C, syn)
117        # note that the outer loop is over the post-synaptic cells, the inner loop over the pre-synaptic
118        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
119                         [(0, 1, 4.0, 0.5),
120                          (1, 1, 5.0, 0.5),
121                          (2, 1, 6.0, 0.5),
122                          (3, 1, 7.0, 0.5),
123                          (0, 3, 12.0, 0.5),
124                          (1, 3, 13.0, 0.5),
125                          (2, 3, 14.0, 0.5),
126                          (3, 3, 15.0, 0.5)])
127        nan = np.nan
128        assert_array_almost_equal(prj.get('weight', format='array', gather=False),
129                                  np.array([[nan, 4.0, nan, 12.0, nan],
130                                               [nan, 5.0, nan, 13.0, nan],
131                                               [nan, 6.0, nan, 14.0, nan],
132                                               [nan, 7.0, nan, 15.0, nan]]),
133                                  9)
134
135    def test_connect_with_distance_dependent_weights(self, sim=sim):
136        d_expr = "d+100"
137        syn = sim.StaticSynapse(weight=d_expr, delay=0.5)
138        C = connectors.AllToAllConnector(safe=False)
139        prj = sim.Projection(self.p1, self.p2, C, syn)
140        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
141                         [(0, 1, 101.0, 0.5),
142                          (1, 1, 100.0, 0.5),
143                          (2, 1, 101.0, 0.5),
144                          (3, 1, 102.0, 0.5),
145                          (0, 3, 103.0, 0.5),
146                          (1, 3, 102.0, 0.5),
147                          (2, 3, 101.0, 0.5),
148                          (3, 3, 100.0, 0.5)])
149
150    def test_connect_with_distance_dependent_weights_and_delays(self, sim=sim):
151        syn = sim.StaticSynapse(weight="d+100", delay="0.2+2*d")
152        C = connectors.AllToAllConnector(safe=False)
153        prj = sim.Projection(self.p1, self.p2, C, syn)
154        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
155                         [(0, 1, 101.0, 2.2),
156                          (1, 1, 100.0, 0.2),
157                          (2, 1, 101.0, 2.2),
158                          (3, 1, 102.0, 4.2),
159                          (0, 3, 103.0, 6.2),
160                          (1, 3, 102.0, 4.2),
161                          (2, 3, 101.0, 2.2),
162                          (3, 3, 100.0, 0.2)])
163
164    def test_connect_with_delays_None(self, sim=sim):
165        syn = sim.StaticSynapse(weight=0.1, delay=None)
166        C = connectors.AllToAllConnector()
167        assert C.safe
168        assert C.allow_self_connections
169        prj = sim.Projection(self.p1, self.p2, C, syn)
170        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False)[
171                         0][3], prj._simulator.state.min_delay)
172
173    @unittest.skip('skipping this test until refactoring of delay checks is complete')
174    def test_connect_with_delays_too_small(self, sim=sim):
175        C = connectors.AllToAllConnector(safe=True)
176        syn = sim.StaticSynapse(weight=0.1, delay=0.0)
177        self.assertRaises(errors.ConnectionError, sim.Projection, self.p1, self.p2, C, syn)
178
179    @unittest.skip('skipping this tests until refactoring of delay checks is complete')
180    def test_connect_with_list_delays_too_small(self, sim=sim):
181        delays = np.ones((self.p1.size, self.p2.size), float)
182        delays[2, 3] = sim.Projection._simulator.state.min_delay - 0.01
183        syn = sim.StaticSynapse(weight=0.1, delay=delays)
184        C = connectors.AllToAllConnector()
185        self.assertRaises(errors.ConnectionError, sim.Projection, self.p1, self.p2, C, syn)
186
187
188class TestFixedProbabilityConnector(unittest.TestCase):
189
190    def setUp(self, sim=sim):
191        sim.setup(num_processes=2, rank=1, min_delay=0.123)
192        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
193        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
194        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
195
196    def test_connect_with_default_args(self, sim=sim):
197        C = connectors.FixedProbabilityConnector(p_connect=0.85,
198                                                 rng=MockRNG(delta=0.1, parallel_safe=True))
199        syn = sim.StaticSynapse()
200        prj = sim.Projection(self.p1, self.p2, C, syn)
201
202        # 20 possible connections. Due to the mock RNG, only the
203        # first 9 are created (0,0), (1,0), (2,0), (3,0), (0,1), (1,1), (2,1), (3,1), (0,2)
204        # of these, (0,1), (1,1), (2,1), (3,1) are created on this node
205        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
206                         [(0, 1, 0.0, 0.123),
207                          (1, 1, 0.0, 0.123),
208                          (2, 1, 0.0, 0.123),
209                          (3, 1, 0.0, 0.123)])
210
211    def test_connect_with_default_args_again(self, sim=sim):
212        C = connectors.FixedProbabilityConnector(p_connect=0.5,
213                                                 rng=MockRNG2(1 - np.array([1, 0, 0, 1,
214                                                                               0, 0, 0, 1,
215                                                                               1, 1, 0, 0,
216                                                                               1, 0, 1, 0,
217                                                                               1, 1, 0, 1]),
218                                                              parallel_safe=True))
219        syn = sim.StaticSynapse()
220        prj = sim.Projection(self.p1, self.p2, C, syn)
221
222        # 20 possible connections. Due to the mock RNG, only the following
223        # are created (0,0), (3,0), (3,1), (0,2), (1,2), (0,3), (2,3), (0,4), (1,4), (3,4)
224        # of these, (3,1), (0,3), (2,3) are created on this node
225        # (note that the outer loop is over post-synaptic cells (columns), the inner loop over pre-synaptic (rows))
226        nan = np.nan
227        assert_array_almost_equal(prj.get('delay', format='array', gather=False),
228                                  np.array([[nan, nan,   nan, 0.123, nan],
229                                               [nan, nan,   nan, nan,   nan],
230                                               [nan, nan,   nan, 0.123, nan],
231                                               [nan, 0.123, nan, nan,   nan]]),
232                                  9)
233        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
234                         [(3, 1, 0.0, 0.123),
235                          (0, 3, 0.0, 0.123),
236                          (2, 3, 0.0, 0.123)])
237
238    def test_connect_with_probability_1(self, sim=sim):
239        # see https://github.com/NeuralEnsemble/PyNN/issues/309
240        C = connectors.FixedProbabilityConnector(p_connect=1,
241                                                 rng=MockRNG(delta=0.01, parallel_safe=True))
242        syn = sim.StaticSynapse()
243        prj = sim.Projection(self.p1, self.p2, C, syn)
244
245        # 20 connections, only some of which are created on this node
246        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
247                         [(0, 1, 0.0, 0.123),
248                          (1, 1, 0.0, 0.123),
249                          (2, 1, 0.0, 0.123),
250                          (3, 1, 0.0, 0.123),
251                          (0, 3, 0.0, 0.123),
252                          (1, 3, 0.0, 0.123),
253                          (2, 3, 0.0, 0.123),
254                          (3, 3, 0.0, 0.123)
255                          ])
256
257    def test_connect_with_weight_function(self, sim=sim):
258        C = connectors.FixedProbabilityConnector(p_connect=0.85,
259                                                 rng=MockRNG(delta=0.1))
260        syn = sim.StaticSynapse(weight=lambda d: 0.1 * d)
261        prj = sim.Projection(self.p1, self.p2, C, syn)
262        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
263                         [(0, 1, 0.1, 0.123),
264                          (1, 1, 0.0, 0.123),
265                          (2, 1, 0.1, 0.123),
266                          (3, 1, 0.2, 0.123)])
267
268    def test_connect_with_random_delays_parallel_safe(self, sim=sim):
269        rd = random.RandomDistribution('uniform', low=0.1, high=1.1,
270                                       rng=MockRNG(start=1.0, delta=0.2, parallel_safe=True))
271        syn = sim.StaticSynapse(delay=rd)
272        C = connectors.FixedProbabilityConnector(p_connect=0.5,
273                                                 rng=MockRNG2(1 - np.array([1, 0, 0, 1,
274                                                                               0, 0, 0, 1,
275                                                                               1, 1, 0, 0,
276                                                                               1, 0, 1, 0,
277                                                                               1, 1, 0, 1]),
278                                                              parallel_safe=True))
279        prj = sim.Projection(self.p1, self.p2, C, syn)
280        nan = np.nan
281        assert_array_almost_equal(prj.get('delay', format='array', gather=False),
282                                  np.array([[nan, nan, nan, 2.0, nan],
283                                               [nan, nan, nan, nan, nan],
284                                               [nan, nan, nan, 2.2, nan],
285                                               [nan, 1.4, nan, nan, nan]]),
286                                  9)
287
288    # def test_connect_with_random_delays_parallel_unsafe(self, sim=sim):
289    #    rd = random.RandomDistribution('uniform', [0.1, 1.1], rng=MockRNG(start=1.0, delta=0.2, parallel_safe=False))
290    #    syn = sim.StaticSynapse(delay=rd)
291    #    C = connectors.FixedProbabilityConnector(p_connect=0.5,
292    #                                             rng=MockRNG2(1 - np.array([1, 0, 0, 1,
293    #                                                                           0, 0, 0, 1,
294    #                                                                           1, 1, 0, 0,
295    #                                                                           1, 0, 1, 0,
296    #                                                                           1, 1, 0, 1]),
297    #                                                          parallel_safe=False))
298    #    prj = sim.Projection(self.p1, self.p2, C, syn)
299    #    nan = np.nan
300    #    assert_array_almost_equal(prj.get('delay', format='array', gather=False),
301    #                              np.array([[nan, nan, nan, 1.2, nan],
302    #                                           [nan, nan, nan, nan, nan],
303    #                                           [nan, nan, nan, 1.4, nan],
304    #                                           [nan, 1.0, nan, nan, nan]]),
305    #                              9)
306
307
308class TestDistanceDependentProbabilityConnector(unittest.TestCase):
309
310    def setUp(self, sim=sim):
311        sim.setup(num_processes=2, rank=1, min_delay=0.123)
312        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
313        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
314        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
315
316    def test_connect_with_default_args(self, sim=sim):
317        C = connectors.DistanceDependentProbabilityConnector(d_expression="d<1.5",
318                                                             rng=MockRNG(delta=0.01))
319        syn = sim.StaticSynapse()
320        prj = sim.Projection(self.p1, self.p2, C, syn)
321        # 20 possible connections. Only those with a sufficiently small distance
322        # are created
323        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
324                         [(0, 1, 0.0, 0.123),
325                          (1, 1, 0.0, 0.123),
326                          (2, 1, 0.0, 0.123),
327                          (2, 3, 0.0, 0.123),
328                          (3, 3, 0.0, 0.123)])
329
330
331class TestFromListConnector(unittest.TestCase):
332
333    def setUp(self, sim=sim):
334        sim.setup(num_processes=2, rank=1, min_delay=0.123)
335        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
336        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
337        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
338
339    def test_connect_with_valid_list(self, sim=sim):
340        connection_list = [
341            (0, 0, 0.1, 0.1),
342            (3, 0, 0.2, 0.11),
343            (2, 3, 0.3, 0.12),  # local
344            (2, 2, 0.4, 0.13),
345            (0, 1, 0.5, 0.14),  # local
346        ]
347        C = connectors.FromListConnector(connection_list)
348        syn = sim.StaticSynapse()
349        prj = sim.Projection(self.p1, self.p2, C, syn)
350        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
351                         [(0, 1, 0.5, 0.14),
352                          (2, 3, 0.3, 0.12)])
353
354    def test_connect_with_out_of_range_index(self, sim=sim):
355        connection_list = [
356            (0, 0, 0.1, 0.1),
357            (3, 0, 0.2, 0.11),
358            (2, 3, 0.3, 0.12),  # local
359            (5, 1, 0.4, 0.13),  # NON-EXISTENT
360            (0, 1, 0.5, 0.14),  # local
361        ]
362        C = connectors.FromListConnector(connection_list)
363        syn = sim.StaticSynapse()
364        self.assertRaises(errors.ConnectionError, sim.Projection, self.p1, self.p2, C, syn)
365
366    def test_with_plastic_synapse(self, sim=sim):
367        connection_list = [
368            (0, 0, 0.1, 0.1, 100, 400),
369            (3, 0, 0.2, 0.11, 101, 500),
370            (2, 3, 0.3, 0.12, 102, 600),  # local
371            (2, 2, 0.4, 0.13, 103, 700),
372            (0, 1, 0.5, 0.14, 104, 800),  # local
373        ]
374        C = connectors.FromListConnector(connection_list, column_names=[
375                                         "weight", "delay", "U", "tau_rec"])
376        syn = sim.TsodyksMarkramSynapse(U=99, tau_facil=88.8)
377        prj = sim.Projection(self.p1, self.p2, C, syn)
378        self.assertEqual(prj.get(["weight", "delay", "tau_facil", "tau_rec", "U"], format='list', gather=False),  # use gather False because we are faking the MPI
379                         [(0, 1, 0.5, 0.14, 88.8, 800.0, 104.0),
380                          (2, 3, 0.3, 0.12, 88.8, 600.0, 102.0)])
381
382    def test_with_stdp_synapse(self, sim=sim):
383        connection_list = [
384            (0, 0, 0.1, 0.1, 10.0, 0.4),
385            (3, 0, 0.2, 0.11, 10.1, 0.5),
386            (2, 3, 0.3, 0.12, 10.2, 0.6),  # local
387            (2, 2, 0.4, 0.13, 10.3, 0.7),
388            (0, 1, 0.5, 0.14, 10.4, 0.8),  # local
389        ]
390        C = connectors.FromListConnector(connection_list, column_names=[
391                                         "weight", "delay", "tau_plus", "w_max"])
392        syn = sim.STDPMechanism(timing_dependence=sim.SpikePairRule(tau_plus=12.3, tau_minus=33.3),
393                                weight_dependence=sim.MultiplicativeWeightDependence(w_max=1.11),
394                                weight=0.321, delay=0.2)
395        prj = sim.Projection(self.p1, self.p2, C, syn)
396        self.assertEqual(prj.get(["weight", "delay", "tau_plus", "tau_minus", "w_max"], format='list', gather=False),  # use gather False because we are faking the MPI
397                         [(0, 1, 0.5, 0.14, 10.4, 33.3, 0.8),
398                          (2, 3, 0.3, 0.12, 10.2, 33.3, 0.6)])
399
400
401class TestFromFileConnector(unittest.TestCase):
402
403    def setUp(self, sim=sim):
404        sim.setup(num_processes=2, rank=1, min_delay=0.123)
405        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
406        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
407        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
408        self.connection_list = [
409            (0, 0, 0.1, 0.1),
410            (3, 0, 0.2, 0.11),
411            (2, 3, 0.3, 0.12),  # local
412            (2, 2, 0.4, 0.13),
413            (0, 1, 0.5, 0.14),  # local
414        ]
415
416    def tearDown(self, sim=sim):
417        for path in ("test.connections", "test.connections.1", "test.connections.2"):
418            if os.path.exists(path):
419                try:
420                    os.remove(path)
421                except PermissionError:
422                    pass
423
424    def test_connect_with_standard_text_file_not_distributed(self, sim=sim):
425        np.savetxt("test.connections", self.connection_list)
426        C = connectors.FromFileConnector("test.connections", distributed=False)
427        syn = sim.StaticSynapse()
428        prj = sim.Projection(self.p1, self.p2, C, syn)
429        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
430                         [(0, 1, 0.5, 0.14),
431                          (2, 3, 0.3, 0.12)])
432
433    def test_connect_with_standard_text_file_distributed(self, sim=sim):
434        local_connection_list = [c for c in self.connection_list if c[1] % 2 == 1]
435        np.savetxt("test.connections.1", local_connection_list)
436        C = connectors.FromFileConnector("test.connections", distributed=True)
437        syn = sim.StaticSynapse()
438        prj = sim.Projection(self.p1, self.p2, C, syn)
439        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
440                         [(0, 1, 0.5, 0.14),
441                          (2, 3, 0.3, 0.12)])
442
443    def test_with_plastic_synapses_not_distributed(self, sim=sim):
444        connection_list = [
445            (0, 0, 0.1, 0.1,  100, 100),
446            (3, 0, 0.2, 0.11, 110, 99),
447            (2, 3, 0.3, 0.12, 120, 98),  # local
448            (2, 2, 0.4, 0.13, 130, 97),
449            (0, 1, 0.5, 0.14, 140, 96),  # local
450        ]
451        file = recording.files.StandardTextFile("test.connections.2", mode='wb')
452        file.write(connection_list, {"columns": ["i", "j", "weight", "delay", "U", "tau_rec"]})
453        C = connectors.FromFileConnector("test.connections.2", distributed=False)
454        syn = sim.TsodyksMarkramSynapse(tau_facil=88.8)
455        prj = sim.Projection(self.p1, self.p2, C, syn)
456        self.assertEqual(prj.get(["weight", "delay", "U", "tau_rec", "tau_facil"], format='list', gather=False),  # use gather False because we are faking the MPI
457                         [(0, 1, 0.5, 0.14, 140.0, 96.0, 88.8),
458                          (2, 3, 0.3, 0.12, 120.0, 98.0, 88.8)])
459
460
461class TestFixedNumberPostConnector(unittest.TestCase):
462
463    def setUp(self, sim=sim):
464        sim.setup(num_processes=2, rank=1, min_delay=0.123)
465        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
466        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
467        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
468
469    def test_with_n_smaller_than_population_size(self, sim=sim):
470        C = connectors.FixedNumberPostConnector(n=3, rng=MockRNG(delta=1))
471        syn = sim.StaticSynapse(weight="0.5*d")
472        prj = sim.Projection(self.p1, self.p2, C, syn)
473        # MockRNG.permutation(arr) returns the reverse of arr, so each pre neuron will connect to neurons 4, 3, 2
474        # however, only neuron 3 is on the "local" (fake MPI) node
475        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
476                         [(0, 3, 1.5, 0.123),
477                          (1, 3, 1.0, 0.123),
478                          (2, 3, 0.5, 0.123),
479                          (3, 3, 0.0, 0.123)])
480
481    def test_with_n_larger_than_population_size(self, sim=sim):
482        C = connectors.FixedNumberPostConnector(n=7, rng=MockRNG(delta=1))
483        syn = sim.StaticSynapse()
484        prj = sim.Projection(self.p1, self.p2, C, syn)
485        # each pre neuron will connect to all post neurons (population size 5 is less than n), then to 4, 3 (MockRNG.permutation)
486        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
487                         [(0, 1, 0.0, 0.123),
488                          (1, 1, 0.0, 0.123),
489                          (2, 1, 0.0, 0.123),
490                          (3, 1, 0.0, 0.123),
491                          (0, 3, 0.0, 0.123),
492                          (0, 3, 0.0, 0.123),
493                          (1, 3, 0.0, 0.123),
494                          (1, 3, 0.0, 0.123),
495                          (2, 3, 0.0, 0.123),
496                          (2, 3, 0.0, 0.123),
497                          (3, 3, 0.0, 0.123),
498                          (3, 3, 0.0, 0.123)])
499
500    def test_with_n_larger_than_population_size_no_self_connections(self, sim=sim):
501        C = connectors.FixedNumberPostConnector(
502            n=7, allow_self_connections=False, rng=MockRNG(delta=1))
503        syn = sim.StaticSynapse()
504        prj = sim.Projection(self.p2, self.p2, C, syn)
505        # connections as follows: (pre - list of post)
506        #   0 - 1 2 3 4 4 3 2
507        #   1 - 0 2 3 4 4 3 2
508        #   2 - 0 1 3 4 4 3 1
509        #   3 - 0 1 2 4 4 2 1
510        #   4 - 0 1 2 3 3 2 1
511        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
512                         [(0, 1, 0.0, 0.123),
513                          (2, 1, 0.0, 0.123),
514                          (2, 1, 0.0, 0.123),
515                          (3, 1, 0.0, 0.123),
516                          (3, 1, 0.0, 0.123),
517                          (4, 1, 0.0, 0.123),
518                          (4, 1, 0.0, 0.123),
519                          (0, 3, 0.0, 0.123),
520                          (0, 3, 0.0, 0.123),
521                          (1, 3, 0.0, 0.123),
522                          (1, 3, 0.0, 0.123),
523                          (2, 3, 0.0, 0.123),
524                          (2, 3, 0.0, 0.123),
525                          (4, 3, 0.0, 0.123),
526                          (4, 3, 0.0, 0.123), ])
527
528    def test_with_replacement(self, sim=sim):
529        C = connectors.FixedNumberPostConnector(n=3, with_replacement=True, rng=MockRNG(delta=1))
530        syn = sim.StaticSynapse()
531        prj = sim.Projection(self.p1, self.p2, C, syn)
532        # 0 - 0 1 2
533        # 1 - 3 4 0
534        # 2 - 1 2 3
535        # 3 - 4 0 1
536        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
537                         [(0, 1, 0.0, 0.123),
538                          (2, 1, 0.0, 0.123),
539                          (3, 1, 0.0, 0.123),
540                          (1, 3, 0.0, 0.123),
541                          (2, 3, 0.0, 0.123)])
542
543    def test_with_replacement_with_variable_n(self, sim=sim):
544        n = random.RandomDistribution('binomial', (5, 0.5), rng=MockRNG(start=1, delta=2))
545        # should give (1, 3, 0, 2)
546        C = connectors.FixedNumberPostConnector(n=n, with_replacement=True, rng=MockRNG(delta=1))
547        syn = sim.StaticSynapse()
548        prj = sim.Projection(self.p1, self.p2, C, syn)
549        # 0 - 0
550        # 1 - 1 2 3
551        # 2 -
552        # 3 - 4 0
553        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
554                         [(1, 1, 0.0, 0.123),
555                          (1, 3, 0.0, 0.123)])
556
557    def test_with_replacement_no_self_connections(self, sim=sim):
558        C = connectors.FixedNumberPostConnector(n=3, with_replacement=True,
559                                                allow_self_connections=False, rng=MockRNG(start=2, delta=1))
560        syn = sim.StaticSynapse()
561        prj = sim.Projection(self.p2, self.p2, C, syn)
562        # 0 - 2 3 4
563        # 1 - 0 2 3
564        # 2 - 4 0 1
565        # 3 - 2 4 0
566        # 4 - 1 2 3
567        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
568                         [(2, 1, 0.0, 0.123),
569                          (4, 1, 0.0, 0.123),
570                          (0, 3, 0.0, 0.123),
571                          (1, 3, 0.0, 0.123),
572                          (4, 3, 0.0, 0.123)])
573
574
575class TestFixedNumberPreConnector(unittest.TestCase):
576
577    def setUp(self, sim=sim):
578        sim.setup(num_processes=2, rank=1, min_delay=0.123)
579        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
580        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
581        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
582
583    def test_with_n_smaller_than_population_size(self, sim=sim):
584        C = connectors.FixedNumberPreConnector(n=3, rng=MockRNG(delta=1))
585        syn = sim.StaticSynapse(weight="0.1*d")
586        prj = sim.Projection(self.p1, self.p2, C, syn)
587        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
588                         [(3, 1, 0.2, 0.123),
589                          (2, 1, 0.1, 0.123),
590                          (1, 1, 0.0, 0.123),
591                          (3, 3, 0.0, 0.123),
592                          (2, 3, 0.1, 0.123),
593                          (1, 3, 0.2, 0.123), ])
594
595    def test_with_n_larger_than_population_size(self, sim=sim):
596        C = connectors.FixedNumberPreConnector(n=7, rng=MockRNG(delta=1))
597        syn = sim.StaticSynapse()
598        prj = sim.Projection(self.p1, self.p2, C, syn)
599        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
600                         [(0, 1, 0.0, 0.123),
601                          (1, 1, 0.0, 0.123),
602                          (2, 1, 0.0, 0.123),
603                          (3, 1, 0.0, 0.123),
604                          (3, 1, 0.0, 0.123),
605                          (2, 1, 0.0, 0.123),
606                          (1, 1, 0.0, 0.123),
607                          (0, 3, 0.0, 0.123),
608                          (1, 3, 0.0, 0.123),
609                          (2, 3, 0.0, 0.123),
610                          (3, 3, 0.0, 0.123),
611                          (3, 3, 0.0, 0.123),
612                          (2, 3, 0.0, 0.123),
613                          (1, 3, 0.0, 0.123), ])
614
615    def test_with_n_larger_than_population_size_no_self_connections(self, sim=sim):
616        C = connectors.FixedNumberPreConnector(
617            n=7, allow_self_connections=False, rng=MockRNG(delta=1))
618        syn = sim.StaticSynapse()
619        prj = sim.Projection(self.p2, self.p2, C, syn)
620        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
621                         [(0, 1, 0.0, 0.123),
622                          (2, 1, 0.0, 0.123),
623                          (3, 1, 0.0, 0.123),
624                          (4, 1, 0.0, 0.123),
625                          (4, 1, 0.0, 0.123),
626                          (3, 1, 0.0, 0.123),
627                          (2, 1, 0.0, 0.123),
628                          (0, 3, 0.0, 0.123),
629                          (1, 3, 0.0, 0.123),
630                          (2, 3, 0.0, 0.123),
631                          (4, 3, 0.0, 0.123),
632                          (4, 3, 0.0, 0.123),
633                          (2, 3, 0.0, 0.123),
634                          (1, 3, 0.0, 0.123), ])
635
636    def test_with_replacement(self, sim=sim):
637        C = connectors.FixedNumberPreConnector(n=3, with_replacement=True, rng=MockRNG(delta=1))
638        syn = sim.StaticSynapse()
639        prj = sim.Projection(self.p1, self.p2, C, syn)
640        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
641                         [  # (0, 0, 0.0, 0.123),
642            #(1, 0, 0.0, 0.123),
643            #(2, 0, 0.0, 0.123),
644            (3, 1, 0.0, 0.123),
645            (0, 1, 0.0, 0.123),
646            (1, 1, 0.0, 0.123),
647            #(2, 2, 0.0, 0.123),
648            #(3, 2, 0.0, 0.123),
649            #(0, 2, 0.0, 0.123),
650            (1, 3, 0.0, 0.123),
651            (2, 3, 0.0, 0.123),
652            (3, 3, 0.0, 0.123), ])
653
654    def test_with_replacement_with_variable_n(self, sim=sim):
655        n = random.RandomDistribution('binomial', (5, 0.5), rng=MockRNG(start=1, delta=2))
656        # should give (1, 3, 0, 2, 4)
657        C = connectors.FixedNumberPreConnector(n=n, with_replacement=True, rng=MockRNG(delta=1))
658        syn = sim.StaticSynapse()
659        prj = sim.Projection(self.p1, self.p2, C, syn)
660        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
661                         [  # (0, 0, 0.0, 0.123),
662            (1, 1, 0.0, 0.123),
663            (2, 1, 0.0, 0.123),
664            (3, 1, 0.0, 0.123),
665            (0, 3, 0.0, 0.123),
666            (1, 3, 0.0, 0.123)])
667
668    def test_with_replacement_no_self_connections(self, sim=sim):
669        C = connectors.FixedNumberPreConnector(n=3, with_replacement=True,
670                                               allow_self_connections=False, rng=MockRNG(start=2, delta=1))
671        syn = sim.StaticSynapse()
672        prj = sim.Projection(self.p2, self.p2, C, syn)
673        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
674                         [  # (2, 0, 0.0, 0.123),  # [2, 3, 4] --> [2, 3, 4]
675            #(3, 0, 0.0, 0.123),
676            #(4, 0, 0.0, 0.123),
677            (0, 1, 0.0, 0.123),   # [0, 1, 2] --> [0, 3, 2]
678            #(1, 1, 0.0, 0.123),
679            (3, 1, 0.0, 0.123),
680            (2, 1, 0.0, 0.123),
681            # (4, 2, 0.0, 0.123),  # [4, 0, 1] --> [4, 0, 1]
682            #(0, 2, 0.0, 0.123),
683            #(1, 2, 0.0, 0.123),
684            (2, 3, 0.0, 0.123),   # [2, 3, 4] --> [2, 0, 4]
685            #(3, 3, 0.0, 0.123),
686            (0, 3, 0.0, 0.123),
687            (4, 3, 0.0, 0.123),
688        ])
689
690    def test_no_replacement_no_self_connections(self, sim=sim):
691        C = connectors.FixedNumberPreConnector(n=3, with_replacement=False,
692                                               allow_self_connections=False, rng=MockRNG(start=2, delta=1))
693        syn = sim.StaticSynapse()
694        prj = sim.Projection(self.p2, self.p2, C, syn)
695        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
696                         [(4, 1, 0.0, 0.123),
697                          (3, 1, 0.0, 0.123),
698                          (2, 1, 0.0, 0.123),
699                          (4, 3, 0.0, 0.123),
700                          #(3, 3, 0.0, 0.123),
701                          (2, 3, 0.0, 0.123),
702                          (1, 3, 0.0, 0.123), ])
703
704    def test_with_replacement_parallel_unsafe(self, sim=sim):
705        C = connectors.FixedNumberPreConnector(
706            n=3, with_replacement=True, rng=MockRNG(delta=1, parallel_safe=False))
707        syn = sim.StaticSynapse()
708        prj = sim.Projection(self.p1, self.p2, C, syn)
709        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
710                         [(0, 1, 0.0, 0.123),
711                          (1, 1, 0.0, 0.123),
712                          (2, 1, 0.0, 0.123),
713                          (3, 3, 0.0, 0.123),
714                          (0, 3, 0.0, 0.123),
715                          (1, 3, 0.0, 0.123), ])
716
717    def test_no_replacement_parallel_unsafe(self, sim=sim):
718        C = connectors.FixedNumberPreConnector(
719            n=3, with_replacement=False, rng=MockRNG(delta=1, parallel_safe=False))
720        syn = sim.StaticSynapse()
721        prj = sim.Projection(self.p1, self.p2, C, syn)
722        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
723                         [(3, 1, 0.0, 0.123),
724                          (2, 1, 0.0, 0.123),
725                          (1, 1, 0.0, 0.123),
726                          (3, 3, 0.0, 0.123),
727                          (2, 3, 0.0, 0.123),
728                          (1, 3, 0.0, 0.123), ])
729
730
731class TestArrayConnector(unittest.TestCase):
732
733    def setUp(self, sim=sim):
734        sim.setup(num_processes=2, rank=1, min_delay=0.123)
735        self.p1 = sim.Population(3, sim.IF_cond_exp(), structure=space.Line())
736        self.p2 = sim.Population(4, sim.HH_cond_exp(), structure=space.Line())
737        assert_array_equal(self.p2._mask_local, np.array([1, 0, 1, 0], dtype=bool))
738
739    def test_connect_with_scalar_weights_and_delays(self, sim=sim):
740        connections = np.array([
741            [0, 1, 1, 0],
742            [1, 1, 0, 1],
743            [0, 0, 1, 0],
744        ], dtype=bool)
745        C = connectors.ArrayConnector(connections, safe=False)
746        syn = sim.StaticSynapse(weight=5.0, delay=0.5)
747        prj = sim.Projection(self.p1, self.p2, C, syn)
748        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
749                         [(1, 0, 5.0, 0.5),
750                          (0, 2, 5.0, 0.5),
751                          (2, 2, 5.0, 0.5)])
752
753    def test_connect_with_random_weights_parallel_safe(self, sim=sim):
754        rd_w = random.RandomDistribution(
755            'uniform', (0, 1), rng=MockRNG(delta=1.0, parallel_safe=True))
756        rd_d = random.RandomDistribution('uniform', (0, 1), rng=MockRNG(
757            start=1.0, delta=0.1, parallel_safe=True))
758        syn = sim.StaticSynapse(weight=rd_w, delay=rd_d)
759        connections = np.array([
760            [0, 1, 1, 0],
761            [1, 1, 0, 1],
762            [0, 0, 1, 0],
763        ], dtype=bool)
764        C = connectors.ArrayConnector(connections, safe=False)
765        prj = sim.Projection(self.p1, self.p2, C, syn)
766        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
767                         [(1, 0, 0.0, 1.0),
768                          (0, 2, 3.0, 1.3),
769                          (2, 2, 4.0, 1.4000000000000001)])  # better to do an "almost-equal" check
770
771
772class TestCloneConnector(unittest.TestCase):
773
774    def setUp(self, sim=sim):
775        sim.setup(num_processes=2, rank=1, min_delay=0.123)
776        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
777        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
778        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
779        connection_list = [
780            (0, 0, 0.0, 1.0),
781            (3, 0, 0.0, 1.0),
782            (2, 3, 0.0, 1.0),  # local
783            (2, 2, 0.0, 1.0),
784            (0, 1, 0.0, 1.0),  # local
785        ]
786        list_connector = connectors.FromListConnector(connection_list)
787        syn = sim.StaticSynapse()
788        self.ref_prj = sim.Projection(self.p1, self.p2, list_connector, syn)
789        self.orig_gather_dict = recording.gather_dict  # create reference to original function
790        # The gather_dict function in recording needs to be temporarily replaced so it can work with
791        # a mock version of the function to avoid it throwing an mpi4py import error when setting
792        # the rank in pyNN.mock by hand to > 1
793
794        def mock_gather_dict(D, all=False):
795            return D
796        recording.gather_dict = mock_gather_dict
797
798    def tearDown(self, sim=sim):
799        # restore original gather_dict function
800        recording.gather_dict = self.orig_gather_dict
801
802    def test_connect(self, sim=sim):
803        syn = sim.StaticSynapse(weight=5.0, delay=0.5)
804        C = connectors.CloneConnector(self.ref_prj)
805        prj = sim.Projection(self.p1, self.p2, C, syn)
806        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
807                         [(0, 1, 5.0, 0.5),
808                          (2, 3, 5.0, 0.5)])
809
810    def test_connect_with_pre_post_mismatch(self, sim=sim):
811        syn = sim.StaticSynapse()
812        C = connectors.CloneConnector(self.ref_prj)
813        p3 = sim.Population(5, sim.IF_cond_exp(), structure=space.Line())
814        self.assertRaises(errors.ConnectionError, sim.Projection, self.p1, p3, C, syn)
815
816
817class TestIndexBasedProbabilityConnector(unittest.TestCase):
818
819    class IndexBasedProbability(connectors.IndexBasedExpression):
820
821        def __call__(self, i, j):
822            return np.array((i + j) % 3 == 0, dtype=float)
823
824    class IndexBasedWeights(connectors.IndexBasedExpression):
825
826        def __call__(self, i, j):
827            return np.array(i * j + 1, dtype=float)
828
829    class IndexBasedDelays(connectors.IndexBasedExpression):
830
831        def __call__(self, i, j):
832            return np.array(i + j + 1, dtype=float)
833
834    def setUp(self, sim=sim, **extra):
835        sim.setup(num_processes=2, rank=1, min_delay=0.123, **extra)
836        self.p1 = sim.Population(5, sim.IF_cond_exp(), structure=space.Line())
837        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
838        assert_array_equal(self.p2._mask_local, np.array([1, 0, 1, 0, 1], dtype=bool))
839
840    def test_connect_with_scalar_weights_and_delays(self, sim=sim):
841        syn = sim.StaticSynapse(weight=1.0, delay=2)
842        C = connectors.IndexBasedProbabilityConnector(self.IndexBasedProbability())
843        prj = sim.Projection(self.p1, self.p2, C, syn)
844        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
845                         [(0, 0, 1, 2),
846                          (3, 0, 1, 2),
847                          (1, 2, 1, 2),
848                          (4, 2, 1, 2),
849                          (2, 4, 1, 2)])
850
851    def test_connect_with_index_based_weights(self, sim=sim):
852        syn = sim.StaticSynapse(weight=self.IndexBasedWeights(), delay=2)
853        C = connectors.IndexBasedProbabilityConnector(self.IndexBasedProbability())
854        prj = sim.Projection(self.p1, self.p2, C, syn)
855        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
856                         [(0, 0, 1, 2),
857                          (3, 0, 1, 2),
858                          (1, 2, 3, 2),
859                          (4, 2, 9, 2),
860                          (2, 4, 9, 2)])
861
862    def test_connect_with_index_based_delays(self, sim=sim):
863        syn = sim.StaticSynapse(weight=1.0, delay=self.IndexBasedDelays())
864        C = connectors.IndexBasedProbabilityConnector(self.IndexBasedProbability())
865        prj = sim.Projection(self.p1, self.p2, C, syn)
866        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
867                         [(0, 0, 1, 1),
868                          (3, 0, 1, 4),
869                          (1, 2, 1, 4),
870                          (4, 2, 1, 7),
871                          (2, 4, 1, 7)])
872
873
874class TestDisplacementDependentProbabilityConnector(unittest.TestCase):
875
876    def setUp(self, sim=sim, **extra):
877        sim.setup(num_processes=2, rank=1, min_delay=0.123, **extra)
878        self.p1 = sim.Population(9, sim.IF_cond_exp(),
879                                 structure=space.Grid2D(aspect_ratio=1.0, dx=1.0, dy=1.0))
880        self.p2 = sim.Population(9, sim.HH_cond_exp(),
881                                 structure=space.Grid2D(aspect_ratio=1.0, dx=1.0, dy=1.0))
882        assert_array_equal(self.p2._mask_local, np.array(
883            [1, 0, 1, 0, 1, 0, 1, 0, 1], dtype=bool))
884
885    def test_connect(self, sim=sim):
886        syn = sim.StaticSynapse(weight=1.0, delay=2)
887
888        def displacement_expression(d):
889            return 0.5 * ((d[0] >= -1) * (d[0] <= 2)) + 0.25 * (d[1] >= 0) * (d[1] <= 1)
890        C = connectors.DisplacementDependentProbabilityConnector(displacement_expression,
891                                                                 rng=MockRNG(delta=0.01))
892        prj = sim.Projection(self.p1, self.p2, C, syn)
893        self.assertEqual(prj.get(["weight", "delay"], format='list', gather=False),  # use gather False because we are faking the MPI
894                         [(0, 0, 1.0, 2.0),
895                          (1, 0, 1.0, 2.0),
896                          (2, 0, 1.0, 2.0),
897                          (3, 0, 1.0, 2.0),
898                          (4, 0, 1.0, 2.0),
899                          (5, 0, 1.0, 2.0),
900                          (6, 0, 1.0, 2.0),
901                          (0, 2, 1.0, 2.0),
902                          (1, 2, 1.0, 2.0),
903                          (2, 2, 1.0, 2.0),
904                          (3, 2, 1.0, 2.0),
905                          (4, 2, 1.0, 2.0),
906                          (5, 2, 1.0, 2.0),
907                          (0, 4, 1.0, 2.0),
908                          (1, 4, 1.0, 2.0),
909                          (2, 4, 1.0, 2.0),
910                          (3, 4, 1.0, 2.0),
911                          (4, 4, 1.0, 2.0),
912                          (5, 4, 1.0, 2.0),
913                          (6, 4, 1.0, 2.0),
914                          (7, 4, 1.0, 2.0),
915                          (8, 4, 1.0, 2.0),
916                          (0, 6, 1.0, 2.0),
917                          (3, 6, 1.0, 2.0),
918                          (6, 6, 1.0, 2.0),
919                          (1, 8, 1.0, 2.0),
920                          (2, 8, 1.0, 2.0)])
921
922
923class TestFixedTotalNumberConnector(unittest.TestCase):
924
925    def setUp(self, sim=sim):
926        sim.setup(num_processes=2, rank=1, min_delay=0.123)
927        self.p1 = sim.Population(4, sim.IF_cond_exp(), structure=space.Line())
928        self.p2 = sim.Population(5, sim.HH_cond_exp(), structure=space.Line())
929        assert_array_equal(self.p2._mask_local, np.array([0, 1, 0, 1, 0], dtype=bool))
930
931    def test_1(self):
932        C = connectors.FixedTotalNumberConnector(n=12, rng=random.NumpyRNG())
933        syn = sim.StaticSynapse(weight="0.5*d")
934        prj = sim.Projection(self.p1, self.p2, C, syn)
935        connections = prj.get(["weight", "delay"], format='list', gather=False)
936        self.assertLess(len(connections), 12)    # unlikely to be 12, since we have 2 MPI nodes
937        self.assertGreater(len(connections), 0)  # unlikely to be 0
938