Package topo :: Package command :: Module pylabplots
[hide private]
[frames] | no frames]

Source Code for Module topo.command.pylabplots

   1  """ 
   2  Line-based and matrix-based plotting commands using MatPlotLib. 
   3   
   4  Before importing this file, you will probably want to do something 
   5  like: 
   6   
   7    from matplotlib import rcParams 
   8    rcParams['backend']='TkAgg' 
   9   
  10  to select a backend, or else select an appropriate one in your 
  11  matplotlib.rc file (if any).  There are many backends available for 
  12  different GUI or non-GUI uses. 
  13   
  14  $Id: pylabplots.py 10921 2010-02-16 18:46:32Z antolikjan $ 
  15  """ 
  16  __version__='$Revision: 10921 $' 
  17   
  18  import re 
  19  import os 
  20  import copy 
  21  import errno 
  22   
  23  try: 
  24      import matplotlib 
  25      import pylab 
  26  except ImportError: 
  27      print "Warning: Could not import matplotlib; pylab plots will not work." 
  28   
  29  import numpy 
  30  from math import pi 
  31  # JABALERT: Import all of these from numpy instead? 
  32  from numpy.oldnumeric import arange, sqrt, array, floor, transpose, argmax, argmin, cos, sin, log10, Float 
  33  from numpy import outer,arange,ones,zeros 
  34   
  35  from numpy.fft.fftpack import fft2 
  36  from numpy.fft.helper import fftshift 
  37  from numpy import abs 
  38   
  39  import topo 
  40  from topo.base.sheetview import SheetView 
  41  from topo.base.arrayutil import octave_output, centroid, wrap 
  42  from topo.base.sheet import Sheet 
  43  from topo.base.arrayutil import wrap 
  44  from topo.misc.filepath import normalize_path 
  45  from topo.misc.util import frange 
  46  import topo.analysis.vision 
  47  from topo.plotting.plot import make_template_plot, Plot 
  48  import param 
  49  from param import ParameterizedFunction 
  50  from param.parameterized import ParamOverrides 
  51  from topo.pattern.basic import SineGrating, RawRectangle, OrientationContrast 
  52  from topo.plotting.plotgroup import create_plotgroup, plotgroups 
  53  from topo.base.cf import CFSheet 
  54   
  55  from topo.analysis.featureresponses import Feature, PatternPresenter, FeatureCurves 
  56  from topo.analysis.featureresponses import SinusoidalMeasureResponseCommand, PositionMeasurementCommand, SingleInputResponseCommand, FeatureCurveCommand, UnitCurveCommand 
  57   
  58   
  59   
60 -class PylabPlotCommand(ParameterizedFunction):
61 """Parameterized command for plotting using Matplotlib/Pylab.""" 62 63 file_dpi = param.Number( 64 default=100.0,bounds=(0,None),softbounds=(0,1000),doc=""" 65 Default DPI when rendering to a bitmap. 66 The nominal size * the dpi gives the final image size in pixels. 67 E.g.: 4"x4" image * 80 dpi ==> 320x320 pixel image.""") 68 69 file_format = param.String(default="png",doc=""" 70 Which image format to use when saving images. 71 The output can be png, ps, pdf, svg, or any other format 72 supported by Matplotlib.""") 73 74 # JABALERT: Should replace this with a filename_format and 75 # associated parameters, as in PlotGroupSaver. 76 # Also should probably allow interactive display to be controlled 77 # separately from the filename, to make things work more similarly 78 # with and without a GUI. 79 filename = param.String(default=None,doc=""" 80 Optional base of the filename to use when saving images; 81 if None the plot will be displayed interactively. 82 83 The actual name is constructed from the filename base plus the 84 suffix plus the current simulator time plus the file_format.""") 85 86 filename_suffix = param.String(default="",doc=""" 87 Optional suffix to be used for disambiguation of the filename.""") 88 89 title = param.String(default=None,doc=""" 90 Optional title to be used when displaying the plot interactively.""") 91 92 __abstract = True 93 94
95 - def _set_windowtitle(self,title):
96 """ 97 Helper function to set the title (if not None) of this PyLab plot window. 98 """ 99 100 # At the moment, PyLab does not offer a window-manager-independent 101 # means for controlling the window title, so what we do is to try 102 # what should work with Tkinter, and then suppress all errors. That 103 # way we should be ok when rendering to a file-based backend, but 104 # will get nice titles in Tk windows. If other toolkits are in use, 105 # the title can be set here using a similar try/except mechanism, or 106 # else there can be a switch based on the backend type. 107 if title is not None: 108 try: 109 manager = pylab.get_current_fig_manager() 110 manager.window.title(title) 111 except: 112 pass
113 114
115 - def _generate_figure(self,p):
116 """ 117 Helper function to display a figure on screen or save to a file. 118 119 p should be a ParamOverrides instance containing the current 120 set of parameters. 121 """ 122 123 pylab.show._needmain=False 124 if p.filename is not None: 125 # JABALERT: need to reformat this as for other plots 126 fullname=p.filename+p.filename_suffix+str(topo.sim.time())+"."+p.file_format 127 pylab.savefig(normalize_path(fullname), dpi=p.file_dpi) 128 else: 129 self._set_windowtitle(p.title) 130 pylab.show()
131 132 133
134 -class vectorplot(PylabPlotCommand):
135 """ 136 Simple line plotting for any vector or list of numbers. 137 138 Intended for interactive debugging or analyzing from the command 139 prompt. See MatPlotLib's pylab functions to create more elaborate 140 or customized plots; this is just a simple example. 141 142 An optional string can be supplied as a title for the figure, if 143 desired. At present, this is only used for the window, not the 144 actual body of the figure (and will thus not appear when the 145 figure is saved). 146 147 The style argument allows different line/linespoints style for 148 the plot: 'r-' for red solid line, 'bx' for blue x-marks, etc. 149 See http://matplotlib.sourceforge.net/matplotlib.pylab.html#-plot 150 for more possibilities. 151 152 The label argument can be used to identify the line in a figure legend. 153 154 Ordinarily, the x value for each point on the line is the index of 155 that point in the vec array, but a explicit list of xvalues can be 156 supplied; it should be the same length as vec. 157 158 Execution of multiple vectorplot() commands with different styles 159 will result in all those styles overlaid on a single plot window. 160 """ 161 162 # JABALERT: All but the first two arguments should probably be Parameters
163 - def __call__(self,vec,xvalues=None,style='-',label=None,**params):
164 p=ParamOverrides(self,params) 165 166 if xvalues is not None: 167 pylab.plot(xvalues, vec, style, label=label) 168 else: 169 pylab.plot(vec, style, label=label) 170 171 pylab.grid(True) 172 self._generate_figure(p)
173 174 175
176 -class matrixplot(PylabPlotCommand):
177 """ 178 Simple plotting for any matrix as a bitmap with axes. 179 180 Like MatLab's imagesc, scales the values to fit in the range 0 to 1.0. 181 Intended for interactive debugging or analyzing from the command 182 prompt. See MatPlotLib's pylab functions to create more elaborate 183 or customized plots; this is just a simple example. 184 """ 185 186 plot_type = param.Callable(default=pylab.gray,doc=""" 187 Matplotlib command to generate the plot, e.g. pylab.gray or pylab.hsv.""") 188 189 # JABALERT: All but the first two should probably be Parameters
190 - def __call__(self,mat,aspect=None,colorbar=True,**params):
191 p=ParamOverrides(self,params) 192 193 p.plot_type() 194 pylab.figure(figsize=(5,5)) 195 pylab.imshow(mat,interpolation='nearest',aspect=aspect) 196 if colorbar and (mat.min()!= mat.max()): pylab.colorbar() 197 self._generate_figure(p)
198 199
200 -class matrixplot3d(PylabPlotCommand):
201 """ 202 Simple plotting for any matrix as a 3D wireframe with axes. 203 204 Uses Matplotlib's beta-quality features for 3D plotting. These 205 usually work fine for wireframe plots, although they don't always 206 format the axis labels properly, and do not support removal of 207 hidden lines. Note that often the plot can be rotated within the 208 window to make such problems go away, and then the best result can 209 be saved if needed. 210 211 Other than the default "wireframe", the type can be "contour" to 212 get a contour plot, or "surface" to get a solid surface plot, but 213 surface plots currently fail in many cases, e.g. for small 214 matrices. 215 216 If you have trouble, you can try matrixplot3d_gnuplot instead. 217 """ 218 219 # JABALERT: All but the first two arguments should probably be Parameters
220 - def __call__(self,mat,type="wireframe",**params):
221 p=ParamOverrides(self,params) 222 223 from mpl_toolkits.mplot3d import axes3d 224 225 fig = pylab.figure() 226 ax = axes3d.Axes3D(fig) 227 228 # Construct matrices for r and c values 229 rn,cn = mat.shape 230 c = outer(ones(rn),arange(cn*1.0)) 231 r = outer(arange(rn*1.0),ones(cn)) 232 233 if type=="wireframe": 234 ax.plot_wireframe(r,c,mat) 235 elif type=="surface": 236 # Sometimes fails for no obvious reason 237 ax.plot_surface(r,c,mat) 238 elif type=="contour": 239 # Works but not usually very useful 240 ax.contour3D(r,c,mat) 241 else: 242 raise ValueError("Unknown plot type "+str(type)) 243 244 ax.set_xlabel('R') 245 ax.set_ylabel('C') 246 ax.set_zlabel('Value') 247 248 self._generate_figure(p)
249 250 251
252 -def matrixplot3d_gnuplot(mat,title=None,outputfilename="tmp.ps"):
253 """ 254 Simple plotting for any matrix as a 3D surface with axes. 255 256 Currently requires the gnuplot-py package to be installed, plus 257 the external gnuplot program; likely to be removed once Matplotlib 258 supports 3D plots better. 259 260 Unlikely to work on non-UNIX systems. 261 262 Should return when it completes, but for some reason the Topographica 263 prompt is not available until this command finishes. 264 """ 265 import Gnuplot 266 import Numeric # ERRORALERT 267 from os import system 268 269 psviewer="gv" # Should be a parameter, or handled better somehow 270 g = Gnuplot.Gnuplot(debug=0) #debug=1: output commands to stderr 271 r,c = mat.shape 272 x = arange(r*1.0) 273 y = arange(c*1.0) 274 # The .tolist() command is necessary to avoid bug in gnuplot-py, 275 # which will otherwise convert a 2D float array into integers (!) 276 m = numpy.asarray(mat,dtype="float32").tolist() 277 #g("set parametric") 278 g("set data style lines") 279 g("set hidden3d") 280 g("set xlabel 'R'") 281 g("set ylabel 'C'") 282 g("set zlabel 'Value'") 283 if title: g.title(title) 284 285 if outputfilename: 286 g("set terminal postscript eps color solid 'Times-Roman' 14") 287 g("set output '"+outputfilename+"'") 288 g.splot(Gnuplot.GridData(m,x,y, binary=1)) 289 #g.hardcopy(outputfilename, enhanced=1, color=1) 290 system(psviewer+" "+outputfilename+" &") 291 292 else: 293 g.splot(Gnuplot.GridData(m,x,y, binary=1)) 294 raw_input('Please press return to continue...\n')
295 296 297
298 -class histogramplot(PylabPlotCommand):
299 """ 300 Compute and plot the histogram of the supplied data. 301 302 See help(pylab.hist) for help on the histogram function itself. 303 304 If given, colors is an iterable collection of matplotlib.colors 305 (see help (matplotlib.colors) ) specifying the bar colors. 306 307 Example use: 308 histogramplot([1,1,1,2,2,3,4,5],title='hist',colors='rgb',bins=3,normed=1) 309 """ 310 311 # JABALERT: All but the first two arguments should probably be Parameters
312 - def __call__(self,data,colors=None,**params):
313 p=ParamOverrides(self,params,allow_extra_keywords=True) 314 315 pylab.figure(figsize=(4,2)) 316 n,bins,bars = pylab.hist(data,**(p.extra_keywords())) 317 318 # if len(bars)!=len(colors), any extra bars won't have their 319 # colors changed, or any extra colors will be ignored. 320 if colors: [bar.set_fc(color) for bar,color in zip(bars,colors)] 321 322 self._generate_figure(p)
323 324 325
326 -class gradientplot(matrixplot):
327 """ 328 Compute and show the gradient plot of the supplied data. 329 Translated from Octave code originally written by Yoonsuck Choe. 330 331 If the data is specified to be cyclic, negative differences will 332 be wrapped into the range specified (1.0 by default). 333 """ 334 335 # JABALERT: All but the first two arguments should probably be Parameters
336 - def __call__(self,data,cyclic=True,cyclic_range=1.0,**params):
337 p=ParamOverrides(self,params) 338 339 r,c = data.shape 340 dx = numpy.diff(data,1,axis=1)[0:r-1,0:c-1] 341 dy = numpy.diff(data,1,axis=0)[0:r-1,0:c-1] 342 343 if cyclic: # Wrap into the specified range 344 # Convert negative differences to an equivalent positive value 345 dx = wrap(0,cyclic_range,dx) 346 dy = wrap(0,cyclic_range,dy) 347 # 348 # Make it increase as gradient reaches the halfway point, 349 # and decrease from there 350 dx = 0.5*cyclic_range-abs(dx-0.5*cyclic_range) 351 dy = 0.5*cyclic_range-abs(dy-0.5*cyclic_range) 352 353 super(gradientplot,self).__call__(sqrt(dx*dx+dy*dy),**p)
354 355 356
357 -class fftplot(matrixplot):
358 """ 359 Compute and show the 2D Fast Fourier Transform (FFT) of the supplied data. 360 361 Example:: fftplot(topo.sim["V1"].sheet_views["OrientationPreference"].view()[0],filename="out") 362 """ 363
364 - def __call__(self,data,**params):
365 p=ParamOverrides(self,params) 366 fft_plot=1-abs(fftshift(fft2(data-0.5, s=None, axes=(-2,-1)))) 367 super(fftplot,self).__call__(fft_plot,**p)
368 369 370
371 -class activityplot(PylabPlotCommand):
372 """ 373 Plots the activity in a sheet. 374 375 Gets plot's extent from sheet.bounds.aarect(). Adds a title and 376 allows the selection of a colormap. If activity is not given, 377 the sheet's current activity is used. 378 """ 379 380 # JABALERT: All but the first two arguments should probably be Parameters 381 # Not sure what this command is for or if anyone is using it.
382 - def __call__(self,sheet,activity=None,cmap=None,**params):
383 p=ParamOverrides(self,params) 384 385 l,b,r,t = sheet.bounds.aarect().lbrt() 386 if activity is None: 387 activity = sheet.activity 388 if cmap is None: 389 cmap=pylab.cm.Greys 390 pylab.imshow(activity, extent=(l,r,b,t),cmap=cmap) 391 392 self._generate_figure(p)
393 394 395
396 -class topographic_grid(PylabPlotCommand):
397 """ 398 By default, plot the XPreference and YPreference preferences for all 399 Sheets for which they are defined, using MatPlotLib. 400 401 If sheet_views other than XPreference and YPreference are desired, 402 the names of these can be passed in as arguments. 403 """ 404 405 xsheet_view_name = param.String(default='XPreference',doc=""" 406 Name of the SheetView holding the X position locations.""") 407 408 ysheet_view_name = param.String(default='YPreference',doc=""" 409 Name of the SheetView holding the Y position locations.""") 410 411 axis = param.Parameter(default=[-0.5,0.5,-0.5,0.5],doc=""" 412 Four-element list of the plot bounds, i.e. [xmin, xmax, ymin, ymax].""") 413
414 - def __call__(self,**params):
415 p=ParamOverrides(self,params) 416 417 for sheet in topo.sim.objects(Sheet).values(): 418 if ((p.xsheet_view_name in sheet.sheet_views) and 419 (p.ysheet_view_name in sheet.sheet_views)): 420 421 x = sheet.sheet_views[p.xsheet_view_name].view()[0] 422 y = sheet.sheet_views[p.ysheet_view_name].view()[0] 423 424 pylab.figure(figsize=(5,5)) 425 426 # This one-liner works in Octave, but in matplotlib it 427 # results in lines that are all connected across rows and columns, 428 # so here we plot each line separately: 429 # pylab.plot(x,y,"k-",transpose(x),transpose(y),"k-") 430 # Here, the "k-" means plot in black using solid lines; 431 # see matplotlib for more info. 432 isint=pylab.isinteractive() # Temporarily make non-interactive for plotting 433 pylab.ioff() 434 for r,c in zip(y,x): 435 pylab.plot(c,r,"k-") 436 for r,c in zip(transpose(y),transpose(x)): 437 pylab.plot(c,r,"k-") 438 439 pylab.xlabel('x') 440 pylab.ylabel('y') 441 # Currently sets the input range arbitrarily; should presumably figure out 442 # what the actual possible range is for this simulation (which would presumably 443 # be the maximum size of any GeneratorSheet?). 444 pylab.axis(p.axis) 445 p.title='Topographic mapping to '+sheet.name+' at time '+topo.sim.timestr() 446 447 if isint: pylab.ion() 448 p.filename_suffix="_"+sheet.name 449 self._generate_figure(p)
450 451
452 -class overlaid_plots(PylabPlotCommand):
453 """ 454 Use matplotlib to make a plot combining a bitmap and line-based overlays. 455 """ 456 457 plot_template = param.List(default=[{'Hue':'OrientationPreference'}],doc=""" 458 Template for the underlying bitmap plot.""") 459 460 overlay = param.List(default=[('contours','OcularPreference',0.5,'black'), 461 ('arrows','DirectionPreference','DirectionSelectivity','white')],doc=""" 462 List of overlaid plots, where each list item may be a 4-tuple 463 specifying either a contour line or a field of arrows:: 464 465 ('contours',map-name,contour-value,line-color) 466 467 ('arrows',arrow-location-map-name,arrow-size-map-name,arrow-color) 468 469 Any number or combination of contours and arrows may be supplied.""") 470 471 normalize = param.Boolean(default='Individually',doc=""" 472 Type of normalization, if any, to use. Options include 'None', 473 'Individually', and 'AllTogether'. See 474 topo.plotting.plotgroup.TemplatePlotGroup.normalize for more 475 details.""") 476 477
478 - def __call__(self,**params):
479 p=ParamOverrides(self,params) 480 481 for template in p.plot_template: 482 483 for sheet in topo.sim.objects(Sheet).values(): 484 name=template.keys().pop(0) 485 plot=make_template_plot(template,sheet.sheet_views,sheet.xdensity,sheet.bounds,p.normalize,name=template[name]) 486 if plot: 487 bitmap=plot.bitmap 488 pylab.figure(figsize=(5,5)) 489 isint=pylab.isinteractive() # Temporarily make non-interactive for plotting 490 pylab.ioff() # Turn interactive mode off 491 492 pylab.imshow(bitmap.image,origin='lower',interpolation='nearest') 493 pylab.axis('off') 494 495 for (t,pref,sel,c) in p.overlay: 496 v = pylab.flipud(sheet.sheet_views[pref].view()[0]) 497 498 if (t=='contours'): 499 pylab.contour(v,[sel,sel],colors=c,linewidths=2) 500 501 if (t=='arrows'): 502 s = pylab.flipud(sheet.sheet_views[sel].view()[0]) 503 scale=int(pylab.ceil(log10(len(v)))) 504 X=pylab.array([x for x in xrange(len(v)/scale)]) 505 v_sc=pylab.zeros((len(v)/scale,len(v)/scale)) 506 s_sc=pylab.zeros((len(v)/scale,len(v)/scale)) 507 for i in X: 508 for j in X: 509 v_sc[i][j]=v[scale*i][scale*j] 510 s_sc[i][j]=s[scale*i][scale*j] 511 pylab.quiver(scale*X,scale*X,-cos(2*pi*v_sc)*s_sc,-sin(2*pi*v_sc)*s_sc,color=c,edgecolors=c,minshaft=3,linewidths=1) 512 513 p.title='%s overlaid with %s at time %s' %(plot.name,pref,topo.sim.timestr()) 514 if isint: pylab.ion() 515 p.filename_suffix="_"+sheet.name 516 self._generate_figure(p)
517 518 519
520 -class tuning_curve(PylabPlotCommand):
521 """ 522 Plot a tuning curve for a feature, such as orientation, contrast, or size. 523 524 The curve datapoints are collected from the curve_dict for 525 the units at the specified coordinates in the specified sheet 526 (where the units and sheet may be set by a GUI, using 527 topo.analysis.featureresponses.UnitCurveCommand.sheet and 528 topo.analysis.featureresponses.UnitCurveCommand.coords, 529 or by hand). 530 """ 531 532 coords = param.List(default=[(0,0)],doc=""" 533 List of coordinates of units to measure.""") 534 535 sheet = param.ObjectSelector( 536 default=None,doc=""" 537 Name of the sheet to use in measurements.""") 538 539 x_axis = param.String(default="",doc=""" 540 Feature to plot on the x axis of the tuning curve""") 541 542 # Can we list some alternatives here, if there are any 543 # useful ones? 544 plot_type = param.Callable(default=pylab.plot,doc=""" 545 Matplotlib command to generate the plot.""") 546 547 unit = param.String(default="",doc=""" 548 String to use in labels to specify the units in which curves are plotted.""") 549 550 __abstract = True 551 552
553 - def _format_x_tick_label(self,x):
554 return "%3.1f" % x
555
556 - def _rotate(self, seq, n=1):
557 n = n % len(seq) # n=hop interval 558 return seq[n:] + seq[:n]
559
560 - def _curve_values(self, i_value, j_value, curve):
561 """Return the x, y, and x ticks values for the specified curve from the curve_dict""" 562 x_values=sorted(curve.keys()) 563 y_values=[curve[key].view()[0][i_value,j_value] for key in x_values] 564 return x_values,y_values,x_values
565
566 - def _reduce_ticks(self,ticks):
567 x = []; 568 y= []; 569 num_ticks = 5; 570 y.append(ticks[0]) 571 x.append(0) 572 for i in xrange(0,num_ticks): 573 y.append(y[-1]+numpy.pi/(num_ticks+1)); 574 x.append(x[-1]+numpy.pi/(num_ticks+1)); 575 y.append(y[-1]+numpy.pi/(num_ticks+1)); 576 x.append(3.14) 577 return (x,y)
578 579
580 - def __call__(self,**params):
581 p=ParamOverrides(self,params) 582 sheet = p.sheet 583 for coordinate in p.coords: 584 i_value,j_value=sheet.sheet2matrixidx(coordinate[0],coordinate[1]) 585 586 f = pylab.figure(figsize=(7,7)) 587 isint=pylab.isinteractive() 588 pylab.ioff() 589 590 pylab.ylabel('Response',fontsize='large') 591 pylab.xlabel('%s (%s)' % (p.x_axis.capitalize(),p.unit),fontsize='large') 592 pylab.title('Sheet %s, coordinate(x,y)=(%0.3f,%0.3f) at time %s' % 593 (sheet.name,coordinate[0],coordinate[1],topo.sim.timestr())) 594 p.title='%s: %s Tuning Curve' % (topo.sim.name,p.x_axis.capitalize()) 595 596 self.first_curve=True 597 for curve_label in sorted(sheet.curve_dict[p.x_axis].keys()): 598 x_values,y_values,ticks=self._curve_values(i_value,j_value,sheet.curve_dict[p.x_axis][curve_label]) 599 600 x_tick_values,ticks = self._reduce_ticks(ticks) 601 labels = [self._format_x_tick_label(x) for x in ticks] 602 pylab.xticks(x_tick_values, labels,fontsize='large') 603 pylab.yticks(fontsize='large') 604 p.plot_type(x_values, y_values, label=curve_label,lw=3.0) 605 self.first_curve=False 606 607 if isint: pylab.ion() 608 pylab.legend(loc=2) 609 self._generate_figure(p)
610 611 612
613 -class cyclic_tuning_curve(tuning_curve):
614 """ 615 Same as tuning_curve, but rotates the curve so that minimum y 616 values are at the minimum x value to make the plots easier to 617 interpret. Such rotation is valid only for periodic quantities 618 like orientation or direction, and only if the correct period 619 is set. 620 621 At present, the y_values and labels are rotated by an amount 622 determined by the minmum y_value for the first curve plotted 623 (usually the lowest contrast curve). 624 """ 625 626 cyclic_range = param.Number(default=pi,bounds=(0,None),softbounds=(0,10),doc=""" 627 Range of the cyclic quantity (e.g. pi for the orientation of 628 a symmetric stimulus, or 2*pi for motion direction or the 629 orientation of a non-symmetric stimulus).""") 630 631 unit = param.String(default="degrees",doc=""" 632 String to use in labels to specify the units in which curves are plotted.""") 633 634 635 # This implementation should work for quantities periodic with 636 # some multiple of pi that we want to express in degrees, but it 637 # will need to be reimplemented in a subclass to work with other 638 # cyclic quantities.
639 - def _format_x_tick_label(self,x):
640 return str(int(180*x/pi))
641 642
643 - def _curve_values(self, i_value, j_value, curve):
644 """ 645 Return the x, y, and x ticks values for the specified curve from the curve_dict. 646 647 With the current implementation, there may be cases (i.e., 648 when the lowest contrast curve gives a lot of zero y_values) 649 in which the maximum is not in the center. This may 650 eventually be changed so that the preferred orientation is in 651 the center. 652 """ 653 if self.first_curve==True: 654 x_values= sorted(curve.keys()) 655 y_values=[curve[key].view()[0][i_value,j_value] for key in x_values] 656 657 min_arg=argmin(y_values) 658 x_min=x_values[min_arg] 659 y_min=y_values[min_arg] 660 y_values=self._rotate(y_values, n=min_arg) 661 self.ticks=self._rotate(x_values, n=min_arg) 662 self.ticks+=[x_min] 663 x_max=min(x_values)+self.cyclic_range 664 x_values.append(x_max) 665 y_values.append(y_min) 666 667 self.x_values=x_values 668 else: 669 y_values=[curve[key].view()[0][i_value,j_value] for key in self.ticks] 670 671 return self.x_values,y_values,self.ticks
672 673 674
675 -def plot_cfproj_mapping(dest,proj='Afferent',style='b-'):
676 """ 677 Given a CF sheet receiving a CFProjection, plot 678 the mapping of the dests CF centers on the src sheet. 679 """ 680 if isinstance(dest,str): 681 from topo import sim 682 dest = sim[dest] 683 plot_coord_mapping(dest.projections()[proj].coord_mapper, 684 dest,style=style)
685 686 687 # JABALERT: not sure whether this is currently used
688 -def plot_coord_mapping(mapper,sheet,style='b-'):
689 """ 690 Plot a coordinate mapping for a sheet. 691 692 Given a CoordinateMapperFn (as for a CFProjection) and a sheet 693 of the projection, plot a grid showing where the sheet's units 694 are mapped. 695 """ 696 697 from pylab import plot,hold,ishold 698 699 xs = sheet.sheet_rows() 700 ys = sheet.sheet_cols() 701 702 hold_on = ishold() 703 if not hold_on: 704 plot() 705 hold(True) 706 707 for y in ys: 708 pts = [mapper(x,y) for x in xs] 709 plot([u for u,v in pts], 710 [v for u,v in pts], 711 style) 712 713 for x in xs: 714 pts = [mapper(x,y) for y in ys] 715 plot([u for u,v in pts], 716 [v for u,v in pts], 717 style) 718 719 hold(hold_on)
720 721 722 # JABALERT: Untested as of Mon Nov 10 12:59:54 GMT 2008
723 -class plot_tracked_attributes(PylabPlotCommand):
724 """ 725 Plots parameter values associated with an AttributeTrackingTF. 726 Example call: 727 VT=AttributeTrackingTF(function=HE, debug_params=['a', 'b',], units=[(0,0),(1,1)], step=1) 728 plot_tracked_attributes(VT,0,10000,attrib_names=['a'],units=[(0,0)], filename='V1') 729 """ 730 731 # JABALERT: These parameters need to be documented. 732 raw = param.Boolean(default=False) 733 734 attrib_names = param.List(default=[]) 735 736 ylabel = param.String(default="") 737 738 # Should be renamed to coords to match other commands 739 units = param.List(default=[]) 740 741 ybounds = param.Parameter(default=(None,None)) 742 743 744 # JABALERT: All but the first two arguments should probably be Parameters
745 - def __call__(self,output_fn,init_time=0,final_time=None,**params):
746 p=ParamOverrides(self,params) 747 748 if final_time is None: 749 final_time=topo.sim.time() 750 751 attrs = p.attrib_names if len(p.attrib_names)>0 else output_fn.attrib_names 752 for a in attrs: 753 pylab.figure(figsize=(6,4)) 754 isint=pylab.isinteractive() 755 pylab.ioff() 756 pylab.grid(True) 757 ylabel=p.ylabel 758 pylab.ylabel(a+" "+ylabel) 759 pylab.xlabel('Iteration Number') 760 761 coords = p.units if len(p.units)>0 else output_fn.units 762 for coord in coords: 763 y_data=[y for (x,y) in output_fn.values[a][coord]] 764 x_data=[x for (x,y) in output_fn.values[a][coord]] 765 if p.raw==True: 766 plot_data=zip(x_data,y_data) 767 pylab.save(normalize_path(p.filename+a+'(%.2f, %.2f)' %(coord[0], coord[1])),plot_data,fmt='%.6f', delimiter=',') 768 769 770 pylab.plot(x_data,y_data, label='Unit (%.2f, %.2f)' %(coord[0], coord[1])) 771 (ymin,ymax)=p.ybounds 772 pylab.axis(xmin=init_time,xmax=final_time,ymin=ymin,ymax=ymax) 773 774 if isint: pylab.ion() 775 pylab.legend(loc=0) 776 p.title=topo.sim.name+': '+a 777 p.filename_suffix=a 778 self._generate_figure(p)
779 780 781 782 # JABALERT: Should be updated to plot for a specified list of sheets, 783 # and then the combination of all of them, so that it will work for 784 # any network. Will need to remove the simple_sheet_name and 785 # complex_sheet_name parameters once that works.
786 -class plot_modulation_ratio(PylabPlotCommand):
787 """ 788 This function computes the modulation ratios of neurons in the 789 specified sheets and plots their histograms. See 790 analysis.vision.complexity for more info. 791 """ 792 793 # JABALERT: All but the first argument should probably be Parameters
794 - def __call__(self,fullmatrix,simple_sheet_name=None,complex_sheet_name=None,bins=frange(0,2.0,0.1,inclusive=True),**params):
795 p=ParamOverrides(self,params) 796 797 from topo.analysis.vision import complexity 798 if (topo.sim.objects().has_key(simple_sheet_name) and topo.sim.objects().has_key(complex_sheet_name)): 799 v1s = complexity(fullmatrix[topo.sim[simple_sheet_name]]).flatten() 800 v1c = complexity(fullmatrix[topo.sim[complex_sheet_name]]).flatten() 801 import matplotlib.ticker as mticker 802 #double the number of complex cells to reflect large width of layer 2/3 803 v1c = numpy.concatenate((array(v1c),array(v1c)),axis=1) 804 pylab.figure() 805 n = pylab.subplot(311) 806 pylab.hist(v1s,bins) 807 pylab.axis([0,2.0,0,4100]) 808 n.yaxis.set_major_locator(mticker.MaxNLocator(3)) 809 810 n = pylab.subplot(312) 811 pylab.hist(v1c,bins) 812 pylab.axis([0,2.0,0,4100]) 813 n.yaxis.set_major_locator(mticker.MaxNLocator(3)) 814 815 n = pylab.subplot(313) 816 pylab.hist(numpy.concatenate((array(v1s),array(v1c)),axis=1),bins) 817 pylab.axis([0,2.0,0,4100]) 818 n.yaxis.set_major_locator(mticker.MaxNLocator(3)) 819 820 self._generate_figure(p)
821 822 823
824 -class measure_position_pref(PositionMeasurementCommand):
825 """Measure a position preference map by collating the response to patterns.""" 826 827 scale = param.Number(default=0.3) 828
829 - def _feature_list(self,p):
830 width =1.0*p.x_range[1]-p.x_range[0] 831 height=1.0*p.y_range[1]-p.y_range[0] 832 return [Feature(name="x",range=p.x_range,step=width/p.divisions), 833 Feature(name="y",range=p.y_range,step=height/p.divisions)]
834 835 836 pg= create_plotgroup(name='Position Preference',category="Preference Maps", 837 doc='Measure preference for the X and Y position of a Gaussian.', 838 pre_plot_hooks=[measure_position_pref.instance()], 839 plot_hooks=[topographic_grid.instance()], 840 normalize='Individually') 841 842 pg.add_plot('X Preference',[('Strength','XPreference')]) 843 pg.add_plot('Y Preference',[('Strength','YPreference')]) 844 pg.add_plot('Position Preference',[('Red','XPreference'), 845 ('Green','YPreference')]) 846 847 848
849 -class measure_cog(ParameterizedFunction):
850 """ 851 Calculate center of gravity (CoG) for each CF of each unit in each CFSheet. 852 853 Unlike measure_position_pref and other measure commands, this one 854 does not work by collating the responses to a set of input patterns. 855 Instead, the CoG is calculated directly from each set of incoming 856 weights. The CoG value thus is an indirect estimate of what 857 patterns the neuron will prefer, but is not limited by the finite 858 number of test patterns as the other measure commands are. 859 860 Measures only one projection for each sheet, as specified by the 861 proj_name parameter. The default proj_name of '' selects the 862 first non-self connection, which is usually useful to examine for 863 simple feedforward networks, but will not necessarily be useful in 864 other cases. 865 """ 866 867 proj_name = param.String(default='',doc=""" 868 Name of the projection to measure; the empty string means 'the first 869 non-self connection available'.""") 870
871 - def __call__(self,**params):
872 p=ParamOverrides(self,params) 873 874 measured_sheets = [s for s in topo.sim.objects(CFSheet).values() 875 if hasattr(s,'measure_maps') and s.measure_maps] 876 877 # Could easily be extended to measure CoG of all projections 878 # and e.g. register them using different names (e.g. "Afferent 879 # XCoG"), but then it's not clear how the PlotGroup would be 880 # able to find them automatically (as it currently supports 881 # only a fixed-named plot). 882 requested_proj=p.proj_name 883 for sheet in measured_sheets: 884 for proj in sheet.in_connections: 885 if (proj.name == requested_proj) or \ 886 (requested_proj == '' and (proj.src != sheet)): 887 self._update_proj_cog(proj) 888 if requested_proj=='': 889 print "measure_cog: Measured %s projection %s from %s" % \ 890 (proj.dest.name,proj.name,proj.src.name) 891 break
892 893
894 - def _update_proj_cog(self,proj):
895 """Measure the CoG of the specified projection and register corresponding SheetViews.""" 896 897 sheet=proj.dest 898 rows,cols=sheet.activity.shape 899 xpref=zeros((rows,cols),Float) 900 ypref=zeros((rows,cols),Float) 901 902 for r in xrange(rows): 903 for c in xrange(cols): 904 cf=proj.cfs[r,c] 905 r1,r2,c1,c2 = cf.input_sheet_slice 906 row_centroid,col_centroid = centroid(cf.weights) 907 xcentroid, ycentroid = proj.src.matrix2sheet( 908 r1+row_centroid+0.5, 909 c1+col_centroid+0.5) 910 911 xpref[r][c]= xcentroid 912 ypref[r][c]= ycentroid 913 914 sheet.sheet_views['XCoG']=SheetView((xpref,sheet.bounds), sheet.name, 915 sheet.precedence,topo.sim.time(),sheet.row_precedence) 916 917 sheet.sheet_views['YCoG']=SheetView((ypref,sheet.bounds), sheet.name, 918 sheet.precedence,topo.sim.time(),sheet.row_precedence)
919 920 921 pg= create_plotgroup(name='Center of Gravity',category="Preference Maps", 922 doc='Measure the center of gravity of each ConnectionField in a Projection.', 923 pre_plot_hooks=[measure_cog.instance()], 924 plot_hooks=[topographic_grid.instance(xsheet_view_name="XCoG",ysheet_view_name="YCoG")], 925 normalize='Individually') 926 pg.add_plot('X CoG',[('Strength','XCoG')]) 927 pg.add_plot('Y CoG',[('Strength','YCoG')]) 928 pg.add_plot('CoG',[('Red','XCoG'),('Green','YCoG')]) 929 930
931 -class measure_or_tuning_fullfield(FeatureCurveCommand):
932 """ 933 Measures orientation tuning curve(s) of a particular unit using a 934 full-field sine grating stimulus. 935 936 The curve can be plotted at various different values of the 937 contrast (or actually any other parameter) of the stimulus. If 938 using contrast and the network contains an LGN layer, then one 939 would usually specify michelson_contrast as the 940 contrast_parameter. If there is no explicit LGN, then scale 941 (offset=0.0) can be used to define the contrast. Other relevant 942 contrast definitions (or other parameters) can also be used, 943 provided they are defined in PatternPresenter and the units 944 parameter is changed as appropriate. 945 """ 946 947 coords = param.Parameter(default=None,doc="""Ignored; here just to suppress warning.""") 948 949 pattern_presenter = param.Callable( 950 default=PatternPresenter(pattern_generator=SineGrating(), 951 contrast_parameter="michelson_contrast"))
952 953 954 create_plotgroup(template_plot_type="curve",name='Orientation Tuning Fullfield',category="Tuning Curves",doc=""" 955 Plot orientation tuning curves for a specific unit, measured using full-field sine gratings. 956 Although the data takes a long time to collect, once it is ready the plots 957 are available immediately for any unit.""", 958 pre_plot_hooks=[measure_or_tuning_fullfield.instance()], 959 plot_hooks=[cyclic_tuning_curve.instance(x_axis="orientation")]) 960 961 962
963 -class measure_or_tuning(UnitCurveCommand):
964 """ 965 Measures orientation tuning curve(s) of a particular unit. 966 967 Uses a circular sine grating patch as the stimulus on the 968 retina. 969 970 The curve can be plotted at various different values of the 971 contrast (or actually any other parameter) of the stimulus. If 972 using contrast and the network contains an LGN layer, then one 973 would usually specify weber_contrast as the contrast_parameter. If 974 there is no explicit LGN, then scale (offset=0.0) can be used to 975 define the contrast. Other relevant contrast definitions (or 976 other parameters) can also be used, provided they are defined in 977 PatternPresenter and the units parameter is changed as 978 appropriate. 979 """ 980 981 num_orientation = param.Integer(default=12) 982 983 static_parameters = param.List(default=["size","x","y"]) 984
985 - def __call__(self,**params):
986 p=ParamOverrides(self,params) 987 self.params('sheet').compute_default() 988 sheet=p.sheet 989 990 for coord in p.coords: 991 self.x=self._sheetview_unit(sheet,coord,'XPreference',default=coord[0]) 992 self.y=self._sheetview_unit(sheet,coord,'YPreference',default=coord[1]) 993 self._compute_curves(p,sheet)
994 995 996 create_plotgroup(template_plot_type="curve",name='Orientation Tuning',category="Tuning Curves",doc=""" 997 Measure orientation tuning for a specific unit at different contrasts, 998 using a pattern chosen to match the preferences of that unit.""", 999 pre_plot_hooks=[measure_or_tuning.instance()], 1000 plot_hooks=[cyclic_tuning_curve.instance(x_axis="orientation")], 1001 prerequisites=['XPreference']) 1002 1003 1004 1005 # JABALERT: Is there some reason not to call it measure_size_tuning?
1006 -class measure_size_response(UnitCurveCommand):
1007 """ 1008 Measure receptive field size of one unit of a sheet. 1009 1010 Uses an expanding circular sine grating stimulus at the preferred 1011 orientation and retinal position of the specified unit. 1012 Orientation and position preference must be calulated before 1013 measuring size response. 1014 1015 The curve can be plotted at various different values of the 1016 contrast (or actually any other parameter) of the stimulus. If 1017 using contrast and the network contains an LGN layer, then one 1018 would usually specify weber_contrast as the contrast_parameter. If 1019 there is no explicit LGN, then scale (offset=0.0) can be used to 1020 define the contrast. Other relevant contrast definitions (or 1021 other parameters) can also be used, provided they are defined in 1022 PatternPresenter and the units parameter is changed as 1023 appropriate. 1024 """ 1025 size=None # Disabled unused parameter 1026 1027 static_parameters = param.List(default=["orientation","x","y"]) 1028 1029 num_sizes = param.Integer(default=10,bounds=(1,None),softbounds=(1,50), 1030 doc="Number of different sizes to test.") 1031 1032 max_size = param.Number(default=1.0,bounds=(0.1,None),softbounds=(1,50), 1033 doc="Maximum extent of the grating") 1034 1035 x_axis = param.String(default='size',constant=True) 1036 1037
1038 - def __call__(self,**params):
1039 p=ParamOverrides(self,params) 1040 self.params('sheet').compute_default() 1041 sheet=p.sheet 1042 1043 for coord in p.coords: 1044 # Orientations are stored as a normalized value beween 0 1045 # and 1, so we scale them by pi to get the true orientations. 1046 self.orientation=pi*self._sheetview_unit(sheet,coord,'OrientationPreference') 1047 self.x=self._sheetview_unit(sheet,coord,'XPreference',default=coord[0]) 1048 self.y=self._sheetview_unit(sheet,coord,'YPreference',default=coord[1]) 1049 self._compute_curves(p,sheet)
1050 1051 1052 # Why not vary frequency too? Usually it's just one number, but it could be otherwise.
1053 - def _feature_list(self,p):
1054 return [Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True), 1055 Feature(name="frequency",values=p.frequencies), 1056 Feature(name="size",range=(0.0,self.max_size),step=1.0/p.num_sizes,cyclic=False)]
1057 1058 1059 create_plotgroup(template_plot_type="curve",name='Size Tuning',category="Tuning Curves", 1060 doc='Measure the size preference for a specific unit.', 1061 pre_plot_hooks=[measure_size_response.instance()], 1062 plot_hooks=[tuning_curve.instance(x_axis="size",unit="Diameter of stimulus")], 1063 prerequisites=['OrientationPreference','XPreference']) 1064 1065 1066
1067 -class measure_contrast_response(UnitCurveCommand):
1068 """ 1069 Measures contrast response curves for a particular unit. 1070 1071 Uses a circular sine grating stimulus at the preferred 1072 orientation and retinal position of the specified unit. 1073 Orientation and position preference must be calulated before 1074 measuring contrast response. 1075 1076 The curve can be plotted at various different values of the 1077 contrast (or actually any other parameter) of the stimulus. If 1078 using contrast and the network contains an LGN layer, then one 1079 would usually specify weber_contrast as the contrast_parameter. If 1080 there is no explicit LGN, then scale (offset=0.0) can be used to 1081 define the contrast. Other relevant contrast definitions (or 1082 other parameters) can also be used, provided they are defined in 1083 PatternPresenter and the units parameter is changed as 1084 appropriate. 1085 """ 1086 1087 static_parameters = param.List(default=["size","x","y"]) 1088 1089 contrasts = param.List(class_=int,default=[10,20,30,40,50,60,70,80,90,100]) 1090 1091 relative_orientations = param.List(class_=float,default=[0.0, pi/6, pi/4, pi/2]) 1092 1093 x_axis = param.String(default='contrast',constant=True) 1094 1095 units = param.String(default=" rad") 1096
1097 - def __call__(self,**params):
1098 p=ParamOverrides(self,params) 1099 self.params('sheet').compute_default() 1100 sheet=p.sheet 1101 1102 for coord in p.coords: 1103 orientation=pi*self._sheetview_unit(sheet,coord,'OrientationPreference') 1104 self.curve_parameters=[{"orientation":orientation+ro} for ro in self.relative_orientations] 1105 1106 self.x=self._sheetview_unit(sheet,coord,'XPreference',default=coord[0]) 1107 self.y=self._sheetview_unit(sheet,coord,'YPreference',default=coord[1]) 1108 1109 self._compute_curves(p,sheet,val_format="%.4f")
1110
1111 - def _feature_list(self,p):
1112 return [Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True), 1113 Feature(name="frequency",values=p.frequencies), 1114 Feature(name="contrast",values=p.contrasts,cyclic=False)]
1115 1116 1117 create_plotgroup(template_plot_type="curve",name='Contrast Response',category="Tuning Curves", 1118 doc='Measure the contrast response function for a specific unit.', 1119 pre_plot_hooks=[measure_contrast_response.instance()], 1120 plot_hooks=[tuning_curve.instance(x_axis="contrast",unit="%")], 1121 prerequisites=['OrientationPreference','XPreference']) 1122 1123 1124
1125 -class measure_orientation_contrast(UnitCurveCommand):
1126 """ 1127 Measures the response to a center sine grating disk and a surround 1128 sine grating ring at different contrasts of the central disk. 1129 1130 The central disk is set to the preferred orientation of the unit 1131 to be measured. The surround disk orientation (relative to the 1132 central grating) and contrast can be varied, as can the size of 1133 both disks. 1134 """ 1135 1136 pattern_presenter = param.Callable( 1137 default=PatternPresenter(pattern_generator=OrientationContrast(), 1138 contrast_parameter="weber_contrast")) 1139 1140 size=None # Disabled unused parameter 1141 # Maybe instead of the below, use size and some relative parameter, to allow easy scaling? 1142 1143 sizecenter=param.Number(default=0.5,bounds=(0,None),doc=""" 1144 The size of the central pattern to present.""") 1145 1146 sizesurround=param.Number(default=1.0,bounds=(0,None),doc=""" 1147 The size of the surround pattern to present.""") 1148 1149 thickness=param.Number(default=0.5,bounds=(0,None),softbounds=(0,1.5),doc="""Ring thickness.""") 1150 1151 contrastsurround=param.Number(default=100,bounds=(0,100),doc="""Contrast of the surround.""") 1152 1153 contrastcenter=param.Number(default=100,bounds=(0,100),doc="""Contrast of the center.""") 1154 1155 x_axis = param.String(default='orientationsurround',constant=True) 1156 1157 orientation_center = param.Number(default=0.0,softbounds=(0.0,numpy.pi),doc=""" 1158 Orientation of the center grating patch""") 1159 1160 units = param.String(default="%") 1161 1162 static_parameters = param.List(default=["x","y","sizecenter","sizesurround","orientationcenter","thickness","contrastcenter"]) 1163 1164 curve_parameters=param.Parameter([{"contrastsurround":30},{"contrastsurround":60},{"contrastsurround":80},{"contrastsurround":90}],doc=""" 1165 List of parameter values for which to measure a curve.""") 1166 1167 or_surrounds = [] 1168
1169 - def __call__(self,**params):
1170 p=ParamOverrides(self,params) 1171 self.params('sheet').compute_default() 1172 sheet=p.sheet 1173 for coord in p.coords: 1174 self.or_surrounds=[] 1175 orientation=p.orientation_center 1176 self.orientationcenter=orientation 1177 1178 for i in xrange(0,self.num_orientation): 1179 self.or_surrounds.append(orientation+i*pi/(self.num_orientation)) 1180 1181 self.x=self._sheetview_unit(sheet,coord,'XPreference',default=coord[0]) 1182 self.y=self._sheetview_unit(sheet,coord,'YPreference',default=coord[1]) 1183 1184 self._compute_curves(p,sheet)
1185
1186 - def _feature_list(self,p):
1187 return [Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True), 1188 Feature(name="frequency",values=p.frequencies), 1189 Feature(name="orientationsurround",values=self.or_surrounds,cyclic=True)]
1190 1191 create_plotgroup(template_plot_type="curve",name='Orientation Contrast',category="Tuning Curves", 1192 doc='Measure the response of one unit to a center and surround sine grating disk.', 1193 pre_plot_hooks=[measure_orientation_contrast.instance()], 1194 plot_hooks=[tuning_curve.instance(x_axis="orientationsurround",unit="%")], 1195 prerequisites=['OrientationPreference','XPreference']) 1196 1197 1198
1199 -class test_measure(UnitCurveCommand):
1200 1201 static_parameters = param.List(default=["size","x","y"]) 1202 1203 x_axis = param.String(default='contrast',constant=True) 1204 1205 units = param.String(default=" rad") 1206
1207 - def __call__(self,**params):
1208 p=ParamOverrides(self,params) 1209 self.params('sheet').compute_default() 1210 sheet=p.sheet 1211 self.x = 0.0 1212 self.y = 0.0 1213 for coord in p.coords: 1214 self._compute_curves(p,sheet,val_format="%.4f")
1215
1216 - def _feature_list(self,p):
1217 return [Feature(name="orientation",values=[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0],cyclic=True), 1218 Feature(name="contrast",values=[100],cyclic=False)]
1219 1220 import types 1221 __all__ = list(set([k for k,v in locals().items() 1222 if isinstance(v,types.FunctionType) or 1223 (isinstance(v,type) and issubclass(v,ParameterizedFunction)) 1224 and not v.__name__.startswith('_')])) 1225