Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node and connection parametrization #4

Closed
jougs opened this issue Dec 6, 2017 · 10 comments
Closed

Node and connection parametrization #4

jougs opened this issue Dec 6, 2017 · 10 comments

Comments

@jougs
Copy link

jougs commented Dec 6, 2017

This issue describes the planned work towards supporting probabilistic parameters for nodes similar to what NewConnect gave us for connection setup.

1. Develop a prototype starting from the Python level

Starting on the Python level makes prototyping fast and gives us the opportunity to get first experiences and user feedback quickly. We can push things to the Cython/C++ level if profiling shows that it is required or if it makes certain parts easier or more reusable.

One problem with this approach might be that users will have full freedom for the specification of parameters, i.e. they can (and probably will) use arbitrary Python expressions to define parameters.

The main challenge then is to provide continued support for these expressions also once we decide to move the code to a lower level. Code generation and Python-callbacks based on Cython, CTypes, Numexpr, SWIG, or Numba would allow this. In most situations this will bring the natural restrictions coming with the global interpreter lock.

2. Use cases

Create 10 neurons with uniformly distributed V_m

n.Create(
    'iaf_psc_alpha',
    n=10,
    params={
        'V_m': nest.random.uniform(-75.0, -55.0)
    }
)

Create 3 neurons with V_m taken from an iterable

n.Create(
    'iaf_psc_alpha',
    n=3,
    params={
        'V_m': [-75.0, -69.0, -73.4]
    }
)

Create 10 neurons with tau_m drawn from a Gaussian distribution; values below 0.5 ms are rejected and replaced by newly drawn values

n.Create(
    'iaf_psc_alpha',
    n=10,
    params={
        'tau_m': nest.random.normal(
            loc=10.0,
            scale=5.0,
            min=0.5,
            redraw=True
        )
    }
)

Create 10 neurons with V_m drawn from an exponential distribution p(V ) = Θ(V+80)e−0.1(V+80)

n.Create(
    'iaf_psc_alpha',
    n=10,
    params={
        ’V_m’: −80.0 + nest.random.exponential(scale=0.1)
    }
)

Connect two populations with negative weights drawn from a lognormal distribution.

n.Connect (
    inh, 
    exc,
    conn_rule={
        ’rule’: ’fixed_indegree’,
        ’indegree’: 100
    },
    syn_spec={
        ’weight’: −nest.random.lognormal(
            mean=100.0,
            sigma=20.0
        )
    }
)

Create a population of 400 Poisson generators with sinusoidally modulated rate, spread in a [-1, 1] x [-2, 2] box using a Sobol quasirandom number sequence. The phase of each sinusioid is determined by the x-coordinate of the generator.

n.CreateSpatial(
    ’sinusoidal_poisson_generator’,
    n=400,
    extent=[(-1, 1), (-2, 2)],
    position=nest.random.sobol(),
    params={
        ’rate’: 100.0,
        ’amplitude’: 100.0,
        ’frequency’: 10.0,
        ’phase’: 180.0nest.node.pos.x
    }
)

Connect two spatially distributed populations using a distance-dependent probabilistic rule with probability p(d) = e -0.2d and no connections for d > 0.5.

n.ConnectSpatial(
    src,
    tgt,
    conn_spec={
        ’rule’: ’pairwise_bernoulli’,
        ’mask’: nest.spatial.distance(src, tgt) < 0.5,
        ’p’: nest.math.exp(-0.2 * n.spatial.distance(src, tgt))
    },
    syn_spec={
        ’weight’: 10.0 - 0.1nest.spatial.distance(src, tgt),
        ’delay’: nest.random.uniform(0.5, 2.5)
    }
)

3. Implementation

In order to support the use cases outlined above, we require a general Parameter class. This class can be used in all places where a parameter (either a concrete value, a random number drawn from a distribution, or a value obtained from calling back into Python) is needed.

The implementation should use a Parameter base class in the kernel with a derived class in PyNEST, which can use implement the callback logic for executing arbitrary functions in Python to draw parameters, read out arrays, etc.

@jougs
Copy link
Author

jougs commented Feb 7, 2018

The intentions described here were already described in this issue: nest#352. I've closed that in favor of this one.

@jougs
Copy link
Author

jougs commented Feb 7, 2018

Another source of information is nest#488

@espenhgn
Copy link

Hi, looked at @jougs comment above. I think it looks good in terms of proposed syntax. I would like functionality to compute distance along a chosen axis (nest.spatial.distance(src, trg, axis=0) or similar. Support for trigonometric functions would be great (nest.math.cos(**kwargs)), as well as support for N-dimensional layers: Say layers where neurons have location (x,y,z) and a fourth dimension representing a feature (e.g., orientation). That could make it easier to create connections with rules that for example should preferably connect neurons with similar preferred features.

@jhnnsnk
Copy link

jhnnsnk commented Nov 6, 2018

I am in favor of the suggestion in #8 (comment) by @heplesser to use a single Create command only. If spatial information is given, the GIDCollection gets the property metadata=spatial instead of metadata=None or similar.

Likewise, I propose to have a single Connect command only. Connection specifications that depend on space can then be applied only if metadata=spatial is true for source and/or target (dependent on which spatial information is required).
Connection specifications that do not require space would just disregard the spatial properties.

As outlined by @jougs, Create can take the nodes' absolute positions into account and Connect the inter-node distances. In addition, I would like to have the possibility that Connect relies on the absolute positions (x,y,z) of both sources and targets, for example:

conn_spec={
    ’rule’: ’pairwise_bernoulli’,
    ’p’: n.math.exp(-0.2 * n.spatial.distance(src, tgt)) if n.srcnode.pos.x < 0.5 else 0.0
 },

I further envision more flexibility in combining parameters with spatial dependency and drawn from a distribution. For example, a noisy distance-dependent delay:

syn_spec={
    ’delay’: n.random.normal(
         mu=0.5 + n.spatial.distance(src, tgt)/0.3,
         sigma=0.2 * (0.5 + n.spatial.distance(src, tgt)/0.3),
         min=0.1,
         redraw=True)
}

I also consider it useful to prescribe the total number of connections for spatial connections:

conn_spec={
    ’rule’: ’fixed_total_number’,
     'N': 100000,
     ’p’: n.math.exp(-0.2 * n.spatial.distance(src, tgt))
}

and to specify a distribution of indegrees:

conn_spec={
    ’indegree’: n.random.normal(
         mu=1000.0,
         sigma=10.0,
         min=10),
     ’p’: n.math.exp(-0.2 * n.spatial.distance(src, tgt))
}

@babsey
Copy link

babsey commented Feb 7, 2019

I like the idea of python library in nest for generating parameters for nodes and connections

However, I tested the performance of the creating nodes with generating numbers for one parameter in two different codes. I computed the first example of jougs in iPython with larger number of nodes (n=10000).

%timeit nest.Create('iaf_psc_alpha', n, {'V_m': nest.random.uniform(-75., -55.)})
%timeit nest.Create('iaf_psc_alpha', n, {'V_m': np.random.uniform(-75., -55., n)})

The only difference between the lines is that I compare number generator of nest and numpy.
It seems that the generating process by numpy is faster than by nest. In my view the parameter library of topology was basically designed to generate number depending on the distance. But for non-spatial number generator, we could take the advantage of the number generator of numpy.

Please correct me if I am wrong.

@jougs
Copy link
Author

jougs commented Feb 10, 2019

I am currently rewriting the whole random number generation framework in NEST to be based on C++11 features. The idea would then also be to not draw numbers on the Python level but just push the description down to C++ and draw numbers there. I guess that benchmarking only really makes sense when the code is in place. I'll replace back here once there is more progress.

stinebuu pushed a commit that referenced this issue Feb 27, 2019
Travis-CI logparser: set build return code
@hakonsbm
Copy link

After a discussion with @heplesser, we decided that when connecting spatially distributed populations, mask specification can only be one of the shapes defined in NEST (rectangular, circular, etc.).

It is possible to achieve the same functionality using parameters in the probability specification, but using masks of pre-defined simple shapes is more efficient. Specifying a mask will therefore only be for efficiency's sake.

@stinebuu
Copy link

Hi @jougs @jhnnsnk @heplesser @espenhgn @babsey !

Me and @hakonsbm have started working on the spatial version of Create, where you can, for instance, use a Parameter to determine the posistion of the nodes:

n.CreateSpatial(
    ’sinusoidal_poisson_generator’,
    n=400,
    position=nest.random.sobol(),
    params={
        ’rate’: 100.0,
        ’amplitude’: 100.0,
        ’frequency’: 10.0,
        ’phase’: 180.0nest.node.pos.x
    }
)

We have encountered a problem though, and hopefully you have some good solutions. How can we determine how many dimensions we want when we send in a Parameter as position?

@heplesser
Copy link
Member

@stinebuu @hakonsbm The CreateSpecial() call must receive

    extent=[(-1, 1), (-2, 2)]

as argument, which defines the number of dimensions.

@stinebuu
Copy link

Fixed in the nest-3 branch! If new ideas or problems pop up, the best thing to do might be to open a new issue on the nest-simulator page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants