import topo
import param
import imagen
from topo import projection, responsefn, learningfn, transferfn, sheet
import topo.learningfn.optimized
import topo.transferfn.optimized
import topo.responsefn.optimized
import topo.sheet.optimized
import topo.transferfn.misc
from topo.submodel import Model, ArraySpec, order_projections # pyflakes:ignore (API import)
from topo.submodel.earlyvision import EarlyVisionModel
@Model.definition
class ModelGCAL(EarlyVisionModel):
cortex_density=param.Number(default=47.0,bounds=(0,None),
inclusive_bounds=(False,True),doc="""
The nominal_density to use for V1.""")
homeostasis = param.Boolean(default=True, doc="""
Whether or not the homeostatic adaption should be applied in V1""")
t_init = param.Number(default=0.15, doc="""
The initial V1 threshold value. This value is static in the L and GCL models
and adaptive in the AL and GCAL models.""")
t_settle = param.Integer(default=16, doc="""
Number of settling steps before applying a reset in the V1 sheet.""")
target_activity = param.Number(default=0.024,doc="""
The target average activity for the homeostatic threshold mechanism.""")
latexc_radius=param.Number(default=0.104,bounds=(0,None),doc="""
Radius of the lateral excitatory bounds within V1.""")
latinh_radius=param.Number(default=0.22917,bounds=(0,None),doc="""
Radius of the lateral inhibitory bounds within V1.""")
latexc_size=param.Number(default=0.05,bounds=(0,None),doc="""
Size of the lateral excitatory connections within V1.""")
latinh_size=param.Number(default=0.15,bounds=(0,None),doc="""
Size of the lateral inhibitory connections within V1.""")
aff_lr=param.Number(default=0.1,bounds=(0.0,None),doc="""
Learning rate for the afferent projection(s) 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.""")
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.""")
expand_sf_test_range=param.Boolean(default=False,doc="""
By default, measure_sine_pref() measures SF at the sizes of RF
used, for speed, but if expand_sf_test_range is True, it will
test over a larger range, including half the size of the
smallest and twice the size of the largest.""")
def property_setup(self, properties):
properties = super(ModelGCAL, self).property_setup(properties)
"Specify weight initialization, response function, and learning function"
projection.CFProjection.cf_shape=imagen.Disk(smoothing=0.0)
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()
return properties
def sheet_setup(self):
sheets = super(ModelGCAL,self).sheet_setup()
sheets['V1'] = [{}]
return sheets
@Model.SettlingCFSheet
def V1(self, properties):
return Model.SettlingCFSheet.params(
tsettle=self.t_settle,
plastic=True,
joint_norm_fn=topo.sheet.optimized.compute_joint_norm_totals_opt,
output_fns=[transferfn.misc.HomeostaticResponse(t_init=self.t_init,
target_activity = self.target_activity,
learning_rate=0.01 if self.homeostasis else 0.0)],
nominal_density=self.cortex_density,
nominal_bounds=sheet.BoundingBox(radius=self.area/2.0))
@Model.matchconditions('V1', 'V1_afferent')
def V1_afferent_conditions(self, properties):
return {'level': 'LGN'}
@Model.CFProjection
def V1_afferent(self, src_properties, dest_properties):
sf_channel = src_properties['SF'] if 'SF' in src_properties else 1
# Adjust delays so same measurement protocol can be used with and without gain control.
LGN_V1_delay = 0.05 if self.gain_control else 0.10
name=''
if 'eye' in src_properties: name+=src_properties['eye']
if 'opponent' in src_properties:
name+=src_properties['opponent']+src_properties['surround']
name+=('LGN'+src_properties['polarity']+'Afferent')
if sf_channel>1: name+=('SF'+str(src_properties['SF']))
gaussian_size = 2.0 * self.v1aff_radius *self.sf_spacing**(sf_channel-1)
weights_generator = imagen.random.GaussianCloud(gaussian_size=gaussian_size)
return [Model.CFProjection.params(
delay=LGN_V1_delay+lag,
dest_port=('Activity','JointNormalize','Afferent'),
name= name if lag==0 else name+('Lag'+str(lag)),
learning_rate=self.aff_lr,
strength=self.aff_strength*(1.5 if self.gain_control else 1.0),
weights_generator=weights_generator,
nominal_bounds_template=sheet.BoundingBox(radius=
self.v1aff_radius*self.sf_spacing**(sf_channel-1)))
for lag in self['lags']]
@Model.matchconditions('V1', 'lateral_excitatory')
def lateral_excitatory_conditions(self, properties):
return {'level': 'V1'}
@Model.CFProjection
def lateral_excitatory(self, src_properties, dest_properties):
return Model.CFProjection.params(
delay=0.05,
name='LateralExcitatory',
weights_generator=imagen.Gaussian(aspect_ratio=1.0, size=self.latexc_size),
strength=self.exc_strength,
learning_rate=self.exc_lr,
nominal_bounds_template=sheet.BoundingBox(radius=self.latexc_radius))
@Model.matchconditions('V1', 'lateral_inhibitory')
def lateral_inhibitory_conditions(self, properties):
return {'level': 'V1'}
@Model.CFProjection
def lateral_inhibitory(self, src_properties, dest_properties):
return Model.CFProjection.params(
delay=0.05,
name='LateralInhibitory',
weights_generator=imagen.random.GaussianCloud(gaussian_size=self.latinh_size),
strength=-1.0*self.inh_strength,
learning_rate=self.inh_lr,
nominal_bounds_template=sheet.BoundingBox(radius=self.latinh_radius))
def analysis_setup(self):
# TODO: This is different in gcal.ty, stevens/gcal.ty and gcal_od.ty
# And depends whether gain control is used or not
import topo.analysis.featureresponses
topo.analysis.featureresponses.FeatureMaps.selectivity_multiplier=2.0
topo.analysis.featureresponses.FeatureCurveCommand.contrasts=[1, 10, 30, 50, 100]
if 'dr' in self.dims:
topo.analysis.featureresponses.MeasureResponseCommand.durations=[(max(self['lags'])+1)*1.0]
if 'sf' in self.dims:
from topo.analysis.command import measure_sine_pref
sf_relative_sizes = [self.sf_spacing**(sf_channel-1) for sf_channel in self['SF']]
wide_relative_sizes=[0.5*sf_relative_sizes[0]] + sf_relative_sizes + [2.0*sf_relative_sizes[-1]]
relative_sizes=(wide_relative_sizes if self.expand_sf_test_range else sf_relative_sizes)
#The default 2.4 spatial frequency value here is
#chosen because it results in a sine grating with bars whose
#width approximately matches the width of the Gaussian training
#patterns, and thus the typical width of an ON stripe in one of the
#receptive fields
measure_sine_pref.frequencies = [2.4*s for s in relative_sizes]
@Model.definition
[docs]class ExamplesGCAL(ModelGCAL):
"""
Reproduces the results of the legacy examples/gcal.ty file.
"""
def __init__(self, **params):
super(ExamplesGCAL, self).__init__(time_dependent=False, **params)
if set(self.dims) != set([ 'xy', 'or']):
raise Exception("ExamplesGCAL only reproducible for dims = ['xy', 'or']")
def setup(self,setup_options=True):
model = super(ExamplesGCAL, self).setup(setup_options)
if setup_options is True or 'sheets' in setup_options:
model.sheets.Retina.update(nominal_bounds=sheet.BoundingBox(radius=self.area/2.0+1.125))
model.sheets.LGNOn.update(nominal_bounds=sheet.BoundingBox(radius=self.area/2.0+0.75))
model.sheets.LGNOff.update(nominal_bounds=sheet.BoundingBox(radius=self.area/2.0+0.75))
model.sheets.V1.update(nominal_density=48)
if setup_options is True or 'projections' in setup_options:
order_projections(model, ['afferent',
'lateral_gain_control',
('V1_afferent', {'polarity':'On'}),
('V1_afferent', {'polarity':'Off'}),
'lateral_excitatory',
'lateral_inhibitory'])
return model