1 """
2 FeatureResponses and associated functions and classes.
3
4 These classes implement map and tuning curve measurement based
5 on measuring responses while varying features of an input pattern.
6
7 $Id: featureresponses.py 11316 2010-07-27 17:52:53Z ceball $
8 """
9 __version__='$Revision: 11316 $'
10
11
12 import copy
13
14 from math import pi
15 from colorsys import hsv_to_rgb
16
17 import numpy
18 from numpy import zeros, empty, object_, size, vectorize, fromfunction
19 from numpy.oldnumeric import Float
20
21 import param
22 from param.parameterized import ParameterizedFunction, ParamOverrides
23
24 import topo
25 import topo.base.sheetcoords
26 from topo.base.arrayutil import wrap
27 from topo.base.cf import CFSheet
28 from topo.base.functionfamily import PatternDrivenAnalysis
29 from topo.base.sheet import Sheet, activity_type
30 from topo.base.sheetview import SheetView
31 from topo.command.basic import pattern_present,restore_input_generators, save_input_generators
32 from topo.misc.distribution import Distribution
33 from topo.misc.util import cross_product, frange
34 from topo import pattern
35 from topo.pattern.basic import SineGrating, Gaussian, RawRectangle, Disk
36 from topo.plotting.plotgroup import plotgroups
37 from topo.sheet import GeneratorSheet
45 """
46 Maintains a matrix of Distributions (each of which is a dictionary
47 of (feature value: activity) pairs).
48
49 The matrix contains one Distribution for each unit in a
50 rectangular matrix (given by the matrix_shape constructor
51 argument). The contents of each Distribution can be updated for a
52 given bin value all at once by providing a matrix of new values to
53 update().
54
55 The results can then be accessed as a matrix of weighted averages
56 (which can be used as a preference map) and/or a selectivity
57 map (which measures the peakedness of each distribution).
58 """
59 - def __init__(self,matrix_shape,axis_range=(0.0,1.0), cyclic=False,keep_peak=True):
60 """Initialize the internal data structure: a matrix of Distribution objects."""
61 self.axis_range=axis_range
62 new_distribution = vectorize(lambda x: Distribution(axis_range,cyclic,keep_peak),
63 doc="Return a Distribution instance for each element of x.")
64 self.distribution_matrix = new_distribution(empty(matrix_shape))
65
66
67
68 - def update(self, new_values, bin):
69 """Add a new matrix of histogram values for a given bin value."""
70
71
72
73
74
75 self.distribution_matrix + fromfunction(vectorize(lambda i,j: {bin:new_values[i,j]}),
76 new_values.shape)
77
78
80 """Return the weighted average of each Distribution as a matrix."""
81
82 weighted_average_matrix=zeros(self.distribution_matrix.shape,Float)
83
84 for i in range(len(weighted_average_matrix)):
85 for j in range(len(weighted_average_matrix[i])):
86 weighted_average_matrix[i,j]=self.distribution_matrix[i,j].weighted_average()
87
88
89
90 return weighted_average_matrix
91
92
94 """Return the bin with the max value of each Distribution as a matrix."""
95
96 max_value_bin_matrix=zeros(self.distribution_matrix.shape,Float)
97
98 for i in range(len(max_value_bin_matrix)):
99 for j in range(len(max_value_bin_matrix[i])):
100 max_value_bin_matrix[i,j]=self.distribution_matrix[i,j].max_value_bin()
101
102 return max_value_bin_matrix
103
104
105
107 """Return the selectivity of each Distribution as a matrix."""
108
109 selectivity_matrix=zeros(self.distribution_matrix.shape,Float)
110
111 for i in range(len(selectivity_matrix)):
112 for j in range(len(selectivity_matrix[i])):
113 selectivity_matrix[i,j]=self.distribution_matrix[i,j].selectivity()
114
115 return selectivity_matrix
116
120 """
121 Records the output of every unit in a sheet, for every combination of feature values.
122 Useful for collecting data for later analysis while presenting many input patterns.
123 """
124
125 - def __init__(self,matrix_shape,features):
126 self.matrix_shape = matrix_shape
127 self.features = features
128 self.dimensions = ()
129 for f in features:
130 self.dimensions = self.dimensions + (size(f.values),)
131 self.full_matrix = empty(self.dimensions,object_)
132
133
134 - def update(self, new_values, feature_value_permutation):
135 """Add a new matrix of histogram values for a given bin value."""
136 index = ()
137 for f in self.features:
138 for ff,value in feature_value_permutation:
139 if(ff == f.name):
140 index = index + (f.values.index(value),)
141 self.full_matrix[index] = new_values
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 -class FeatureResponses(PatternDrivenAnalysis):
157 """
158 Systematically vary input pattern feature values and collate the responses.
159
160 Each sheet has a DistributionMatrix for each feature that will be
161 tested. The DistributionMatrix stores the distribution of
162 activity values for each unit in the sheet for that feature. For
163 instance, if the features to be tested are orientation and phase,
164 we will create a DistributionMatrix for orientation and a
165 DistributionMatrix for phase for each sheet. The orientation and
166 phase of the input are then systematically varied (when
167 measure_responses is called), and the responses of each unit
168 to each pattern are collected into the DistributionMatrix.
169
170 The resulting data can then be used to plot feature maps and
171 tuning curves, or for similar types of feature-based analyses.
172 """
173
174
175
176
177
178 repetitions = param.Integer(default=1,bounds=(1,None),doc="""
179 How many times each stimulus will be presented.
180
181 Each stimulus is specified by a particular feature
182 combination, and need only be presented once if the network
183 has no other source of variability. If results differ for
184 each presentation of an identical stimulus (e.g. due to
185 intrinsic noise), then this parameter can be increased
186 so that results will be an average over the specified
187 number of repetitions.""")
188
189 _fullmatrix = {}
190
196
208
213
254
288
295
321
322
324 self.measure_responses(pattern_presenter,param_dict,feature_values,display)
325
326 for sheet in self.sheets_to_measure():
327 rows,cols = sheet.activity.shape
328 input_bounds = self.input_sheet.bounds
329 input_sheet_views = self.input_sheet.sheet_views
330
331 for ii in range(rows):
332 for jj in range(cols):
333 view = SheetView((self._featureresponses[sheet][ii,jj],input_bounds),
334 sheet.name,sheet.precedence,topo.sim.time(),sheet.row_precedence)
335 x,y = sheet.matrixidx2sheet(ii,jj)
336 key = ('RFs',sheet.name,x,y)
337 input_sheet_views[key]=view
338
339
340
341
353
379
380
387
390 """
391 Measures and collects the responses to a set of features for calculating feature maps.
392
393 For each feature and each sheet, the results are stored as a
394 preference matrix and selectivity matrix in the sheet's
395 sheet_views; these can then be plotted as preference
396 or selectivity maps.
397 """
398
399 selectivity_multiplier = param.Number(default=17,bounds=(0.0,None),doc="""
400 Factor by which to multiply the calculated selectivity values
401 before plotting them. Usually set much greater than 1.0 to
402 highlight particularly unselective areas, especially when
403 combining selectivity with other plots as when using Confidence
404 subplots.""")
405
406
407
408
409
410 sheet_views_prefix = param.String(default="",doc="""
411 Prefix to add to the name under which results are stored in
412 sheet_views.""")
413
417
419 """
420 Present the given input patterns and collate the responses.
421
422 If weighted_average is True, the feature responses are
423 calculated from a weighted average of the values of each bin
424 in the distribution, rather than simply using the actual value
425 of the parameter for which response was maximal (the discrete
426 method). Such a computation will generally produce much more
427 precise maps using fewer test stimuli than the discrete
428 method. However, weighted_average methods generally require
429 uniform and full-range sampling, as described below, which is
430 not always feasible.
431
432 For measurements at evenly-spaced intervals over the full
433 range of possible parameter values, weighted_averages are a
434 good measure of the underlying continuous-valued parameter
435 preference, assuming that neurons are tuned broadly enough
436 (and/or sampled finely enough) that they respond to at least
437 two of the tested parameter values. This method will not
438 usually give good results when those criteria are not met,
439 i.e. if the sampling is too sparse, not at evenly-spaced
440 intervals, or does not cover the full range of possible
441 values. In such cases weighted_average should be set to
442 False, and the number of test patterns will usually need
443 to be increased instead.
444 """
445 self.measure_responses(pattern_presenter,param_dict,self.features,display)
446
447 for sheet in self.sheets_to_measure():
448 bounding_box = sheet.bounds
449
450 for feature in self._featureresponses[sheet].keys():
451
452
453
454
455
456
457
458
459
460
461 cyclic = self._featureresponses[sheet][feature].distribution_matrix[0,0].cyclic
462 if cyclic:
463 norm_factor = self._featureresponses[sheet][feature].distribution_matrix[0,0].axis_range
464 else:
465 norm_factor = 1.0
466
467
468 value_offset = [f.value_offset for f in self.features if f.name==feature]
469 value_multiplier = [f.value_multiplier for f in self.features if f.name==feature]
470
471
472 if weighted_average:
473
474 preference_map = SheetView((((self._featureresponses[sheet][feature].weighted_average())+value_offset)*value_multiplier/norm_factor,
475 bounding_box), sheet.name, sheet.precedence, topo.sim.time())
476
477 else:
478 preference_map = SheetView((((self._featureresponses[sheet][feature].max_value_bin())+value_offset)*value_multiplier/norm_factor,
479 bounding_box), sheet.name, sheet.precedence, topo.sim.time())
480
481 preference_map.cyclic = cyclic
482 preference_map.norm_factor = norm_factor
483
484
485 sheet.sheet_views[self.sheet_views_prefix+feature.capitalize()+'Preference']=preference_map
486
487 selectivity_map = SheetView((self.selectivity_multiplier*
488 self._featureresponses[sheet][feature].selectivity(),
489 bounding_box), sheet.name , sheet.precedence, topo.sim.time(),sheet.row_precedence)
490 sheet.sheet_views[self.sheet_views_prefix+feature.capitalize()+'Selectivity']=selectivity_map
491
494 """
495 Measures and collects the responses to a set of features, for calculating tuning and similar curves.
496
497 These curves represent the response of a Sheet to patterns that
498 are controlled by a set of features. This class can collect data
499 for multiple curves, each with the same x axis. The x axis
500 represents the main feature value that is being varied, such as
501 orientation. Other feature values can also be varied, such as
502 contrast, which will result in multiple curves (one per unique
503 combination of other feature values).
504
505 The sheet responses used to construct the curves will be stored in
506 a dictionary curve_dict kept in the Sheet of interest. A
507 particular set of patterns is then constructed using a
508 user-specified PatternPresenter by adding the parameters
509 determining the curve (curve_param_dict) to a static list of
510 parameters (param_dict), and then varying the specified set of
511 features. The results can be accessed in the curve_dict,
512 indexed by the curve_label and feature value.
513 """
514 post_collect_responses_hook = param.HookList(default=[],instantiate=False,doc="""
515 List of callable objects to be run at the end of collect_feature_responses function.
516 The functions should accept three parameters: FullMatrix, curve label, sheet""")
517
518 - def __init__(self,features,sheet,x_axis):
525
528
530 self.initialize_featureresponses(features)
531 rows,cols=self.sheet.shape
532 bounding_box = self.sheet.bounds
533 self.measure_responses(pattern_presenter,param_dict,features,display)
534 self.sheet.curve_dict[self.x_axis][curve_label]={}
535 for key in self._featureresponses[self.sheet][self.x_axis].distribution_matrix[0,0]._data.iterkeys():
536 y_axis_values = zeros(self.sheet.shape,activity_type)
537 for i in range(rows):
538 for j in range(cols):
539 y_axis_values[i,j] = self._featureresponses[self.sheet][self.x_axis].distribution_matrix[i,j].get_value(key)
540 Response = SheetView((y_axis_values,bounding_box), self.sheet.name , self.sheet.precedence, topo.sim.time(),self.sheet.row_precedence)
541 self.sheet.curve_dict[self.x_axis][curve_label].update({key:Response})
542 for f in self.post_collect_responses_hook: f(self._fullmatrix[self.sheet],curve_label,self.sheet)
543
544
545
546
547
548
549
550 -class Feature(object):
551 """
552 Stores the parameters required for generating a map of one input feature.
553 """
554
555 - def __init__(self, name, range=None, step=0.0, values=None, cyclic=False, value_offset=0.0, value_multiplier=1.0, compute_fn=None, offset=0, keep_peak=True):
556 """
557 Users can provide either a range and a step size, or a list of values.
558 If a list of values is supplied, the range can be omitted unless the
559 default of the min and max in the list of values is not appropriate.
560
561 If non-None, the compute_fn should be a function that when given a list
562 of other parameter values, computes and returns the value for this feature.
563
564 If supplied, the offset is added to the given or computed values to allow
565 the starting value to be specified.
566 """
567 self.name=name
568 self.cyclic=cyclic
569 self.compute_fn=compute_fn
570 self.range=range
571 self.keep_peak=keep_peak
572 self.value_offset=value_offset
573 self.value_multiplier=value_multiplier
574
575 if values is not None:
576 self.values=values if offset == 0 else [v+offset for v in values]
577 if not self.range:
578 self.range=(min(self.values),max(self.values))
579 else:
580 if range is None:
581 raise ValueError('The range or values must be specified.')
582 low_bound,up_bound = self.range
583 values=(frange(low_bound,up_bound,step,not cyclic))
584 self.values = values if offset == 0 else \
585 [(v+offset)%(up_bound-low_bound) if cyclic else (v+offset)
586 for v in values]
587
590 """
591 Function object for presenting PatternGenerator-created patterns.
592
593 This class helps coordinate a set of patterns to be presented to a
594 set of GeneratorSheets. It provides a standardized way of
595 generating a set of linked patterns for testing or analysis, such
596 as when measuring preference maps or presenting test patterns.
597 Subclasses can provide additional mechanisms for doing this in
598 different ways.
599 """
600
601
602 contrast_parameter = param.Parameter('michelson_contrast')
603
604
605 divisions = param.Parameter()
606
607 apply_output_fns = param.Boolean(default=True, doc="""
608 When presenting a pattern, whether to apply each sheet's
609 output function. If False, for many networks the response
610 will be linear, which requires fewer test patterns to measure
611 a meaningful response, but it may not correspond to the actual
612 preferences of each neuron under other conditions. If True,
613 callers will need to ensure that the input patterns are in a
614 suitable range to drive the neurons to generate meaningful
615 output, because e.g. a threshold-based output function might
616 result in no activity for inputs that are too weak..""")
617
618 duration = param.Number(default=1.0,doc="""
619 Amount of simulation time for which to present each test pattern.
620 By convention, most Topographica example files are designed to
621 have a suitable activity pattern computed by the
622 default time, but the duration will need to be changed for
623 other models that do not follow that convention or if a
624 linear response is desired.""")
625
626
627
628 generator_sheets = param.List(default=[], doc="""
629 The set of GeneratorSheets onto which patterns will be drawn.
630
631 By default (i.e. for an empty list), all GeneratorSheets in
632 the simulation will be used.
633 """)
634
635 - def __init__(self,pattern_generator,**params):
636 """
637 pattern_generator is the PatternGenerator that will be drawn
638 on the generator_sheets (the parameters of the
639 pattern_generator are specified during calls.
640 """
641 super(PatternPresenter,self).__init__(**params)
642 self.gen = pattern_generator
643
644
645 - def __call__(self,features_values,param_dict):
646 for param,value in param_dict.iteritems():
647
648
649 self.gen.__setattr__(param,value)
650
651 for feature,value in features_values.iteritems():
652 self.gen.__setattr__(feature,value)
653
654 all_input_sheet_names = topo.sim.objects(GeneratorSheet).keys()
655
656 if len(self.generator_sheets)>0:
657 input_sheet_names = [sheet.name for sheet in self.generator_sheets]
658 else:
659 input_sheet_names = all_input_sheet_names
660
661
662 inputs = dict.fromkeys(input_sheet_names)
663 for k in inputs.keys():
664 inputs[k]=copy.deepcopy(self.gen)
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680 if 'direction' in features_values:
681 import __main__
682 if '_new_motion_model' in __main__.__dict__ and __main__.__dict__['_new_motion_model']:
683
684 from topo.pattern import Translator
685 for name in inputs:
686 inputs[name] = Translator(generator=inputs[name],
687 direction=features_values['direction'],
688 speed=features_values['speed'],
689 reset_period=self.duration)
690
691 else:
692
693 orientation = features_values['direction']+pi/2
694 from topo.pattern.basic import Sweeper
695 for name in inputs.keys():
696 speed=features_values['speed']
697 try:
698 step=int(name[-1])
699 except:
700 if not hasattr(self,'direction_warned'):
701 self.warning('Assuming step is zero; no input lag number specified at the end of the input sheet name.')
702 self.direction_warned=True
703 step=0
704 speed=features_values['speed']
705 inputs[name] = Sweeper(generator=inputs[name],step=step,speed=speed)
706 setattr(inputs[name],'orientation',orientation)
707
708
709 if features_values.has_key('hue'):
710
711
712
713
714
715
716 rgb_retina = False
717 for name in input_sheet_names:
718 if not ('Red' in name or 'Green' in name or 'Blue' in name):
719 rgb_retina=True
720
721 if not rgb_retina:
722 for name in inputs.keys():
723 r,g,b=hsv_to_rgb(features_values['hue'],1.0,1.0)
724 if (name.count('Red')):
725 inputs[name].scale=r
726 elif (name.count('Green')):
727 inputs[name].scale=g
728 elif (name.count('Blue')):
729 inputs[name].scale=b
730 else:
731 if not hasattr(self,'hue_warned'):
732 self.warning('Unable to measure hue preference, because hue is defined only when there are different input sheets with names with Red, Green or Blue substrings.')
733 self.hue_warned=True
734 else:
735 from contrib import rgbimages
736 r,g,b=hsv_to_rgb(features_values['hue'],1.0,1.0)
737 for name in inputs.keys():
738 inputs[name] = rgbimages.ExtendToRGB(generator=inputs[name],
739 relative_channel_strengths=[r,g,b])
740
741
742
743
744 if features_values.has_key('retinotopy'):
745
746 coordinate_x=[]
747 coordinate_y=[]
748 coordinates=[]
749 for name,i in zip(inputs.keys(),range(len(input_sheet_names))):
750 l,b,r,t = topo.sim[name].nominal_bounds.lbrt()
751 x_div=float(r-l)/(self.divisions*2)
752 y_div=float(t-b)/(self.divisions*2)
753 for i in range(self.divisions):
754 if not bool(self.divisions%2):
755 if bool(i%2):
756 coordinate_x.append(i*x_div)
757 coordinate_y.append(i*y_div)
758 coordinate_x.append(i*-x_div)
759 coordinate_y.append(i*-y_div)
760 else:
761 if not bool(i%2):
762 coordinate_x.append(i*x_div)
763 coordinate_y.append(i*y_div)
764 coordinate_x.append(i*-x_div)
765 coordinate_y.append(i*-y_div)
766 for x in coordinate_x:
767 for y in coordinate_y:
768 coordinates.append((x,y))
769
770 x_coord=coordinates[features_values['retinotopy']][0]
771 y_coord=coordinates[features_values['retinotopy']][1]
772 inputs[name].x = x_coord
773 inputs[name].y = y_coord
774
775 if features_values.has_key('retx'):
776 for name,i in zip(inputs.keys(),range(len(input_sheet_names))):
777 inputs[name].x = features_values['retx']
778 inputs[name].y = features_values['rety']
779
780 if features_values.has_key("phasedisparity"):
781 temp_phase1=features_values['phase']-features_values['phasedisparity']/2.0
782 temp_phase2=features_values['phase']+features_values['phasedisparity']/2.0
783 for name in inputs.keys():
784 if (name.count('Right')):
785 inputs[name].phase=wrap(0,2*pi,temp_phase1)
786 elif (name.count('Left')):
787 inputs[name].phase=wrap(0,2*pi,temp_phase2)
788 else:
789 if not hasattr(self,'disparity_warned'):
790 self.warning('Unable to measure disparity preference, because disparity is defined only when there are inputs for Right and Left retinas.')
791 self.disparity_warned=True
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816 if features_values.has_key("ocular"):
817 for name in inputs.keys():
818 if (name.count('Right')):
819 inputs[name].scale=2*features_values['ocular']
820 elif (name.count('Left')):
821 inputs[name].scale=2.0-2*features_values['ocular']
822 else:
823 self.warning('Skipping input region %s; Ocularity is defined only for Left and Right retinas.' %
824 name)
825
826 if features_values.has_key("contrastcenter")or param_dict.has_key("contrastcenter"):
827 if self.contrast_parameter=='michelson_contrast':
828 for g in inputs.itervalues():
829 g.offsetcenter=0.5
830 g.scalecenter=2*g.offsetcenter*g.contrastcenter/100.0
831
832 elif self.contrast_parameter=='weber_contrast':
833
834
835
836
837 for g in inputs.itervalues():
838 g.offsetcenter=0.5
839 g.scalecenter=2*g.offsetcenter*g.contrastcenter/100.0
840
841 elif self.contrast_parameter=='scale':
842 for g in inputs.itervalues():
843 g.offsetcenter=0.0
844 g.scalecenter=g.contrastcenter
845
846 if features_values.has_key("contrastsurround")or param_dict.has_key("contrastsurround"):
847 if self.contrast_parameter=='michelson_contrast':
848 for g in inputs.itervalues():
849 g.offsetsurround=0.5
850 g.scalesurround=2*g.offsetsurround*g.contrastsurround/100.0
851
852 elif self.contrast_parameter=='weber_contrast':
853
854
855
856
857 for g in inputs.itervalues():
858 g.offsetsurround=0.5
859 g.scalesurround=2*g.offsetsurround*g.contrastsurround/100.0
860
861 elif self.contrast_parameter=='scale':
862 for g in inputs.itervalues():
863 g.offsetsurround=0.0
864 g.scalesurround=g.contrastsurround
865
866 if features_values.has_key("contrast") or param_dict.has_key("contrast"):
867 if self.contrast_parameter=='michelson_contrast':
868 for g in inputs.itervalues():
869 g.offset=0.5
870 g.scale=2*g.offset*g.contrast/100.0
871
872 elif self.contrast_parameter=='weber_contrast':
873
874
875
876
877 for g in inputs.itervalues():
878 g.offset=0.5
879 g.scale=2*g.offset*g.contrast/100.0
880
881 elif self.contrast_parameter=='scale':
882 for g in inputs.itervalues():
883 g.offset=0.0
884 g.scale=g.contrast
885
886
887 for sheet_name in set(all_input_sheet_names).difference(set(input_sheet_names)):
888 inputs[sheet_name]=pattern.Constant(scale=0)
889
890 pattern_present(inputs, self.duration, plastic=False,
891 apply_output_fns=self.apply_output_fns)
892
895 """
896 Convenience functions for handling subplots (such as colorized Activity plots).
897 Only needed for avoiding typing, as plots can be declared with their own
898 specific subplots without using these functions.
899 """
900
901 plotgroups_to_subplot=param.List(default=
902 ["Activity", "Connection Fields", "Projection", "Projection Activity"],
903 doc="List of plotgroups for which to set subplots.")
904
905 subplotting_declared = param.Boolean(default=False,
906 doc="Whether set_subplots has previously been called")
907
908 _last_args = param.Parameter(default=())
909
910 @staticmethod
911 - def set_subplots(prefix=None,hue="",confidence="",force=True):
912 """
913 Define Hue and Confidence subplots for each of the plotgroups_to_subplot.
914 Typically used to make activity or weight plots show a
915 preference value as the hue, and a selectivity as the
916 confidence.
917
918 The specified hue, if any, should be the name of a SheetView,
919 such as OrientationPreference. The specified confidence, if
920 any, should be the name of a (usually) different SheetView,
921 such as OrientationSelectivity.
922
923 The prefix option is a shortcut making the usual case easier
924 to type; it sets hue to prefix+"Preference" and confidence to
925 prefix+"Selectivity".
926
927 If force=False, subplots are changed only if no subplot is
928 currently defined. Force=False is useful for setting up
929 subplots automatically when maps are measured, without
930 overwriting any subplots set up specifically by the user.
931
932 Currently works only for plotgroups that have a plot
933 with the same name as the plotgroup, though this could
934 be changed easily.
935
936 Examples::
937
938 Subplotting.set_subplots("Orientation")
939 - Set the default subplots to OrientationPreference and OrientationSelectivity
940
941 Subplotting.set_subplots(hue="OrientationPreference")
942 - Set the default hue subplot to OrientationPreference with no selectivity
943
944 Subplotting.set_subplots()
945 - Remove subplots from all the plotgroups_to_subplot.
946 """
947
948 Subplotting._last_args=(prefix,hue,confidence,force)
949
950 if Subplotting.subplotting_declared and not force:
951 return
952
953 if prefix:
954 hue=prefix+"Preference"
955 confidence=prefix+"Selectivity"
956
957 for name in Subplotting.plotgroups_to_subplot:
958 if plotgroups.has_key(name):
959 pg=plotgroups[name]
960 if pg.plot_templates.has_key(name):
961 pt=pg.plot_templates[name]
962 pt["Hue"]=hue
963 pt["Confidence"]=confidence
964 else:
965 Subplotting().warning("No template %s defined for plotgroup %s" % (name,name))
966 else:
967 Subplotting().warning("No plotgroup %s defined" % name)
968
969 Subplotting.subplotting_declared=True
970
971
972 @staticmethod
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055 -class MeasureResponseCommand(ParameterizedFunction):
1056 """Parameterized command for presenting input patterns and measuring responses."""
1057
1058 scale = param.Number(default=1.0,softbounds=(0.0,2.0),doc="""
1059 Multiplicative strength of input pattern.""")
1060
1061 offset = param.Number(default=0.0,softbounds=(-1.0,1.0),doc="""
1062 Additive offset to input pattern.""")
1063
1064 display = param.Boolean(default=False,doc="""
1065 Whether to update a GUI display (if any) during the map measurement.""")
1066
1067 weighted_average= param.Boolean(default=True, doc="""
1068 Whether to compute results using a weighted average, or just
1069 discrete values. A weighted average can give more precise
1070 results, without being limited to a set of discrete values,
1071 but the results can have systematic biases due to the
1072 averaging, especially for non-cyclic parameters.""")
1073
1074 pattern_presenter = param.Callable(default=None,instantiate=True,doc="""
1075 Callable object that will present a parameter-controlled pattern to a
1076 set of Sheets. Needs to be supplied by a subclass or in the call.
1077 The attributes duration and apply_output_fns (if non-None) will
1078 be set on this object, and it should respect those if possible.""")
1079
1080 static_parameters = param.List(class_=str,default=["scale","offset"],doc="""
1081 List of names of parameters of this class to pass to the
1082 pattern_presenter as static parameters, i.e. values that
1083 will be fixed to a single value during measurement.""")
1084
1085 subplot = param.String("",doc="""Name of map to register as a subplot, if any.""")
1086
1087 apply_output_fns = param.Boolean(default=None, doc="""
1088 If non-None, pattern_presenter.apply_output_fns will be
1089 set to this value. Provides a simple way to set
1090 this commonly changed option of PatternPresenter.""")
1091
1092 duration = param.Number(default=None,doc="""
1093 If non-None, pattern_presenter.duration will be
1094 set to this value. Provides a simple way to set
1095 this commonly changed option of PatternPresenter.""")
1096
1097 sheet_views_prefix = param.String(default="",doc="""
1098 Optional prefix to add to the name under which results are
1099 stored in sheet_views. Can be used e.g. to distinguish maps as
1100 originating from a particular GeneratorSheet.""")
1101
1102 generator_sheets = param.List(default=[],doc="""
1103 pattern_presenter.generator_sheets will be set to this value.
1104 The default value of [] results in all GeneratorSheets being
1105 used.""")
1106
1107 __abstract = True
1108
1109
1129
1130
1132 """Return the list of features to vary; must be implemented by each subclass."""
1133 raise NotImplementedError
1134
1138 """Parameterized command for presenting sine gratings and measuring responses."""
1139
1140 pattern_presenter = param.Callable(instantiate=True,
1141 default=PatternPresenter(pattern_generator=SineGrating()),doc="""
1142 Callable object that will present a parameter-controlled pattern to a
1143 set of Sheets. By default, uses a SineGrating presented for a short
1144 duration. By convention, most Topographica example files
1145 are designed to have a suitable activity pattern computed by
1146 that time, but the duration will need to be changed for other
1147 models that do not follow that convention.""")
1148
1149 frequencies = param.List(class_=float,default=[2.4],doc="Sine grating frequencies to test.")
1150
1151 num_phase = param.Integer(default=18,bounds=(1,None),softbounds=(1,48),
1152 doc="Number of phases to test.")
1153
1154 num_orientation = param.Integer(default=4,bounds=(1,None),softbounds=(1,24),
1155 doc="Number of orientations to test.")
1156
1157 scale = param.Number(default=0.3)
1158
1159 __abstract = True
1160
1164 """Parameterized command for measuring topographic position."""
1165
1166 divisions=param.Integer(default=6,bounds=(1,None),doc="""
1167 The number of different positions to measure in X and in Y.""")
1168
1169 x_range=param.NumericTuple((-0.5,0.5),doc="""
1170 The range of X values to test.""")
1171
1172 y_range=param.NumericTuple((-0.5,0.5),doc="""
1173 The range of Y values to test.""")
1174
1175 size=param.Number(default=0.5,bounds=(0,None),doc="""
1176 The size of the pattern to present.""")
1177
1178 pattern_presenter = param.Callable(
1179 default=PatternPresenter(Gaussian(aspect_ratio=1.0)),doc="""
1180 Callable object that will present a parameter-controlled
1181 pattern to a set of Sheets. For measuring position, the
1182 pattern_presenter should be spatially localized, yet also able
1183 to activate the appropriate neurons reliably.""")
1184
1185 static_parameters = param.List(default=["scale","offset","size"])
1186
1187 __abstract = True
1188
1220
1224 """A callable Parameterized command for measuring tuning curves."""
1225
1226 num_orientation = param.Integer(default=12)
1227
1228 sheet = param.ObjectSelector(
1229 default=None,doc="""
1230 Name of the sheet to use in measurements.""")
1231
1232 units = param.String(default='%',doc="""
1233 Units for labeling the curve_parameters in figure legends.
1234 The default is %, for use with contrast, but could be any
1235 units (or the empty string).""")
1236
1237
1238 x_axis = param.String(default='orientation',doc="""
1239 Parameter to use for the x axis of tuning curves.""")
1240
1241 static_parameters = param.List(default=[])
1242
1243
1244
1245
1246 curve_parameters=param.Parameter([{"contrast":30},{"contrast":60},{"contrast":80},{"contrast":90}],doc="""
1247 List of parameter values for which to measure a curve.""")
1248
1249 __abstract = True
1250
1255
1256
1258 """
1259 Compute a set of curves for the specified sheet, using the
1260 specified val_format to print a label for each value of a
1261 curve_parameter.
1262 """
1263
1264 x=FeatureCurves(self._feature_list(p),sheet=sheet,x_axis=self.x_axis)
1265 for curve in p.curve_parameters:
1266 static_params = dict([(s,p[s]) for s in p.static_parameters])
1267 static_params.update(curve)
1268 curve_label="; ".join([('%s = '+val_format+'%s') % (n.capitalize(),v,p.units) for n,v in curve.items()])
1269
1270 x.collect_feature_responses(self._feature_list(p),p.pattern_presenter,static_params,curve_label,p.display)
1271
1272
1273
1275 return [Feature(name="phase",range=(0.0,2*pi),step=2*pi/p.num_phase,cyclic=True),
1276 Feature(name="orientation",range=(0,pi),step=pi/p.num_orientation,cyclic=True),
1277 Feature(name="frequency",values=p.frequencies)]
1278
1279
1281 """Look up and return the value of a SheetView for a specified unit."""
1282 matrix_coords = sheet.sheet2matrixidx(*sheet_coord)
1283
1284 if(map_name in sheet.sheet_views):
1285 pref = sheet.sheet_views[map_name].view()[0]
1286 val = pref[matrix_coords]
1287 else:
1288 self.warning(("%s should be measured before plotting this tuning curve -- " +
1289 "using default value of %s for %s unit (%d,%d).") % \
1290 (map_name,default,sheet.name,sheet_coord[0],sheet_coord[1]))
1291 val = default
1292
1293 return val
1294
1312