How to write a new NEOSIM module

This guide is intended to help people write new modules for Neosim. Such modules can code for new simulation entities (e.g. integrate and fire, or compartmental entities), new visualisation entities, new projection schemes, new ways to specify populations. Example modules are provided in the development kit in the examples/ directory which should provide a useful basis for developing user modules.

The basics - what's a Neosim module?

Neosim modules are encapsulated in Java Archive files (.jar). This format is similar to the Unix tar format, but is platform independent and includes an extra header file or "manifest". This includes text information such as which of the files in the archive is the main(). The "jar" for a module include all the code (.class files) and any other resources such as images which the module needs.

A module is made by writing the code and packing it into a jar file with a suitable manifest. This can be easily automated using a makefile.

Once a module is completed, the .jar file can be placed in an area accessible via a URL, and can then be incorporated into any other model in the world. Importantly, local models which rely on the module being available can be run by other people without them having to reinstall software.

An example module - a new type of entity

In this example, we wish to code a new type of entity which counts how many input spikes arrive.

New entities are written by extending a built in class, EntityImpl. This is a basic implementation of the Neosim Entity interface, and the method which needs to be written is called handleEvents. This is called by the Neosim kernel to advance the time of each entity, and to deliver its events.

class Counter extends EntityImpl {
  public Time handleEvents(EventList el, Time endt) {
    // Deal with incoming events, advance local time to endt
    return endt;
  }
}
The code inside handleEvents receives incoming events in the ordered EventList. These events are of type Event. An example implementation of this method is:
  public Time handleEvents(EventList el, Time endt) {
    boolean passed = false;
    while (el.hasMoreElements() && !passed) { 
      Event e = el.top();
      if (e.getTime().t <= endt.t) {
	e = el.pop();
	if (e.getConnection().getDstPortID().id == 0) {
          // Port 0 is used for internal system events
	  defaultEventHandler( e );
        } else {
	  Time outTime = e.getTime(); 
	  EntityID src = e.getConnection().getSrcEntityID();
	  System.out.println("Counter: received spike event");
	}
      } else { 
	passed = true; 
      }
    }
    return endt;
  }

How do we make this class into a plug-in module?

Simple answer:

The process of building and exporting a module is automated for the examples included in the Neosim development kit, so to build a module you just need to type:
cd examples/entityjar
make

Detailed answer:

As well as an Entity there needs to be an EntityClass which contains a method makeEntity responsible for building entities. The reason for having a separate class to do this is that it can store all the information about an entity which is common between instances of the entity.

public class CounterClass extends EntityClassImpl {
    PortID in; 
    public CounterClass() {  
 	super("CounterClass");
	in = addInPort(SpikeEvent.inst.getEventClass(), "in"); 
    } 
    public Entity makeEntity(Population p, int i) {  
	return new Counter(this, p, i); 
    }
}
This can then be made into a module using the JAR tool provided with the Java development kit:
javac CounterClass.java
jar cvf CounterClass.jar manifest *.class
... where the manifest file looks like:
Name: CounterClass.class
Java-Bean: True
and the result is a file:
CounterClass.jar
which can be made available on the web and included into anyone's simulation model.

How do we make a test XML file?

Along with each module there should be a test XML file to check the module works, and to act as an example for people wanting to use the module. For the counter module, a simple test would be to connect a single spike generator to the counter.


<neosim>	
  <header>	
    <author>Fred Howell</author> 
    <documentation>Test the counter module.</documentation>
  </header>
 
  <!-- Define the entities and networks available-->
  <prototypes>
    <define-entity name="counter" modulename="libCounter"/>
    <define-entity name="spike" modulename="libSpike"/>
    <define-network name ="simple">
        <population name="spikepop"  entityname="spike" num="1" />
 	<population name="counterpop" entityname="counter" num="1" />
	<projection name="prj1" srcPop="spikepop" dstPop="counterpop" type="standard" >
	  <connection src="0" scrPort="out" dst="0" dstPort="in" delay="1"/> 
	</projection>
    </define-network>
  </prototypes>


  <!-- Instantiate a model-->
  <model>
    <network name ="simple"/>   
  </model>

  <!-- Control the experiment - specify locations of modules, run time -->
  <experiment>	 
    <module name="libSpike" location="http://www.neosim/org/jars/spikegen.jar" version="1" />	
    <module name="libCounter" location="http://www.neosim.org/jars/CounterClass.jar" version="1" />	
    <control>
      <time value="10.0"/>
    </control>
  </experiment>
</neosim>
This can then be run using the XML reader .

Other modules

A user coded script reader

The main() for a simulation can be provided by a plug-in script reader (the XML reader is one example of this). A basic example of this is included in examples/scriptjar.
/** A basic "Hello World" script reader for neosim,
 * to demonstrate how to package Script Readers in 
 * to JAR files
 */	
public class HelloScript extends BasicApp {
  
    public HelloScript() { super(); } 
   
    /** Bootstrap - runs in main thread
     */
    public void bootstrap(String args[]) {
	super.bootstrap(args);

	System.out.println("=== NEOSIM Hello Script " );	
	PopulationNode root = getPopulationTree();

	/** Here we can create cells and run a simulation 
	 */
	System.out.println("Hello world" );	
    }     
}
The method which is overridden is bootstrap which accepts command line arguments, and has access to the script reader commands for controlling a simulation.

A user coded control module

A control module provides a user control sequence which can be included from an XML file, augmenting the <control> element. It is like a cut-down script reader.

An example, which runs the simulation for 100.0s is:-

public class HelloControl extends ControlModuleImpl {  
    Time t;
    String msg;
    public void init(Hashtable params) {
        t = new Time(100.0);
        msg = params.get("message");
        // could set parameters from the name/value params hashtable
    }
    public void run(ScriptReader sr) {
	System.out.println("Hello from control module message="+msg);
        sr.runSim( t );
        // issue script reader commands, queries, etc etc
    }     
}
An example is in examples/controljar. It can be included in an XML file using:
<control>
  <module name="sayhello" location="file:./hellocontrol.jar" message="hello bob"/>
</control>

A user coded projection scheme

User specific projection schemes can be included - see the example in examples/projectionjar and the overview of user projections. In general, a projection is specified as a source method and a dest method. A new scheme, which connects each entity in the source population to all the entities within a given distance could be written:
class ConnectToSphere extends SourceMethodImpl {
    public void sendRequests( Entity srce, Population dPop, DestMethod dm ) {
       // get list of members of dPop within radius
       // for each, call sendRequest() to send a connection request
    }
}

class Proj3DSphereImpl extends GeneralProjectionImpl {
   public Proj3DSphereImpl( Population srcPop, PortID outPort, 
                            Population destPop, PortID inPort, 
                            Time delay, double radius ) {
      super( srcPop, destPop, 
             new ConnectToSphere(outPort,radius), 
             new DestMethodImpl(inPort,delay));
   }
}
In this example, the source method sends requests to all entities within a certain distance, and the default destination method fulfills all requests. Another way to implement this would be for the source method to send requests to all entities, and have the destination method compute the distance and say yay or nay. This would be a much slower implementation.

What about C++?

Native code can be linked in a module using the Java Native Interface (JNI). The example for this is in examples/c++entityjar. The problem with C++ modules is that they require an architecture specific shared library (libxxx.so for unix, xxx.dll for Windows), so are less portable.

Conclusions

This brief guide has introduced the way to build plug-in modules for Neosim. Some issues remain about how the development and use of modules built in different places will work in practice; if A uses a module from B, and B changes his module, what happens? There are a number of possible solutions: taking local copies of modules may be the simplest.


Fred Howell
Last modified: Fri Nov 3 11:50:17 GMT 2000