A question on object oriented programming

The basics of how to develop, test, and use models.
Post Reply
ArthurJ

A question on object oriented programming

Post by ArthurJ »

Dear Ted,

During my studies in computer science I've become quite fond of object oriented programming in java. Being interested in computational modelling, I've done an attempt at making my model consist mostly out of classes. However, for some reason it does not exactly do what I want it to do. I hope you can help me correct my (probably obvious) error.

What I want to do is the following:
1. Create an object representing the 'experimental rig'. This class has a bunch of functions to plot graphs, such as voltage, current and frequency.
2. I want to create a cell object, or a network of cells, and give a reference to the network to my experimental rig object.
3. Finally, I want to call a method ShowButtonSet() in object ExperimentalRig, which shows an xpanel with controls that allow me to change the parameters of the network, rerun the experiment with new parameters, and show me plots of the last run simulation.

Here´s how I tried to translate this into code:

File I actually run:

Code: Select all

load_file("nrngui.hoc")
load_file("stdrun.hoc")
nrnmainmenu()			

xopen("TemplatePyramidalCell.hoc")	
xopen("TemplateExperimentalRig.hoc")

objref Rig
Rig = new ExperimentalRig()


//----------------------------------------------------------------------------
//  setup simulation parameters
//----------------------------------------------------------------------------

Dt = .1				// macroscopic time step <<>>
npoints = 300000
trans = 0
dt = 0.1			// must be submultiple of Dt
tstart = trans
tstop = trans + npoints * Dt
runStopAt = tstop
steps_per_ms = 5
celsius = 36
v_init = -65

run()
Rig.ShowButtonSet()


xpanel("RunControl")
	xbutton("Run single experiment", "RunAndUpdate()")
xpanel(400, 800)

proc RunAndUpdate(){
	printf("Run starting")
	run()
	printf("Run done")
	Rig.ShowButtonSet()
	
}

Experimental rig class

Code: Select all

begintemplate ExperimentalRig


public init, ShowButtonSet, PlotVoltageGraph, CurrentCell
public PlotCurrentGraph, PlotVoltageGraph, PlotFrequencyGraph
objref CurrentVector, VoltageVector, FrequencyVector, NetworkConnection, SpikeVector
objref RecurrentConnection, SelfConnection
objref CurrentCell
objref nil, frequencyGraph, currentGraph, voltageGraph
isICAN = 1

proc init() {
	CurrentCell = new PyramidalCell()
	CurrentCell.soma SelfConnection = new NetCon(&CurrentCell.soma.v(.5), nil)
	CurrentCell.soma RecurrentConnection = new NetCon(&CurrentCell.soma.v(.5), nil)
	
	VoltageVector = new Vector()
	CurrentVector = new Vector()
	SpikeVector = new Vector()
	CurrentCell.insertElectrode()
	CurrentVector.record(&CurrentCell.electrode.i)
	VoltageVector.record(&CurrentCell.soma.v(.5))
	SelfConnection.record(SpikeVector)
	access CurrentCell.soma
	
}


proc ShowButtonSet() {
  xpanel("Spike results")
  xbutton("Show voltage", "PlotVoltageGraph()")
  xbutton("Show frequency", "PlotFrequencyGraph()")
  xbutton("Show current", "PlotCurrentGraph()")
  xcheckbox("Ican", &isICAN,  "SetIcan()")
  xslider(&RecurrentConnection.weight, 0, 1, 0, 0)
  
  
  xpanel(100, 800)
}

proc SetIcan(){
	access CurrentCell.soma
	if (isICAN == 0){
		CurrentCell.soma.gbar_iCAN(0.5) = 0
	}
	if (isICAN == 1){
		CurrentCell.soma.gbar_iCAN(0.5) = 1.88e-5
	}
	
		
}

proc PlotCurrentGraph(){
	currentGraph = new Graph()
	currentGraph.size(0,300000,-1,1)
	
	CurrentVector.plot(currentGraph)
	currentGraph.flush()
}

proc PlotVoltageGraph(){
	voltageGraph = new Graph()
	voltageGraph.size(0,300000,-100,50)
	VoltageVector.plot(voltageGraph)
	voltageGraph.flush()
}

proc PlotFrequencyGraph() {
	
	FrequencyVector = new Vector(30)

	if (SpikeVector.size > 0) {
		SpikeVector.div(1000)
		SpikeVector.floor()
		for i=0, SpikeVector.size-1 {
			currentIndex = SpikeVector.x[i]
			FrequencyVector.x[currentIndex] = FrequencyVector.x[currentIndex] + 1
		}
	}
	
	frequencyGraph = new Graph()
	frequencyGraph.size(0, 30, 0, 30)
	FrequencyVector.plot(frequencyGraph)
	frequencyGraph.flush()
}


endtemplate ExperimentalRig
Now, in the first run everything goes fine. I can click and all buttons are functional. However, if I try to rerun the experiment by clicking the RunAndUpdate button, the program freezes. I do get a line in the console saying 'Run starting', but not a 'Run done'. Why?

If possible, is there a way for me to move the RunAndUpdate() procedure into the ExperimentalRig class and make the buttons work without freezing?

Any help is very much appreciated!
ted
Site Admin
Posts: 5784
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: A question on object oriented programming

Post by ted »

You're off to a good start; code looks pretty clean, and I don't see anything egregious, but if there was a problem that jumped out at me, you would have found it already. So some debugging is in order, and here are some suggestions to help you with it.

1. Until everything seems to be working properly, make life easier by using short run times. Anything longer than 100-200 time steps is too long. For this particular case, that means start with run times of 10-20 ms.

2. Avoid burying magic numbers deep in your program. Use symbolic constants instead. For example, rather than
tstop = 300000
declare
TSTOP = whatever number you want near the start of your program, and when the time comes to assign a value to tstop, do it with a statement like
tstop = TSTOP
You might want to do something similar with the axis specifications for your graphs.

3. Follow a cycle of incremental revision and testing. Write only a few lines of code at a time, then check to make sure they work as expected. If they don't,

Code: Select all

repeat
  revise as necessary
  test again
until satisfied that they do work
4. NEURON's hoc interpreter generates pretty good syntax error messages, but run time errors can be very hard to diagnose. As always, localization is the key to diagnosis, and a good way to try to localize problems is by embedding print statements to monitor program execution. You already started doing that; adding a few more might help.

5. Another way to localize problems is by commenting out blocks of code. Use paired /* */ to eliminate swaths of code until you have something that runs properly. Then selectively uncomment bits and pieces until you find the error.


Finally, here are some questions and suggestions that aren't likely to have anything to do with the problem you encountered, but they might help you improve your program.

1. Instead of
access somesection
statements that affect properties of somesection
it is generally better to use section stack syntax e.g.

Code: Select all

somesection {
  statements that affect properties of somesection
}
because it doesn't affect the default section.

2. Vector.plot plots the elements of a Vector vs. the Vector's index values. If you want the x axis to be time, make sure that you also record time to a Vector

Code: Select all

objref tvec
tvec = new Vector()
tvec.record(&t)
so you can then
Vector.plot(graph, tvec)

3. Do you really want to execute ShowButtonSet() more than once?

4. The NetCon.record method captures spike times in a Vector. PlotFrequencyGraph() does not calculate or plot spike frequency.

5. Of these statements
load_file("nrngui.hoc")
load_file("stdrun.hoc")
nrnmainmenu()
the 2nd and 3rd are superfluous. It will be useful to examine nrngui.hoc and stdrun.hoc -- see Secrets of NEURON: the hoc library in the Forum's Hot tips area.
Post Reply