""" Simple GCAL-based color map. Subcortical inputs are taken from the single-eye trichromatic color map simulation from: Judah Ben De Paula, Modeling the Self-Organization of Color Selectivity in the Visual Cortex, PhD thesis, Department of Computer Sciences, The University of Texas at Austin, 2007. and the V1 model is from: Jean-Luc R. Stevens, Judith S. Law, Jan Antolik, and James A. Bednar. Mechanisms for stable, robust, and adaptive development of orientation maps in the primary visual cortex. Journal of Neuroscience 33:15747-15766, 2013. http://dx.doi.org/10.1523/JNEUROSCI.1037-13.2013 Not yet calibrated or validated. Updated Fri Mar 7 14:11:38 GMT 2014 """ from math import pi import param from topo import learningfn,numbergen,transferfn,pattern,projection,responsefn,sheet import topo.learningfn.optimized import topo.learningfn.projfn import topo.transferfn.optimized import topo.pattern.random import topo.pattern.image import topo.responsefn.optimized import topo.sheet.optimized import topo.transferfn.misc from topo.base.arrayutil import DivideWithConstant # Parameters that can be passed on the command line using -p from topo.misc.commandline import global_params as p p.add( area=param.Number(default=1.0,bounds=(0,None), inclusive_bounds=(False,True),doc=""" Linear size of cortical area to simulate. 2.0 gives a 2.0x2.0 Sheet area in V1."""), eyes=param.List(default=[''],doc=""" Which eyes to simulate, with '' representing a single-eye simulation and ['Left','Right'] for a two-eye simulation."""), cone_types=param.List(default=['Red','Green','Blue'],doc=""" Which cone types to include in the simulation. Currently supports RGB or GB."""), gain_control=param.Boolean(default=False,doc=""" Whether to use divisive lateral inhibition in the LGN for contrast gain control."""), retina_density=param.Number(default=24.0,bounds=(0,None), inclusive_bounds=(False,True),doc=""" The nominal_density to use for the retina."""), lgn_density=param.Number(default=24.0,bounds=(0,None), inclusive_bounds=(False,True),doc=""" The nominal_density to use for the LGN."""), cortex_density=param.Number(default=48.0,bounds=(0,None), inclusive_bounds=(False,True),doc=""" The nominal_density to use for V1."""), scale=param.Number(default=0.7,inclusive_bounds=(False,True),doc=""" Brightness of the input patterns"""), color_strength=param.Number(default=0.75,bounds=(0.0,None),doc=""" Ratio between strengths of LGN color vs. luminosity channel connections to V1. Examples:: :0.00: Monochrome simulation :0.50: Equal weight to color vs. luminosity channels :0.75: Equal weight to each channel (assuming 6 color and 2 lum channels) :1.00: No luminosity channels Note that luminosity channels are not required for developing monochromatic responses, so even for color_strength=1.0 there are normally orientation-selective and not color-selective neurons."""), cone_scale=param.List(default=[0.9,1.0,0.97],doc=""" Scaling factors for the [L,M,S] cones. Default values are from De Paula (2007). Allows individual photoreceptor channels to be boosted or reduced relative to each other, e.g. to approximate the final result of a homeostatic process not being simulated."""), aff_strength=param.Number(default=1.0,bounds=(0.0,None),doc=""" Overall strength of the afferent projection to V1."""), exc_strength=param.Number(default=1.7,bounds=(0.0,None),doc=""" Overall strength of the lateral excitatory projection to V1."""), inh_strength=param.Number(default=1.4,bounds=(0.0,None),doc=""" Overall strength of the lateral inhibitory projection to V1."""), aff_lr=param.Number(default=0.1,bounds=(0.0,None),doc=""" Learning rate for the afferent projection to V1."""), exc_lr=param.Number(default=0.0,bounds=(0.0,None),doc=""" Learning rate for the lateral excitatory projection to V1."""), inh_lr=param.Number(default=0.3,bounds=(0.0,None),doc=""" Learning rate for the lateral inhibitory projection to V1.""")) ### Specify weight initialization, response function, and learning function projection.CFProjection.cf_shape=pattern.Disk(smoothing=0.0) projection.CFProjection.weights_generator=pattern.Constant() projection.CFProjection.response_fn=responsefn.optimized.CFPRF_DotProduct_opt() projection.CFProjection.learning_fn=learningfn.optimized.CFPLF_Hebbian_opt() projection.CFProjection.weights_output_fns=[transferfn.optimized.CFPOF_DivisiveNormalizeL1_opt()] projection.SharedWeightCFProjection.response_fn=responsefn.optimized.CFPRF_DotProduct_opt() ### Input patterns and LGN sheets center_polarities=['On','Off'] rg_cone_types=[c for c in p.cone_types if c != 'Blue'] blue_opponent=''.join(rg_cone_types) opponent_types=list(set([rg_cone_types[0]+'-'+rg_cone_types[-1], rg_cone_types[-1]+'-'+rg_cone_types[0]]))+ \ ['Blue-'+blue_opponent,'Luminosity'] mcgill_filename={'Red':0,'Green':1,'Blue':2} cone_scale_=dict(zip(['Red','Green','Blue'],p.cone_scale)) input_type=pattern.image.FileImage # Convenience variable; number of afferent connections to V1 num_aff=len(center_polarities)*len(p.eyes)*len(opponent_types) for e in p.eyes: for cone in p.cone_types: image_filenames=["images/mcgill/foliage_b/%02d_%d.png"%(i,mcgill_filename[cone]) for i in xrange(1,26)] inputs=[input_type(filename=f, size=10.0, x=numbergen.UniformRandom(lbound=-0.75,ubound=0.75,seed=12), y=numbergen.UniformRandom(lbound=-0.75,ubound=0.75,seed=34), orientation=numbergen.UniformRandom(lbound=-pi,ubound=pi,seed=56), scale= 2.0-numbergen.UniformRandom(lbound=0,ubound=2,seed=78)*1.4*cone_scale_[cone] \ if e=='Right' else numbergen.UniformRandom(lbound=0,ubound=2,seed=78)*1.4*cone_scale_[cone]) for f in image_filenames] input_composite=pattern.Selector(generators=inputs) topo.sim[e+cone+'Retina']=sheet.GeneratorSheet( nominal_density=p.retina_density, input_generator=input_composite, period=1.0, phase=0.05, nominal_bounds=sheet.BoundingBox(radius=0.5+0.27083+0.375+0.2)) for l in center_polarities: for opponent in opponent_types: s=e+opponent+' LGN'+l if not p.gain_control: topo.sim[s]=sheet.CFSheet( nominal_density=p.lgn_density,row_precedence=0.6 if l=='On' else 0.7, nominal_bounds=sheet.BoundingBox(radius=p.area/2.0+0.25+0.5), output_fns=[transferfn.PiecewiseLinear(lower_bound=0.0,upper_bound=1.0)], measure_maps=False) else: # Add lateral connections for divisive normalization topo.sim[s]=sheet.optimized.SettlingCFSheet_Opt( nominal_density=p.lgn_density,row_precedence=0.6 if l=='On' else 0.7, nominal_bounds=sheet.BoundingBox(radius=p.area/2.0+0.25+0.5), output_fns=[transferfn.misc.HalfRectify()], tsettle=2,strict_tsettle=1, measure_maps=False) lgn_surroundg = pattern.Gaussian(size=0.25,aspect_ratio=1.0, output_fns=[transferfn.DivisiveNormalizeL1()]) topo.sim.connect(s,s,delay=0.05,name='LateralGC', dest_port=('Activity'), activity_group=(0.6,DivideWithConstant(c=0.11)), connection_type=projection.SharedWeightCFProjection, strength=0.6,weights_generator=lgn_surroundg, nominal_bounds_template=sheet.BoundingBox(radius=0.25)) ### Sheets topo.sim['V1'] = sheet.SettlingCFSheet(nominal_density=p.cortex_density, tsettle=16, plastic=True, nominal_bounds=sheet.BoundingBox(radius=p.area/2.0), output_fns=[transferfn.misc.HomeostaticResponse()]) topo.sim['V1'].joint_norm_fn=topo.sheet.optimized.compute_joint_norm_totals_opt ### Connections # DoG weights for the LGN centerg = pattern.Gaussian(size=0.07385,aspect_ratio=1.0, output_fns=[transferfn.DivisiveNormalizeL1()]) surroundg = pattern.Gaussian(size=0.29540,aspect_ratio=1.0, output_fns=[transferfn.DivisiveNormalizeL1()]) for e in p.eyes: for l in center_polarities: # Connect photoreceptors to the LGN for cone in rg_cone_types: opp_sheet = rg_cone_types[rg_cone_types.index(cone)-1] topo.sim.connect(e+cone+'Retina', e+cone+'-'+opp_sheet+' LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**center_polarities.index(l), nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentCenter', weights_generator=centerg) topo.sim.connect(e+cone+'Retina', e+opp_sheet+'-'+cone+' LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**(1+center_polarities.index(l)), nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentSurround', weights_generator=surroundg) topo.sim.connect(e+cone+'Retina', e+'Blue-'+blue_opponent+' LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**(1+center_polarities.index(l))/2, nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentCenter'+cone, weights_generator=centerg) topo.sim.connect(e+'Blue'+'Retina', e+'Blue-'+blue_opponent+' LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**center_polarities.index(l), nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentCenter'+'Blue', weights_generator=centerg) for cone in p.cone_types: topo.sim.connect(e+cone+'Retina', e+'Luminosity LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**center_polarities.index(l)/3, nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentCenter'+cone, weights_generator=centerg) topo.sim.connect(e+cone+'Retina', e+'Luminosity LGN'+l, delay=0.05, connection_type=projection.SharedWeightCFProjection, strength=4.7*(-1)**(1+center_polarities.index(l))/3, nominal_bounds_template=sheet.BoundingBox(radius=0.375), name='AfferentSurround'+cone, weights_generator=surroundg) # Connect LGN to V1 for opponent in opponent_types: # Determine strength per channel from how many luminosity and color channels there are num_tot=len(center_polarities)*len(opponent_types) num_lum=len(center_polarities) num_col=num_tot-num_lum color_scale=((1.0*num_tot/num_lum*(1.0-p.color_strength)) if opponent=="Luminosity" else (1.0*num_tot/num_col*p.color_strength)) topo.sim.connect(e+opponent+' LGN'+l, 'V1', delay=0.05, dest_port=('Activity', 'JointNormalize', 'Afferent'), connection_type=projection.CFProjection, strength=p.aff_strength*color_scale*(1.0 if not p.gain_control else 1.5), name=e+opponent+' LGN'+l+'Afferent', weights_generator=pattern.random.GaussianCloud(gaussian_size=2*0.27083), nominal_bounds_template=sheet.BoundingBox(radius=0.27083), learning_rate=p.aff_lr) topo.sim.connect( 'V1','V1',delay=0.05,strength=p.exc_strength,name='LateralExcitatory', connection_type=projection.CFProjection,learning_rate=p.exc_lr, nominal_bounds_template=sheet.BoundingBox(radius=0.104), weights_generator=pattern.Gaussian(aspect_ratio=1.0, size=0.05)) topo.sim.connect( 'V1','V1',delay=0.05,strength=-1.0*p.inh_strength,name='LateralInhibitory', connection_type=projection.CFProjection,learning_rate=p.inh_lr, nominal_bounds_template=sheet.BoundingBox(radius=0.22917), weights_generator=pattern.random.GaussianCloud(gaussian_size=0.15)) # default locations for model editor vs = [None]*(num_aff-1) + ['V1'] + [None]*(num_aff) ls=[] for e in p.eyes: for opponent in opponent_types: for l in center_polarities: ls += [e+opponent+' LGN'+l]+[None] es=[] for e in p.eyes: for cone in p.cone_types: es += [None]*(len(center_polarities)/2) + [e+cone+'Retina',None] + \ [None]*(len(center_polarities)/2) topo.sim.grid_layout([vs,ls,es], xstep=70/len(p.eyes), ystep=200, item_scale=0.6/len(p.eyes)) ## Create appropriate combined plots from topo.plotting.plotgroup import create_plotgroup from topo.command.pylabplot import overlaid_plots create_plotgroup(name='Orientation and Hue Preference', doc='Plot the orientation preference overlaid with hue preference boundaries.', pre_plot_hooks=[],category="Combined Preference Maps", plot_hooks=[overlaid_plots.instance(plot_template=[{"Hue":"OrientationPreference","Confidence":"OrientationSelectivity"},{"Strength":"OrientationSelectivity"}],overlay=[("contours","HuePreference",0.9,"red"),("contours","HuePreference",0.4,"green")],normalize=True)], normalize='Individually'), ### Set up appropriate defaults for analysis # Measure feature maps based on unthresholded initial response for # speed and reliability from topo.analysis.featureresponses import MeasureResponseCommand MeasureResponseCommand.durations=[0.225] MeasureResponseCommand.pattern_response_fn.apply_output_fns=False # Measure hue and orientation preference in run_batch by default import topo.command topo.command.default_analysis_plotgroups=["Hue Preference","Orientation Preference","Orientation and Hue Preference","Activity"]