|


The user is not limited to operating within the traditional "code-based
command-mode environment." Among its many extensions to hoc, NEURON
includes functions for implementing a fully graphical, windowed interface.
Through this interface, and without having to write any code at all, the user
can effortlessly create and arrange displays of menus, parameter value editors,
graphs of parameters and state variables, and views of the model neuron.
Anatomical views, called "space plots," can be explored, revealing what
mechanisms and point processes are present and where they are located.
The purpose of NEURON's graphical interface is to promote a match between what
the user thinks is inside the computer, and what is actually there. These
visualization enhancements are a major aid to maintaining conceptual control
over the simulation because they provide immediate answers to questions about
what is being represented in the computer.
The interface has no provision for constructing neuronal topology, a conscious
design choice based on the strong likelihood that a graphical toolbox for
building neuronal topologies would find little use. Small models with simple
topology are so easily created in hoc that a graphical topology editor
is unnecessary. More complex models are too cumbersome to deal with using a
graphical editor. It is best to express the topological specifications of
complex stereotyped models through algorithms, written in hoc, that
generate the topology automatically. Biologically realistic models often
involve hundreds or thousands of sections, whose dimensions and
interconnections are contained in large data tables generated by hours of
painstaking quantitative morphometry. These tables are commonly read by
hoc procedures that in turn create and connect the required sections
without operator intervention.
The basic features of the graphical interface and how to use it to monitor and
control simulations are discussed elsewhere (Moore and Hines 1996). However,
several sophisticated analyis and simulation tools that have special utility
for nerve simulation are worthy of mention.
|



It is often convenient to deal with groups of sections that are
related. Therefore NEURON provides a data class called a SectionList
that can be used to identify subsets of sections. Section lists fit nicely
with the "regular expression" method of selecting sections, used in earlier
implementations of NEURON, in that
objref alldend alldend = new SectionList() forsec "dend" alldend.append() forsec alldend print secname()forms a list of all the sections whose names contain the string "dend" and then iterates over the list, printing the name of each section in it. For the example program presented in this report, this would generate the following output in the NEURON interpreter window dendrite[0] dendrite[1] dendrite[2]although in this very simple example it would clearly have been easy enough to loop over the array of dendrites directly, e.g.
for i = 0,2 {
dendrite[i] print secname()
}
|

To help the user manage very large simulations, the interpreter syntax
has been extended to facilitate the construction of hierarchical objects. This
is illustrated by the following code fragment, which specifies a pattern for a
simple stylized neuron consisting of three dendrites connected to one end of a
soma and an axon connected to the other end.
|
begintemplate Cell1
public soma, dendrite, axon
create soma, dendrite[3], axon
proc init() {
for i=0,2 connect dendrite[i](0), soma(0)
connect axon(0), soma(1)
axon insert hh
}
endtemplate Cell1
Whenever a new instance of this pattern is created, the init()
procedure automatically connects the soma, dendrite, and
axon sections together. A complete pattern would also specify default
membrane properties as well as the number of segments for each section.
Names that can be referenced outside the pattern are listed in the
public statement. In this case, since init is not in the
list, the user could not re-initialize by calling the init()
procedure. Public names are referenced through a dot notation.
The particular benefit of using templates ("classes" in standard object
oriented terminology) is the fact that they can be employed to create any
number of instances of a pattern. For example,
|
objref cell[10][10] for i=0,9 for j=0,9 cell[i][j]=new Cell1()
creates an array of 100 objects of type Cell1 that can be referenced
individually via the object variable cell. In this example,
cell[4][5].axon.gnabar_hh(0.5) is the value of the maximum HH sodium
conductance in the middle of the axon of cell[4][5].
As this example implies, templates offer a natural syntax for the creation of
networks. However it is entirely up to the user to logically organize the
templates in such a way that they appropriately reflect the structure of the
problem. Generally, any given structural organization can be viewed as a
hierarchy of container classes, such as cells, microcircuits, layers, or
networks. The important issue is how much effort is required for the concrete
network representation to support a range of logical views of the same abstract
network. A logical view that organizes the cells differently may not be easy
to compute if the network is built as an elaborate hierarchy. This kind of
pressure tends to encourage relatively flat organizations that make it easier
to implement functions that search for specific information. The bottom line
is that network simulation design remains an ad hoc process that requires
careful programming judgement.
One very important class of logical views that are not generally organizable
as a hierarchy are those of synaptic organization. In connecting cells with
synapses, one is often driven to deal with general graphs, which is to say, no
structure at all.
In addition to the notions of classes and objects (a synapse is an object with
a pre- and a postsynaptic logical connection) the interpreter offers one other
fundamental language feature that can be useful in dealing with objects that
are collections of other objects. This is the notion of "iterators," taken
from the Sather programming language (Murer et al. 1996). This is a separation
of the process of iteration from that of "what is to be done for each item."
If a programmer implements one or more iterators in a collection class, the
user of the class does not need to know how the class indexes its items.
Instead the class will return each item in turn for execution in the context of
the loop body. This allows the user to write
|
for layer1.synapses(syn, type) {
// statements that manipulate the object
// reference named "syn" (The iterator
// causes "syn" to refer, in turn,
// to each synapse of a certain type
// in the layer1 object)
}
without being aware of the possibly complicated process of picking out these
synapses from the layer (that is the responsibility of the author of the class
of which layer1 is an instance).
It is to be sadly emphasized that these kinds of language features, though
very useful, do not impose any policy with regard to the design decisions users
must make in building their networks. Different programmers express very
different designs on the same language base, with the consequence that it is
more often than not infeasible to reconcile slightly different representations
of even very similar concepts.
An example of a useful way to deal uniformly with the issue of synaptic
connectivity is the policy implemented in NEURON by Lytton (1996). This
implementation uses the normal NMODL methodology to define a synaptic
conductance model and enclose it within a framework that manages network
connectivity.
|

|
|
Address questions and inquiries to Michael Hines or Ted Carnevale
Digital preprint of "The NEURON Simulation Environment" by M.L. Hines and N.T. Carnevale,
Neural Computation, Volume 9, Number 6 (August 15, 1997), pp. 1179-1209.
Copyright © 1997 by the Massachusetts Institute of Technology, all rights reserved. |