Repository: spinnakermanchester/spynnaker
Issue: FixedProbabilityConnector sometimes overflow

found with 
https://github.com/SpiNNakerManchester/sPyNNaker/blob/master/unittests/model_tests/test_connectors.py
 
in rare but not zero case the asserts 
            assert max(source_histogram) <= max_row_length
            assert max(target_histogram) <= max_col_length
Fail.

Passing a constant seed into FixedProbabilityConnector is NOT a FIX!

We need to decide between
1. have an actual max 
2. Capping to the utility_calls.get_probable_maximum_selected value
3.Capping to the higher of utility_calls.get_probable_maximum_selected or some user specified value.
a. in a config
b. as an extra parameter to the connector

Could be related to:
https://github.com/SpiNNakerManchester/sPyNNaker/issues/585



Changed files:
spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py

diff --git a/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py
index 18c6e56623..79c0142b82 100644
--- a/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py
+++ b/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py
@@ -65,18 +65,13 @@ class FixedProbabilityConnector(AbstractGenerateConnectorOnMachine):
             self._n_pre_neurons * self._n_post_neurons, self._p_connect)
         return self._get_delay_maximum(delays, n_connections)
 
-    def _get_n_connections(self, out_of):
-        return utility_calls.get_probable_maximum_selected(
-            self._n_pre_neurons * self._n_post_neurons, out_of,
-            self._p_connect)
-
     @overrides(AbstractConnector.get_n_connections_from_pre_vertex_maximum)
     def get_n_connections_from_pre_vertex_maximum(
             self, delays, post_vertex_slice, min_delay=None, max_delay=None):
         # pylint: disable=too-many-arguments
         n_connections = utility_calls.get_probable_maximum_selected(
             self._n_pre_neurons * self._n_post_neurons,
-            post_vertex_slice.n_atoms, self._p_connect)
+            post_vertex_slice.n_atoms, self._p_connect, chance=1.0/10000.0)
 
         if min_delay is None or max_delay is None:
             return int(math.ceil(n_connections))
@@ -90,7 +85,7 @@ class FixedProbabilityConnector(AbstractGenerateConnectorOnMachine):
         # pylint: disable=too-many-arguments
         n_connections = utility_calls.get_probable_maximum_selected(
             self._n_pre_neurons * self._n_post_neurons,
-            self._n_pre_neurons, self._p_connect)
+            self._n_pre_neurons, self._p_connect, chance=1.0/10000.0)
         return n_connections
 
     @overrides(AbstractConnector.get_weight_maximum)
@@ -115,7 +110,7 @@ class FixedProbabilityConnector(AbstractGenerateConnectorOnMachine):
         if not self.__allow_self_connections:
             items[0:n_items:post_vertex_slice.n_atoms + 1] = numpy.inf
 
-        present = items < self._p_connect
+        present = items <= self._p_connect
         ids = numpy.where(present)[0]
         n_connections = numpy.sum(present)
 
@@ -145,10 +140,16 @@ class FixedProbabilityConnector(AbstractGenerateConnectorOnMachine):
             self, pre_slices, pre_slice_index, post_slices,
             post_slice_index, pre_vertex_slice, post_vertex_slice,
             synapse_type):
+        prob_value = round(decimal.Decimal(
+            str(self._p_connect)) * DataType.U032.scale)
+        # If prob=1.0 has been specified, take care when scaling value to
+        # ensure that it doesn't wrap round to zero as an unsigned long fract
+        if (self._p_connect == 1.0):
+            prob_value = round(decimal.Decimal(
+                str(self._p_connect)) * (DataType.U032.scale - 1))
         params = [
             self.__allow_self_connections,
-            round(decimal.Decimal(
-                str(self._p_connect)) * DataType.U032.scale)]
+            prob_value]
         params.extend(self._get_connector_seed(
             pre_vertex_slice, post_vertex_slice, self._rng))
         return numpy.array(params, dtype="uint32")