Skip to content

Commit 09ebc94

Browse files
authored
fix(dyn): PoissonGroup never spikes due to boolean rand_like dtype (#862)
PoissonGroup.update drew random values with rand_like(self.spike.value). Since spike is boolean-typed and rand_like inherits the input dtype, the draw was boolean (approximately all True), so the spike-probability comparison was always False and the group never fired. This silenced every network driven by PoissonGroup, including the decision_making_network and stdp simulation examples. Force a real uniform draw with dtype=float. Add firing-rate regression tests (default and batching mode); the previous shape-only test missed this.
1 parent b6b5cc5 commit 09ebc94

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

brainpy/dyn/others/input.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ def __init__(
230230
self.reset_state(self.mode)
231231

232232
def update(self):
233-
spikes = bm.random.rand_like(self.spike.value) <= (self.freqs * share['dt'] / 1000.)
233+
# ``dtype=float`` is required: ``self.spike`` is boolean, and ``rand_like``
234+
# defaults to the input dtype. Without it the draw is boolean (~all True),
235+
# making the ``<= prob`` comparison always False so the group never fires.
236+
spikes = bm.random.rand_like(self.spike.value, dtype=float) <= (self.freqs * share['dt'] / 1000.)
234237
spikes = bm.asarray(spikes, dtype=self.spk_type)
235-
# import jax
236-
# jax.debug.print('PoissonGroup: freqs = {f}, spikes = {s}', f=self.freqs, s=spikes)
237238
self.spike.value = spikes
238239
return spikes
239240

brainpy/dyn/others/input_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
# ==============================================================================
16+
import numpy as np
1617
from absl.testing import parameterized
1718

1819
import brainpy as bp
20+
import brainpy.math as bm
1921
from brainpy.dyn.others import input
2022

2123

@@ -35,3 +37,36 @@ def test_PoissonGroup(self):
3537
progress_bar=False)
3638
runner.run(30.)
3739
self.assertTupleEqual(runner.mon['spike'].shape, (300, 2))
40+
41+
def test_PoissonGroup_fires_at_expected_rate(self):
42+
# Regression: PoissonGroup must actually emit spikes at ~freqs Hz.
43+
# A dtype bug (boolean rand_like) previously made it fire at 0 Hz while
44+
# still returning the correct output shape, so a shape-only check missed it.
45+
bm.random.seed(1234)
46+
freqs = 200. # Hz
47+
num = 200
48+
duration = 1000. # ms
49+
model = input.PoissonGroup(size=num, freqs=freqs)
50+
runner = bp.DSRunner(model, monitors=['spike'], progress_bar=False)
51+
runner.run(duration)
52+
spikes = np.asarray(runner.mon['spike'])
53+
empirical_rate = spikes.sum() / num / (duration / 1000.)
54+
# Poisson counting noise here is < 1%; 15% tolerance is safe yet still
55+
# rejects the 0 Hz regression by a wide margin.
56+
self.assertAlmostEqual(empirical_rate, freqs, delta=0.15 * freqs)
57+
58+
def test_PoissonGroup_fires_in_batching_mode(self):
59+
# The float draw must preserve the batched shape of ``spike`` and still
60+
# produce the expected rate across the batch axis.
61+
bm.random.seed(1234)
62+
freqs = 200. # Hz
63+
num, batch = 30, 4
64+
duration = 1000. # ms
65+
model = input.PoissonGroup(size=num, freqs=freqs, mode=bm.BatchingMode(batch))
66+
runner = bp.DSRunner(model, monitors=['spike'], progress_bar=False)
67+
runner.run(duration)
68+
spikes = np.asarray(runner.mon['spike']) # (batch, time, num)
69+
self.assertEqual(spikes.shape[0], batch)
70+
self.assertEqual(spikes.shape[-1], num)
71+
empirical_rate = spikes.sum() / (batch * num) / (duration / 1000.)
72+
self.assertAlmostEqual(empirical_rate, freqs, delta=0.15 * freqs)

0 commit comments

Comments
 (0)