//genesis - analysis_funcs.g /* Functions for network spike analysis This file is intended to be included by a file such as replay_netview.g for replaying and analyzing the result of simulations of two-dimensional rectangular grids of "Ex_cells" and "Inh_cells". It assumes global definitions for these quantities: float tmax // simulation time in sec int Ex_NX, Ex_NY // dimensions of the grid of excitatory cells int Ninputs // Number of input rows in the network float octave_distance = 0.96e-3 // approx 1 mm/octave // used to give integer rows/octave rows_per_octave = {round {octave_distance/Ex_SEP_Y}} and the existence of a disk_in element /Ex_diskin that uses clock 2. A typical usage is: include analysis_funcs.g make_freq_graph 1048 368 make_freqmon // Set up /Ex_diskin messages to spikegens, and PLOT message to freq_graph make_freqmon_messages 0 */ /* Other global variables defined in this script */ float freq_binwidth = 0.010 // default for "binwidth" used in spike freq calcs function freq_overlaytoggle(widget) str widget setfield /freq_form/frequency overlay {getfield {widget} state} setfield /freq_form/inh_frequency overlay {getfield {widget} state} end function make_freq_graph(xpos, ypos) float xpos, ypos str form = "/freq_form" float binwidth = freq_binwidth int row_num = 0 float xmin = 0; float xmax = 1.0; float ymin = 0; float ymax = 25; create xform {form} [{xpos},{ypos},400,380] pushe {form} create xgraph frequency -hgeom 45% -bg white \ -title "Average Spike Frequency" setfield ^ xmin {xmin} xmax {xmax} ymin 0 ymax {ymax} create xgraph inh_frequency -hgeom 40% -bg white \ -title "Average Interneuron Spike Frequency" setfield ^ xmin {xmin} xmax {xmax} ymin 0 ymax {40} create xdialog rownum -wgeom 50% -title "Row number" \ -value {row_num} -script "change_rownum <v>" create xdialog binwidth -wgeom 50% -ygeom 0:inh_frequency -xgeom 0:rownum \ -title "Bin width" -value {binwidth} \ -script "change_binwidth <v>" create xtoggle overlay -wgeom 50% -script "freq_overlaytoggle <widget>" setfield overlay offlabel "Overlay OFF" onlabel "Overlay ON" state 0 create xbutton DISMISS -wgeom 50% -ygeom 0:rownum -xgeom 0:overlay \ -script "xhide "{form} pope makegraphscale {form}/frequency makegraphscale {form}/inh_frequency useclock {form}/frequency 3 // This uses the binwidth to avoid ramping useclock {form}/inh_frequency 3 // This uses the binwidth to avoid ramping xshow {form} end function change_binwidth(binwidth) float binwidth // default "window" for the frequency monitor freq_binwidth = binwidth // set the global value also int Ncells setclock 3 {binwidth} Ncells = {getmsg /frequency_monitor[0]/spikesummer -in -count} float scale_factor = 1.0/(binwidth*Ncells) setfield /frequency_monitor[0]/spikerate gain {scale_factor} Ncells = {getmsg /inh_frequency_monitor/spikesummer -in -count} float scale_factor = 1.0/(binwidth*Ncells) setfield /inh_frequency_monitor/spikerate gain {scale_factor} end /* Note that the GENESIS 2 freq_monitor object cannot be used when there are multiple spike sources. Here, a frequency monitor is created with a calculator object to sum the states of a set of spikegens responding to the soma Vm of each cell in an input row. Binning is done by setting clock 3 to the binwidth and using it for the calculator resetclock. A frequency_monitor is created for each input row. */ function make_freqmon float binwidth = freq_binwidth // default "window" for the frequency monitor int Ncells = Ex_NX*Ex_NY // for a single row of cells float scale_factor = 1.0/(binwidth*Ex_NX) int targ_row // rows receiving thalamic input numbered 1 - Nrows int n // This creates an extra /frequency_monitor[0] for average over all rows for (n = 0; n <= Nrows; n = n + 1) str name = "/frequency_monitor[" @ {n} @ "]" if ({exists {name}}) // Delete any existing frequency monitor delete {name} end create neutral {name} // and make a new one pushe {name} create calculator spikesummer useclock spikesummer 2 // clock used by Ex_diskin and spikecatchers setclock 3 {binwidth} setfield spikesummer resetclock 3 create diffamp spikerate setfield spikerate saturation 10000 gain {scale_factor} addmsg spikesummer spikerate PLUS output pope end // for loop over targ_rows, plus one for all rows // Now fix the normalization for the average over all cells scale_factor = 1.0/(binwidth*Ncells) setfield /frequency_monitor[0]/spikerate gain {scale_factor} end function make_inh_freqmon str name = "/inh_frequency_monitor" float binwidth = freq_binwidth // default "window" for the frequency monitor int Ncells = Ex_NX*Ex_NY float scale_factor = 1.0/(binwidth*Ncells) if ({exists {name}}) // Delete any existing frequency monitor delete {name} end create neutral {name} // and make a new one pushe {name} create calculator spikesummer useclock spikesummer 2 setclock 3 {binwidth} setfield spikesummer resetclock 3 create diffamp spikerate setfield spikerate saturation 10000 gain {scale_factor} addmsg spikesummer spikerate PLUS output pope end /* These functions set up messages from groups of cells to appropriate spike detectors, in order to calculate average spiking frequencies. In these simulations, cells are grouped by horizontal rows in the network. For the Ex_cells, the network rows are numbered from 0 through Ex_NY -1, and the cells are numbered from 0 through Ex_NX*Ex_NY -1. The rows for thalamic input (target rows) are numbered from 1 through Ninputs, corresponding to Ninputs channels of input. In the ACnet simulations, this will be the thalamic output for a tone burst at a particular frequency. The target row is logarithmicaly mapped to vertical distance along the vertical (y) axis to frequency, so that one octave (a doubling of frequency) spans "octave_distance", and rows_per_octave = octave_distance/Ex_SEP_Y. In order to generalize this script to allow for only a single input, but multiple rows to analyze, Nrows is typically used instead of Ninputs, where row_offset = {round {rows_per_octave/3.0}} // To skip top and bottom rows Nrows = Ex_NY - 2*row_offset The neighboring rows also receive a reduced amount of input from the thalamic input channel. For many of the ACnet simulations, these span a vertical distance of 1/3 of an octave, or +/- rows_per_octave/3 rows. The first input row is rows_per_octave/3 rows above cell row 0. */ /* This function is specific to /frequency_monitor[0] and the spikerate plot */ function make_freqmon_messages(rownum) str name = "/frequency_monitor[0]" int rownum, n, offset, maxcell, cellnum if (rownum > Nrows) echo "Row number must be <= " {Nrows} return end if (rownum == 0) // this means all rows that can receive input offset = ({round {rows_per_octave/3.0}})*Ex_NX maxcell = Ex_NX*Ex_NY - 1 - offset else // else just the cells in an input target row offset = (rownum + {round {rows_per_octave/3.0}} - 1)*Ex_NX maxcell = Ex_NX - 1 // up to end of row end pushe {name} for (n = 0; n <= maxcell; n = n +1) create spikegen spikecatcher[{n}] useclock spikecatcher[{n}] 2 setfield spikecatcher[{n}] thresh 0 abs_refract 0.001 cellnum = n + offset addmsg /Ex_diskin spikecatcher[{n}] INPUT val[0][{cellnum}] addmsg spikecatcher[{n}] spikesummer SUM state // state = 1 if spike end pope // Now set up the plot int overlay_no = {getfield /freq_form/frequency overlay_no} int col_num = overlay_no str colorlist = "black red blue cyan green magenta orange" str color // convert col_num to range 1 though 7 col_num = col_num - {trunc {col_num/7.0}}*7 + 1 color = {getarg {arglist {colorlist}} -arg {col_num}} // setfield /freq_form/frequency yoffset 10 addmsg {name}/spikerate /freq_form/frequency PLOT output *frequency *{color} end /* Set up /frequency_monitor[1] through /frequency_monitor[Nrows] */ function make_freq_file int n, targ_row, offset, cellnum str spike_freq_file = "spike_freq_" @ {RUNID} @ ".txt" if ({exists /spike_freq}) call /spike_freq RESET // this closes and reopens the file delete /spike_freq end create asc_file /spike_freq setfield /spike_freq flush 0 leave_open 1 filename {spike_freq_file} setfield /spike_freq append 0 float_format %8.3f // calculate and write total number of lines and columns to a header // t = 0 through tmax - netview_dt, plus header int nlines = {round {tmax/freq_binwidth}} + 1 int ncols = Nrows + 1 // extra column for time call /spike_freq OUT_OPEN call /spike_freq OUT_WRITE {nlines} {ncols} setfield /spike_freq append 1 useclock /spike_freq 3 // This is the binwidth for (targ_row = 1; targ_row <= Nrows; targ_row = targ_row + 1) str name = "/frequency_monitor[" @ {targ_row} @ "]" pushe {name} offset = (targ_row + {round {rows_per_octave/3.0}} - 1)*Ex_NX for (n = 0; n < Ex_NX; n = n +1) // up to end of row create spikegen spikecatcher[{n}] useclock spikecatcher[{n}] 2 setfield spikecatcher[{n}] thresh 0 abs_refract 0.001 cellnum = n + offset addmsg /Ex_diskin spikecatcher[{n}] INPUT val[0][{cellnum}] addmsg spikecatcher[{n}] spikesummer SUM state // 1 if spike end addmsg spikerate /spike_freq SAVE output pope end end function make_inh_freqmon_messages(rownum) str name = "/inh_frequency_monitor" int rownum, n, offset, cellnum, maxcell if (rownum > Nrows) echo "Row number must be <= " {Nrows} return end // MGBv inputs target y-coord range, not a row, and Inh cells have // twice the spacing of Ex cell. Thus the row numbers of Inh inputs // increase at half the rate. if (rownum == 0) // this means all rows that can receive input offset = {round {({round {rows_per_octave/3.0}})*Inh_NX/2.0}} maxcell = Inh_NX*Inh_NY - 1 - offset else // else just the cells in an input target row offset = \ {round {(rownum -1 + {round {rows_per_octave/3.0}})*Inh_NX/2.0}} maxcell = Inh_NX - 1 end pushe {name} for (n = 0; n <= maxcell; n = n +1) create spikegen spikecatcher[{n}] useclock spikecatcher[{n}] 2 setfield spikecatcher[{n}] thresh 0 abs_refract 0.001 cellnum = n + offset addmsg /Inh_diskin spikecatcher[{n}] INPUT val[0][{cellnum}] addmsg spikecatcher[{n}] spikesummer SUM state // state = 1 if spike end pope // Now set up the plot int overlay_no = {getfield /freq_form/inh_frequency overlay_no} int col_num = overlay_no str colorlist = "black red blue cyan green magenta orange" str color // convert col_num to range 1 though 7 col_num = col_num - {trunc {col_num/7.0}}*7 + 1 color = {getarg {arglist {colorlist}} -arg {col_num}} // setfield /freq_form/inh_frequency yoffset 10 addmsg {name}/spikerate /freq_form/inh_frequency PLOT output *frequency *{color} end function change_rownum(rownum) int rownum make_freqmon make_freqmon_messages {rownum} make_inh_freqmon make_inh_freqmon_messages {rownum} change_binwidth {getclock 3} end /* Functions for making and plotting firing rate distribution histogram */ int hbins = 10000 // number of histogram bins float hbinwidth = 0.1 // Hz function make_rate_dist str name = "rate_dist" int n int Ncells = Ex_NX*Ex_NY if ({exists {name}}) // Delete any existing version delete {name} end create neutral {name} // and make a new one pushe {name} // make the table that will hold the binned count of number of cells // having that averge spiking rate create table count_table call count_table TABCREATE {hbins} 0 {hbins*hbinwidth} // xdivs xmin xmax // add the circuitry and messages from /Ex_diskin for (n = 0; n < Ncells; n = n +1) create spikegen spikecatcher[{n}] useclock spikecatcher[{n}] 2 setfield spikecatcher[{n}] thresh 0 abs_refract 0.001 addmsg /Ex_diskin spikecatcher[{n}] INPUT val[0][{n}] create calculator spikecatcher[{n}]/spikecounter useclock spikecatcher[{n}]/spikecounter 2 // For some reason I wanted to reset after a period longer than tmax // setclock 4 {tmax} setclock 4 1000 setfield spikecatcher[{n}]/spikecounter resetclock 4 addmsg spikecatcher[{n}] spikecatcher[{n}]/spikecounter \ SUM state // state = 1 if spike end pope end //========================================================== // Functions to calculate spike times for raster plots //========================================================== function make_spike_data_tables(Nplots, tmax, tablename) float tmax int Nplots int Npoints = {round {tmax/0.001}} // estimate max number of APs int i int xdivs = Npoints float xmin = 0; float xmax = {tmax} // this is arbitrary for (i = 0; i < {Nplots}; i = i + 1) if ({exists {tablename}[{i}]}) // Get rid of any existing one with old xdivs delete {tablename}[{i}] end create table {tablename}[{i}] call {tablename}[{i}] TABCREATE {xdivs} {xmin} {xmax} setfield {tablename}[{i}] step_mode 4 stepsize 0.0 end // useclock {tablename} 1 end function make_spike_time_file(Nplots,filename, tablename) str filename, tablename int i,j, ntimes, Nplots float spike_time openfile {filename} w for (i = 0; i < {Nplots}; i = i + 1) ntimes = {getfield {tablename}[{i}] output} for (j=0; j < {ntimes}; j = j + 1) spike_time = {getfield {tablename}[{i}] table->table[{j}]} // write it to the file writefile {filename} {spike_time} " " -n end writefile {filename} // put a final newline end // i = 0; i < {Nplots} closefile {filename} end /* Specialized function to set up the messages from the cells for which spike times will be recorded. Calculate spike times for n_per_row equally spaced cells on each row from row_min through row_max. Rows are numbered from 0 to Ex_Ny-1. The Vm message source will have to be customized */ function make_rasterplot_msgs(row_min, row_max, n_per_row, Vm_src, dest_table) int i, j, cell_num, row_min, row_max, n_per_row str Vm_src, dest_table // spacing between cell numbers when n_per_row are plotted int spacing = {trunc {Ex_NX/(n_per_row + 1)}} int num_plots = 0 for (i = {row_min}; i <= {row_max}; i = i + 1) if (n_per_row <= 1) // Just plot the middle cell cell_num = i*Ex_NX + spacing addmsg {Vm_src} {dest_table}[{i-row_min}] INPUT val[0][{cell_num}] else for (j = 1; j <= {n_per_row}; j = j + 1) cell_num = i*Ex_NX + j*spacing addmsg {Vm_src} {dest_table}[{num_plots}] INPUT val[0][{cell_num}] num_plots = num_plots + 1 end end // if (n_per_row <= 1) end // for (i = {row_min}; i <= {row_max};) end /* Wrapper functions using the above, called from main program */ function setup_rasterplot(n_per_row) // assume all target rows // Number of cells per row to plot int n_per_row // Target rows for MGBv inputs are numbered 1 through Nrows int targ_min = 1; int targ_max = {Nrows} int row_min = targ_min + {round {rows_per_octave/3.0}} int row_max = targ_max + {round {rows_per_octave/3.0}} int Nplots = (targ_max - targ_min + 1)*n_per_row make_spike_data_tables {Nplots} {tmax} {spikedata} make_rasterplot_msgs {row_min} {row_max} {n_per_row} /Ex_diskin {spikedata} end /* This would be for use without the control panel. Currently, make_spike_time_file is called with proper args via the button "Write spike times to file" and -script do_write_rasterfile */ function write_rasterfile(n_per_row) // assume all target rows int n_per_row int Nplots = n_per_row*Nrows str spike_time_file = "spike_times_" @ {RUNID} @ ".txt" make_spike_time_file {Nplots} {spike_time_file} {spikedata} if (debug) echo "Wrote spike time data file "{spike_time_file} end end //========================================================== // Functions to create the GUI for the spike analysis //========================================================== function histoverlay(widget) str widget setfield /histform/##[TYPE=xgraph] overlay {getfield {widget} state} end function make_hist // for display of spike freq distribution float hgraph_xmax = {hbins*hbinwidth} hgraph_xmax = 100 // hack to lower plot range str formpath = "/histform" float ymax = 100 create xform {formpath} [270,430,700,300] create xgraph {formpath}/hgraph [0,0%,100%,90%] -ymax 100 -bg ivory setfield {formpath}/hgraph XUnits "interval" YUnits count \ xmax {hgraph_xmax} title "Firing rate distribution" create xbutton {formpath}/display -title "Display Bins" [0,90%,25%,25] \ -script "xhide "{formpath} -script display_bins create xbutton {formpath}/clear -title "Clear Bins" [25%,90%,25%,25] \ -script "xhide "{formpath} -script clearbins create xbutton {formpath}/DISMISS [50%,90%,25%,25] \ -script "xhide "{formpath} create xtoggle {formpath}/overlay [75%,90%,25%,25] -script "histoverlay <w>" setfield {formpath}/overlay offlabel "Overlay OFF" \ onlabel "Overlay ON" state 0 makegraphscale {formpath}/hgraph end /* plot a bar on a histogram */ function plotbar(xplotpath, x, y, dx) str xplotpath float x, y, dx call {xplotpath} ADDPTS {x-dx/2} 0 call {xplotpath} ADDPTS {x-dx/2} {y} call {xplotpath} ADDPTS {x+dx/2} {y} call {xplotpath} ADDPTS {x+dx/2} 0 end function del_hist(formpath) str formpath, elm if({getfield {formpath}/overlay state} == 0) foreach elm ({el {formpath}/##[ISA=xplot]}) delete {elm} end else // This is getting much less general move /histform/hgraph/Count /histform/hgraph/Count#0.0 setfield /histform/hgraph/Count#0.0 fg orange end call /histform/hgraph RESET end /* post-run analysis */ function display_bins str dataform = "/histform" str hgraphpath = "/histform/hgraph" str hplot = {dataform}@"/hgraph/Count" int Ncells = Ex_NX*Ex_NY del_hist /histform if (!{exists {hplot}}) create xplot {hplot} end // setfield {hplot} linewidth 2 int i, n, index float x, y, ymax, count call {hgraphpath} RESET // loop through the spikecatcher[n]/spikecounter output fields to get // the spike count for that cell, normalize to spike freq, and add 1 // to the appropriate bin of /rate_dist/count_table. for (n = 0; n < Ncells; n = n + 1) count = {getfield /rate_dist/spikecatcher[{n}]/spikecounter output} index = {round {(count/tmax)/hbinwidth}} setfield /rate_dist/count_table table->table[{index}] \ {{getfield /rate_dist/count_table table->table[{index}]} + 1} end // plot histogram ymax = 0.0 for (i=0; i < hbins; i=i+1) x = (i + 0.5)*hbinwidth y = {getfield /rate_dist/count_table table->table[{i}]} ymax = {max {y} {ymax}} plotbar {hplot} {x} {y} {hbinwidth} end setfield {hgraphpath} ymax {ymax + 1} setfield {hgraphpath} xmax {(hbins + 1)*hbinwidth} // hack to reduce range of histogram // setfield {hgraphpath} xmax 50 xshow {dataform} end /* Functions for ISI histogram and spike analysis */ function clearbins int i for (i=0; i < hbins; i=i+1) setfield /rate_dist/count_table table->table[{i}] 0 end end