How nengo generates Gaussian white noise


I am trying to test the robustness of my model by adding a moderate amount of Gaussian white noise. However, when calculating the noise power, I found that the power and variance were not equal. To test the code correctly, I used Numpy to generate a Gaussian white noise whose power and variance were equal. I am confused as to why the power and variance of the Gaussian white noise in Nengo are not equal. The code is as follows:

import nengo
import matplotlib.pyplot as plt
import numpy as np

model = nengo.Network(label="test for Gaussion noise")
with model:
    Gauss = nengo.Node(np.random.normal(loc=0.0, scale=0.707))
    Probe_Gauss = nengo.Probe(Gauss)
with nengo.Simulator(model) as sim:
pnoise2 = sum(np.square([Probe_Gauss]))/len([Probe_Gauss])   # NOT Equal to 0.5

a = np.random.normal(loc=0.0, scale=0.707, size=10000)
pnoise2 = sum(np.square(a))/len(a)  # Equal to 0.5

Also, when defining an ensemble you can specify its corresponding noise, does this have the same effect as creating a Node that stores noise connected to the ensemble? I save the input in an npy file, how to read it out and connect it to Ensemble?

Any good ideas or examples are welcome! :smile:

What’s happening is when you create your Node, the np.random.normal(loc=0.0, scale=0.707) call is creating one random number, which is then passed to the Node and used as a constant value that does not change each timestep.

One way to get noise that varies each timestep would be

    Gauss = nengo.Node(lambda t: np.random.normal(loc=0.0, scale=0.707))

However, the preferred way to implement this in Nengo is to use the WhiteNoise process:

    noise_process = nengo.processes.WhiteNoise(
        dist=nengo.dists.Gaussian(mean=0, std=0.707),
    Gauss = nengo.Node(noise_process)

Note that I’ve set scale=False to get the same result as your example. Often, you want to use scale=True to get proper scaling on the white noise, such that the integral doesn’t change if you change the Simulator dt. See the WhiteNoise docstring for more info.

1 Like

Hi @YL0910,

The behaviour you are observing is a result of how the nengo.Node interprets inputs given to it when it is created. With a nengo.Node, if the input is a callable object (e.g., a Python function), the node will call that callable object during each timestep to generate the node’s output. However, if the node is presented with a non-callable object (e.g., a Python list, or scalar value), the node will output that value (unchanged) for every timestep in the simulation.

Since np.random.normal(...) returns a scalar value when called, what Nengo is doing is evaluating the normal function call (i.e., generating a sample) and then using the same value for every simulation timestep. You can see the result of this by printing out the values of[Probe_Gauss].

To get the Nengo node to function like the standalone Numpy call, you’ll need to encapsulate the np.random.normal(...) function within a function. The easiest way to do it is to use a lambda function, like so:

with model:
    Gauss = nengo.Node(lambda t: np.random.normal(loc=0.0, scale=0.707))

When you use the code above, Nengo will call the lambda function in each timestep of the simulation, which in turn calls the np.random.normal function, generating a new value for each timestep.

I should note that Nengo also provides a WhiteNoise process to generate Gaussian white noise signals as well. This Nengo example goes through what processes are, and how to use them. For your specific code, the equivalent Nengo node (with process) looks like this:

with model:
    Gauss = nengo.Node(
        nengo.processes.WhiteNoise(dist=nengo.dists.Gaussian(0, 0.707), scale=False)

Sort of. If you read the documentation for the nengo.Ensemble, you’ll see that the noise parameter is:

The difference between using a node to inject noise into an ensemble, and using the noise parameter of the ensemble is that signals injected into an ensemble via a connection will have the signal go through (get multiplied) by the ensemble’s encoders (if you are not connecting directly to the .neurons object). If you use the noise parameter when creating the ensemble, the noise is injected as current, bypassing the ensemble’s encoders. If you want to inject custom noise into the ensemble, you can create your own process (see the example I linked above) to read the data from file, and output them into your simulation.

Alternatively, if your noise data is not that large, you can read the data (from file) into memory, then use a Python function (with nengo.Node) or a custom process to iterate through the data during each timestep of the simulation.

EDIT: It seems like while I was crafting my response, @Eric did post a response to some of your questions, but we both arrived at the same conclusion. :grin:

Thank you for your friendly and detailed answers. With your answer I have solved my problem, thank you very much! :smile:

1 Like