GCAL model with multiple maps

This tutorial shows how to use the GCAL model to explore the development of preferences for a wide variety of feature maps, including retinotopy, orientation, ocular dominance, disparity, motion direction, spatial frequency, and color. Each of these are called "dimensions", which are controlled by the dims parameter. For instance, dims=['xy','or'] works the same as the regular GCAL Orientation Map Tutorial, because it specifies that the only dimensions that will be varied on the input are xy (spatial position, leading to a retinotopic map) and or (orientation, leading to an orientation map). The other dimensions can be supplied similarly below. Throughout this tutorial, there will be sections that apply only to particular dimensions; these will be skipped automatically if that dimension has not been selected.

For some dimensions, there are very many different ways they could be modelled, both for the input patterns and for the neural model. For instance, the motion direction dimension (e.g. dims=['xy','or','dr']) uses oriented Gaussian patterns swept perpendicularly to their orientation axis, but many other types of motion exist and could be added as options. Similarly, at the neural level, there are many ways to allow neurons to have a preference for motion direction, but here by default it is done by adding multiple lagged projections to V1, such that V1 receives input from various different times in the past.

See Fischer (2014) for a discussion of the various dimensions supported.

Imports


In [ ]:
import datetime
start_time = datetime.datetime.now()

%reload_ext topo.misc.ipython
%view webm:5

In [ ]:
import topo
from topo.command import load_snapshot, save_snapshot, save_script_repr

from topo.analysis import Collector
from imagen.analysis import fft_power_spectrum
from featuremapper.analysis.pinwheels import PinwheelAnalysis
from featuremapper.analysis.hypercolumns import PowerSpectrumAnalysis

import holoviews
from holoviews import ViewMap, Points, Matrix
from holoviews.operation import vectorfield

from topo.submodel.color import ModelGCALColor

import time, os 
from holoviews.plotting import PlotSaver

Define the model


In [ ]:
# Uncomment to see all of the options available for use below
#%params ModelGCALColor

In [ ]:
simulations = { 
    # Parameters to match published simulations
    "fischer:ms14 fig6.1":      dict(dims=['xy','or'],cortex_density=141),
    "fischer:ms14 fig6.2-3":    dict(dims=['xy','or','od'],cortex_density=141),
    "fischer:ms14 fig6.5-6":    dict(dims=['xy','or','dy'],cortex_density=141),
    "fischer:ms14 fig6.7-8":    dict(dims=['xy','or','od','dy'],cortex_density=141),
    "fischer:ms14 fig6.9-11":   dict(dims=['xy','or','dr'],num_inputs=1,
                                     speed=3.0/24.0,aff_lr=0.1/4.0,
                                     aff_strength=1.5,cortex_density=141),

    "fischer:ms14 fig6.12-13":  dict(dims=['xy','or','cr'],dataset='FoliageA',
                                     strength_factor=0.75,color_strength=0.3,
                                     cone_scale=[0.80,0.75,0.80]),

    "fischer:ms14 fig6.14":     dict(dims=['xy','or','od','cr'],dataset='FoliageA',
                                     strength_factor=0.75,color_strength=0.3,
                                     cone_scale=[0.80,0.75,0.80]), # Estimated

    "fischer:ms14 fig6.15-16b": dict(dims=['xy','or','sf'],dataset='FoliageA'), # Estimated
    "fischer:ms14 fig6.16c":    dict(dims=['xy','or','sf'],dataset='FoliageA',
                                     sf_spacing=2.5,sf_channels=3,gain_control_SF=True),

    # Other potentially useful simulations
    "retinotopic map":          dict(dims=['xy']),
    "all maps":                 dict(dims=['xy','or','od','dy','cr','sf','dr'],dataset='FoliageA'),
    "all maps but sf":          dict(dims=['xy','or','od','dy','cr','dr']),
}

# Special parameters overriding those above, if desired, e.g. to reduce time and memory requirements
overrides = dict(cortex_density=48)# cortex_density was 63 for most fisher:ms14 simulations

# Choose desired simulation here
params = dict(simulations["fischer:ms14 fig6.7-8"],**overrides)

In [ ]:
topo.sim.model = ModelGCALColor(**params)

In [ ]:
gcal=topo.sim.model.setup()

In [ ]:
times=[10000] # Determines total duration of simulation, plus frequency of generating plots, e.g. [100,1000,10000]

Define plotting and analysis


In [ ]:
save_plots=False # Set this to true if you want .png files saved externally

In [ ]:
dim_or='or' in topo.sim.model.dims
dim_od='od' in topo.sim.model.dims
dim_dy='dy' in topo.sim.model.dims
dim_dr='dr' in topo.sim.model.dims
dim_sf='sf' in topo.sim.model.dims
dim_cr='cr' in topo.sim.model.dims

twoeyes = dim_od or dim_dy

In [ ]:
c = Collector()

# Projection activities
for proj in gcal.projections:
     if proj.dest.level=='V1' and proj.src.level=='LGN':
         c.set_path('Activity'+'.'+proj.parameters['name'],
                    c.collect(gcal.projections[('V1', proj.parameters['name'])]))

In [ ]:
# Preference map measurements
from topo.analysis.command import measure_or_pref, \
    measure_od_pref, measure_phasedisparity, measure_dr_pref, \
    measure_sine_pref, measure_hue_pref

if(dim_or and not dim_sf and not dim_dr): c.collect(measure_or_pref)
if(twoeyes): c.collect(measure_od_pref)
if(dim_dy):  c.collect(measure_phasedisparity)
if(dim_dr):  c.collect(measure_dr_pref)
if(dim_sf):  c.collect(measure_sine_pref)
if(dim_cr):  c.collect(measure_hue_pref)

In [ ]:
# Sheet activities
for sheet_item in gcal.sheets:
    if sheet_item.level=='Retina' or sheet_item.level=='V1':
        c.set_path('Activity'+'.'+str(sheet_item), c.collect(gcal.sheets[str(sheet_item)]))

for proj in gcal.projections:
     if proj.dest.level=='V1' and proj.src.level=='LGN':
         c.set_path('CFs'+'.'+proj.parameters['name'],
                    c.collect(gcal.projections[('V1', proj.parameters['name'])],grid=True))
            
# Analysis
c.FFTPowerSpectrum.V1 = c.analyze(c.ref.OrientationPreference.V1, fft_power_spectrum)
c.Pinwheels.V1 =        c.analyze(c.ref.OrientationPreference.V1
                                  * c.ref.OrientationSelectivity.V1, PinwheelAnalysis)
c.FFTAnalysis.V1 =      c.analyze(c.ref.Pinwheels.V1, PowerSpectrumAnalysis)

Example input patterns


In [ ]:
color_example = dim_cr and topo.sim.model.dataset!='Gaussian'

# Monochrome training patterns:
gcal.training_patterns.LeftRetina.anim(50) + \
gcal.training_patterns.RightRetina.anim(50) if twoeyes else \
gcal.training_patterns.Retina.anim(50) if not color_example else None

In [ ]:
# Color training patterns:
if color_example:
    eyes = [gcal.sheets.LeftRetina,gcal.sheets.RightRetina] if twoeyes else [gcal.sheets.Retina]
    animated_RGB = [None for e in eyes]
    for (e,eye) in enumerate(eyes):
          
        gen = eye.parameters['input_generator'].generators
    
        dataset_length=len(gen)
        R = [Matrix(gen[i].channels()[0], label='Red Channel'  ) for i in range(0,dataset_length)]
        G = [Matrix(gen[i].channels()[1], label='Green Channel') for i in range(0,dataset_length)]
        B = [Matrix(gen[i].channels()[2], label='Blue Channel' ) for i in range(0,dataset_length)]
        rgb_overlay=[R[i]*G[i]*B[i] for i in range(0,dataset_length)]
    
        animated_RGB[e] = ViewMap(dimensions=['Time'])
        for t in range(0,dataset_length):
            animated_RGB[e][t] = rgb_overlay[t]
    
    holoviews.core.options.channels['RGB_Image'] = \
    holoviews.operation.ChannelOpts('RGBA', 'Red Channel * Green Channel * Blue Channel')

sum(animated_RGB[1:],animated_RGB[0]) if color_example else None

Training the network (slow!)


In [ ]:
print("Collection will start at iteration %d and end on iteration %d" % (min(times), max(times)))

In [ ]:
data = c(times=times)

In [ ]:
str(datetime.datetime.now()-start_time) # Real time elapsed since starting this run

Results

Activity patterns


In [ ]:
data.Activity.V1

In [ ]:
data.Activity.grid().cols(4)

On-Off connection fields


In [ ]:
%view 150

In [ ]:
eyes = ['Left','Right'] if twoeyes else ['']
sfs = ['','SF2'] if dim_sf else ['']

on_minus_off = []
for (e,eye) in enumerate(eyes):
    for l in gcal['lags']:
        for (s,sf) in enumerate(sfs):
            luminosityname = "RedGreenBlueRedGreenBlue" if dim_cr and s==0 else ""
            plot=(data.CFs[eye+luminosityname+"LGNOnAfferent" +sf+("" if l==0 else "Lag"+str(l))] * 
                  data.CFs[eye+luminosityname+"LGNOffAfferent"+sf+("" if l==0 else "Lag"+str(l))])
            plot.title = (("" if l==0 else "Lag"+str(l)) +" "+sf + eye + ' ' + luminosityname + ' ON-OFF CFs').strip()
            on_minus_off += [plot]

defaultproj=0
num_lags=len(gcal['lags'])

on_minus_off[0] if len(on_minus_off)==1 else sum(on_minus_off[1:],on_minus_off[0]).cols(num_lags if num_lags>1 else 4)

Orientation map


In [ ]:
%view 150

In [ ]:
%%opts Points color='w' marker='*' s=100 edgecolors='k'
annotated_map = (data.OrientationPreference.V1 
                 * data.OrientationSelectivity.V1
                 * Points(on_minus_off[defaultproj][0,-0.3:0.3].keys())) if dim_or else None
annotated_map << on_minus_off[defaultproj][0,-0.3:0.3] << data.OrientationPreference.V1.hist(adjoin=False) if dim_or else None

Ocularity map


In [ ]:
%opts Preference_SheetView cmap="gray"
(data.OcularPreference.V1 << data.OcularPreference.V1.hist(adjoin=False)) + \
    (data.OcularSelectivity.V1 << data.OcularSelectivity.V1.hist(adjoin=False)) if twoeyes else None

In [ ]:
%%opts Level_Contours linewidths=5 color='k' Pinwheels_Points linewidth=2
pins = data.Pinwheels.V1.select(Duration=max(gcal['lags'])+1,Time=times[-1])[3] if dim_or else None
od_contours = holoviews.operation.contours(data.OcularPreference.V1). \
    select(Duration=max(gcal['lags'])+1,Time=times[-1])[1] if twoeyes else None
data.OrientationPreference.V1.select(Duration=max(gcal['lags'])+1,Time=times[-1]) * pins * od_contours if twoeyes and dim_or else None

Disparity map


In [ ]:
data.PhasedisparityPreference.V1 * data.PhasedisparitySelectivity.V1 << data.PhasedisparityPreference.V1.hist(adjoin=False) \
    if dim_dy else None

Direction map

Preference only


In [ ]:
data.DirectionPreference.V1 << data.DirectionPreference.V1.hist(adjoin=False) if dim_dr else None

Preference + Selectivity


In [ ]:
data.DirectionPreference.V1 * data.DirectionSelectivity.V1 << data.DirectionPreference.V1.hist(adjoin=False) \
    if dim_dr else None

In [ ]:
%view 300

In [ ]:
%%opts VectorField [color_dim='angle' normalize_individually=False] cmap='hsv' linewidth=1 edgecolors='k'
dir_vf = vectorfield(data.DirectionPreference.V1 * data.DirectionSelectivity.V1, rows=10, cols=10) if dim_dr else None

data.OrientationPreference.V1 * data.OrientationSelectivity.V1 * dir_vf << \
    data.OrientationPreference.V1.hist(adjoin=False) if dim_dr else None

Spatial frequency map


In [ ]:
%view 150

In [ ]:
data.FrequencyPreference.V1 * data.FrequencySelectivity.V1 << data.FrequencyPreference.V1.hist(adjoin=False) \
    if dim_sf else None

In [ ]:
%%opts FrequencyPreference_SheetView cmap='gray'
data.FrequencyPreference.V1 << data.FrequencyPreference.V1.hist(adjoin=False) \
    if dim_sf else None

Color map


In [ ]:
data.HuePreference.V1 * data.HueSelectivity.V1 << data.HuePreference.V1.hist(adjoin=False) \
    if dim_cr else None

Phase preference


In [ ]:
%view 100

In [ ]:
if dim_or:
    OR_HCS = data.OrientationPreference.V1 * data.OrientationSelectivity.V1
    phase_HCS = data.PhasePreference.V1 * data.PhaseSelectivity.V1
    top_row = OR_HCS + data.OrientationPreference.V1.hist() + data.OrientationSelectivity.V1.hist() 
    bottom_row =  phase_HCS + data.PhasePreference.V1.hist() + data.PhaseSelectivity.V1.hist()
(top_row + bottom_row).cols(3) if dim_or else None

Orientation pinwheels + FFT analysis


In [ ]:
%view 120

In [ ]:
if dim_or: 
    for t in times:
        data.FFTPowerSpectrum.V1.select(Duration=max(gcal['lags'])+1,Time=t) \
                .bounds=holoviews.core.boundingregion.BoundingBox(radius=20.0)
            
((data.Pinwheels.V1.hist(index=0) + data.FFTPowerSpectrum.V1.map(lambda x,k: x.roi)) + data.FFTAnalysis.V1).cols(2) if dim_or else None

Save output for external use


In [ ]:
notebook_suffix='/'
notebook_folder=''.join(topo.sim.model.dims).upper() + time.strftime("-%Y-%m-%d-%H%M")+notebook_suffix

In [ ]:
%opts Level_Contours color='k'
%opts Level_Contours linewidths=3


def save_plot(time,data_,filebase,selection={"Duration":max(gcal['lags'])+1},hist=True):
    """
    Save a plot of the given data_to a file whose name includes filebase.
    The selection can be anything accepted by .select.
    """
    dat = data_.select(Time=time,**selection) if hist==False else \
          data_.select(Time=time,**selection).hist(adjoin=True,index=0)
    PlotSaver(dat,filename=notebook_folder+filebase+str(time)+'.png')
    
    
def save_pref_map(name):
    """Save preference, selectivity, and pref+select plots for the given feature map"""
    save_plot(time,data[name+"Preference"].V1,name+'P_')
    save_plot(time,data[name+"Selectivity"].V1,name+'S_')
    save_plot(time,data[name+"Preference"].V1*data[name+"Selectivity"].V1,name+'PS_')
    
    
if save_plots:
    if not os.path.exists(notebook_folder): os.mkdir(notebook_folder)
    save_snapshot(notebook_folder+'snapshot.typ')
    save_script_repr(notebook_folder+'script_repr.ty')

    for time in times:
        if dim_or:
            save_pref_map('Orientation')
            save_pref_map('Phase')
            save_plot(time,data.Pinwheels.V1,'OrientationPinwheels_')
            save_plot(time,data.FFTPowerSpectrum.V1.roi,'OrientationFFTPower_',hist=False)        
            save_plot(time,data.FFTAnalysis.V1.get((0,0)),'OrientationFFTHist_',hist=False)
    
        if dim_dr:
            save_pref_map('Direction')
            save_plot(time,data.OrientationPreference.V1*data.OrientationSelectivity.V1*dir_vf,
                      'DirectionArrows_')
        if dim_od:
            save_plot(time,data.OrientationPreference.V1 * pins * od_contours,'Ocular_Contours')
    
        if twoeyes: save_pref_map('Ocular')
        if dim_dy:  save_pref_map('Disparity')
        if dim_cr:  save_pref_map('Hue')

In [ ]:
end_time = datetime.datetime.now()
str(end_time-start_time)

End of notebook