NeuroMaC examples

This section provides examples highlighting the key features of NeuroMac and illustrate how to use the software suite.

All examples are found in the examples directory. Bash shell scripts are available to run the models and convert the raw output into useful output (such as a SWC-file or figure). Check these shell scripts for individual commands.

Note

For beginners it is strongly advised not to start a NeuroMac model from scratch but to start with an example and adopt it to meet your needs.

Random walk

Not truly a random walk. Rather, several branches grow from the soma, start of straight before orientating themselves randomly. Consequently, the branches do not grow for long as they will inevitably bump into one another. The resultant structural overlaps are detected by NeuroMac and cause the branches to terminate.

Run code

Run the example by executing the prepared bash script

$ cd examples # Or from wherever you came. From now I assume you're in the examples directory
$ ./run_random_walk.sh

This script (as well as all further example scripts) will do several things:

  1. Run the model. Running the model generates a SQL database containing all generated structures.
  2. Generate one SWC-file of the produced structure
  3. Generate a movie of the development of the structure.
  4. Generate two plots of the produced structure with and without radii, respectively.

The following files will be produced:

random_walk.db # raw SQL database
cell_type_1__0.swc  # SWC file
random_walk.mp4 # movie
random_walk_radii.pdf # plot with radii
random_walk_wire.pdf  # wire plot

Implementation details

The user has to write two files: a configuration file and a file containing the growth cones. The configuration file (random_walk/random_walk.cfg) defines properties of the brain volume to simulate as well as which structures to grow in accordance to a specified set of rules. Also, the configuration file contains some parameters for NeuroMaC itself (see Configuration file).

Of most interest is the file containing the growth-rules:

random_walk/Random_machine.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from growth_procs import unit_sample_on_sphere,\
     normalize_length,\
     prepare_next_front

L_NORM = 3.0

def extend_front(front,seed,constellation) :
    if front.order == 0:
        new_fronts = []
        for i in range(5):
            rnd_dir = unit_sample_on_sphere()
            new_pos = normalize_length(rnd_dir,L_NORM*5)
            new_pos = front.xyz + new_pos
            new_front = prepare_next_front(front,new_pos,set_radius=1.0,add_order=True)
            new_fronts.append(new_front)
        return new_fronts
            
    else:
        rnd_dir = unit_sample_on_sphere()
        new_pos = normalize_length(rnd_dir,L_NORM)
        new_pos = front.xyz + new_pos
        new_front = prepare_next_front(front,new_pos,set_radius=1.0)
        return [new_front]

In the first three lines several helper functions are imported. These come with NeuroMac (Front implementation helper functions). In short, the unit_sample_on_sphere is used to sample a 3D vector used as the component for random growth. normalize_length is used to scale a vector to the desired length while prepare_next_front is a secure wrapper to extend a front (If the work “front” does not ring a bell, please consult the NeuroMaC Rationale)

The actual growth-rule is described in extend_front(front,seed,constellation). Always include this exact method definition or NeuroMaC will not work!

In this case two separate rules are included. Two is the unofficial default: one rule describes the behaviour at the soma (or any other initial point with front.order==0) while the other rules covers the rest. Here five initial branches are started. Afterwards a front elongates with purely random angles.

  • rnd_dir = unit_sample_on_sphere(): Get a unit length random 3D vector.
  • new_pos = normalize_length(rnd_dir,L_NORM*5): Scale a vector to a length of L_NORM*5.
  • new_front = prepare_next_front(front,new_pos,set_radius=1.0,add_order=True): Create the next front.

Warning

extend_front() always has to return a list of fronts. If only a single front is returned (as happens for elongations), return a list: return [new_front]. See Front growth-rule specification

Attraction between fronts

Basic example illustrating phenomenological attraction between two developing neurites. One neurite structure simply grows straight while the second neurite is attracted by the first one.

In addition, this example illustrates how to parallelize the execution by decomposing the volume.

Code can be run by executing the following wrapper-command:

$ ./run_attraction_demo.sh

Configuration file

Below the configuration file. Note the section sub_volumes. The volume can be decomposed in a number of rectangles along the x, y and z-directions as specified by xa, ya and za. Think of this decomposition as making the specified number of slices along an axis. The total number of volumes is . The number has to be given as argument to the Admin.py script as it specifies the number of Subvolume agents.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[system]
seed=21051982
proxy_sub_port= 5599
proxy_pub_port= 5560
pull_port=55002
time_out = 10000
no_cycles=30
out_db=demo_attraction/demo_attraction.db
synapse_distance = 5

[substrate]
dim_xyz = [100.0,100.0,100.0]

[sub_volumes]
xa=1
ya=1
za=2

[cell_type_1]
no_seeds=1
algorithm = TestF_Left
location = [[20,50,20],[20,50,20]]  
soma_radius = 10

[cell_type_2]
no_seeds= 1
algorithm = TestF_Right
location = [[80,80,80],[80,80,80]] 
soma_radius = 10

Implementation details

The first neurite has a trivial growth-rule so that it grows straight from one side to the other of the volume.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import numpy as np

from growth_procs import prepare_next_front

L_NORM=4.0

def extend_front(front,seed,constellation) :
    # this structure grows straight without ccontextual interaction
    new_pos =front.xyz + np.array([-1.0*L_NORM,0,0])
    new_front = prepare_next_front(front,new_pos,set_radius=1.5)
    return [new_front]

The second neurite is more interesting in that its direction of growth depends on the first neurite. More precisely, the second neurite is attracted to the nearest front of the first neurite.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

from growth_procs import direction_to,\
     normalize_length,\
     get_entity,\
     prepare_next_front

L_NORM=3.0

def extend_front(front,seed,constellation) :
    # attract by a different neuron, get information
    other_entities = get_entity("cell_type_2",constellation)
    if not len(other_entities) == 0:
        dir_to_entity = direction_to(front,other_entities,what="nearest")
        dir_to_entity = normalize_length(dir_to_entity,1.0)
    else: # during the first extension cycle, no distal info is known locally
        dir_to_entity = np.array([.5,.0,.0])
    new_pos = front.xyz + normalize_length(dir_to_entity,L_NORM)
    new_front = prepare_next_front(front,new_pos,set_radius=1.5)
    return [new_front]

Warning

In case the volume is decomposed into many sub volumes, and attraction between fronts of non-neighboring sub volumes occurs , special piece of code needs to be added to the growth rule. In the aforementioned case, during the first update cycle an updating front cannot know anything about fronts that are in sub volumes beyond the direct neighbors. Only in the second cycle, they will receive a “summary” of all distant sub volumes. As such, during the first cycle, one has to catch the case in which no attracting/repulsive fronts are found.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

from growth_procs import direction_to,\
     normalize_length,\
     get_entity,\
     prepare_next_front

L_NORM=3.0

def extend_front(front,seed,constellation) :
    # attract by a different neuron, get information
    other_entities = get_entity("cell_type_2",constellation)
    if not len(other_entities) == 0:
        dir_to_entity = direction_to(front,other_entities,what="nearest")
        dir_to_entity = normalize_length(dir_to_entity,1.0)
    else: # during the first extension cycle, no distal info is known locally
        dir_to_entity = np.array([.5,.0,.0])
    new_pos = front.xyz + normalize_length(dir_to_entity,L_NORM)
    new_front = prepare_next_front(front,new_pos,set_radius=1.5)
    return [new_front]

In this example, we quickly fixed the problem by extending the front a little bit during the first cycle when no attraction from a distant front is felt. From the second cycle on, the front behaviour is as expected.

This example is included in the examples directory and can be run by executing ./run_distant_attraction.sh.

Interaction between fronts and the substrate

One way of interactions between a front and the substrate is through entities that are physically located in the substrate. For instance, borders can be added by means of a point-cloud. In case of the cortex, a gradient leading towards the pia exists; most likely through reelin excreted from Cahal-Retzius cells REF or radial glia REF.

In this example we set up such a point-cloud representing the pia and to which a neurite will be attracted. (Check Interactions between fronts and substrate for info about generating the point cloud).

Code can be run by executing the following wrapper-command:

$ ./run_env_attraction1.sh

Substrate configuration

To register the point cloud in the simulated volume, add it in the substrate section of the config file as illustrated below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[system]
seed=21051982
proxy_sub_port= 5599
proxy_pub_port= 5560
pull_port=55002
time_out = 10000
no_cycles=80
out_db=straight_to_pia/straight.db
synapse_distance = 5

[substrate]
dim_xyz = [100.0,100.0,200.0]
pia = straight_to_pia/new_pia_points.pkl

[sub_volumes]
xa=1
ya=1
za=1

[cell_type_1]
no_seeds=1
algorithm = Straight
location = [[50,50,10],[50,50,10]]  
soma_radius = 10

Implementation details

The growth-rules are very similar to the rules required to implement attraction between neurites. The sole difference is that a list of “pia” entities is queried from the substrate.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np

from growth_procs import direction_to,\
     normalize_length,\
     get_entity,\
     prepare_next_front

L_NORM=3

def extend_front(front,seed,constellation) :
    """
    Growth is directed by an attraction to the "pia"; a point-cloud \
    located at the top of the volume. (See straight.cfg)
    """
    other_entities = get_entity("pia",constellation)
    dir_to_entity = direction_to(front,other_entities,what="nearest")
    dir_to_entity = dir_to_entity
    new_pos = front.xyz + normalize_length(dir_to_entity,L_NORM)
    new_front = prepare_next_front(front,new_pos,set_radius=1.0)

    return [new_front]

Fronts updating the substrate

This example illustrates how a front can update the substrate by leaving an cue. This cue can be picked up by another front (or by the front itself; but that would be pointless) in the usual way.

Code can be run by executing the following wrapper-command:

$ ./run_update_env.sh

Configuration file

The configuration file contains no special instructions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[system]
seed=21051982
proxy_sub_port= 5599
proxy_pub_port= 5560
pull_port=55002
time_out = 10000
no_cycles=75
out_db=update_environment/update.db

[substrate]
dim_xyz = [100.0,100.0,100.0]

[sub_volumes]
xa=1
ya=1
za=1

[cell_type_1]
no_seeds=1
algorithm = Update_env_machine
location = [[80,50,50],[80,50,50]]  
soma_radius = 10

[cell_type_2]
no_seeds=1
algorithm = Attracted_by
location = [[40,10,10],[40,10,10]]  
soma_radius = 10

Front implementation

One front drops a series of cues at a specific moment. For demonstration purposes this moment is expressed in terms of update steps (a attribute of front.Front).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import numpy as np

from growth_procs import prepare_next_front

L_NORM = 5

def extend_front(front,seed,constellation) :
    new_pos =front.xyz + np.array([-1.0*L_NORM,0,0])
    new_front = prepare_next_front(front,new_pos,set_radius=3.0)

    # secrete some "substance_x" at some point
    if front.path_length > 40 and front.path_length < 46 :
        # special syntax for updating the environent
        return [new_front],{"substance_x":front}
    else :
        return [new_front]

The other neurite can try to sense this substance. However, as long as the cue is not available, the neurite should catch the case that growth_procs.get_entity() returns an empty list, which cannot be passed to the growth_procs.direction_to() function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import numpy as np

from growth_procs import unit_sample_on_sphere,\
     direction_to,\
     normalize_length,\
     get_entity,\
     prepare_next_front

L_NORM=2.0

def extend_front(front,seed,constellation) :
    # attract by a different neuron, get information
    other_entities = get_entity("substance_x",constellation)

    # fetch the case that no such entities exists    
    if len(other_entities) == 0 :
        new_pos =front.xyz + np.array([L_NORM,0,0])
    else :
        rnd_dir = unit_sample_on_sphere()
        dir_to_entity = direction_to(front,other_entities,what="nearest")
        dir_to_entity = normalize_length(dir_to_entity,1.0) + rnd_dir*0.3
        new_pos = front.xyz + normalize_length(dir_to_entity,L_NORM)
    new_front = prepare_next_front(front,new_pos,set_radius=1.5)

    return [new_front]

Miscellaneous examples

Forests of neurons

NeuroMac offers the opportunity to generate large numbers of neurites that may or may not have the same growth-rules. In this example, we grow a forest of neurons according to the same growth rules.

This can be specified in the configuration file [here to_pia/many.cfg]:

[cell_type_proto_purk1]
no_seeds=10
algorithm = Meander_to_pia
location = [[20,20,20],[180,180,20]]
soma_radius = 10

Built-in structural conflict detection

./run_intersection.sh

Self-avoidance

./run_selfavoidance.sh

Gradual attraction

Distance-dependent attraction

./run_gradient.sh