#!/usr/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright (c) 2012 Michael Hull.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------
#from morphforge.morphology.core import MorphPath
from morphforge.core import LocMgr
from morphforge.morphology.visitor import SectionIndexerDF
#from morphforge.simulation.neuron.objects.neuronrecordable import NEURONRecordableOnLocation
from random import randint
import pylab
[docs]class SummariserOptions(object):
[docs] def __init__(self):
self.include_overview = True
self.include_overview_neuron_to_neuron_diagram = True
self.include_overview_individual_neuron_table = True
self.include_overview_individual_synapse_table = True
self.include_overview_individual_stimuli = True
self.include_details = True
self.include_details_indvidual_neuron = True
self.include_details_indvidual_neuron_morphology = True
self.include_details_indvidual_neuron_morphology_mpl = True
"""
DocumentLayout:
Root:
/Simulation Overview
/Overview Diagram
/Input Summary
/KeyTraces
/Details:
/Entire Population1
/Cells:
/Population1
/Cell1, Cell2
/Population2
/Cell1, Cell2
/Chemical Synapses:
All Details: (By type)
By-Presynaptic
By-Post Synaptic
/Gap Junctions
All Details
/Stimulations
/Current Clamps
/Voltage Clamps
/Channel Dynamics
/Synaptic Dynamics
/Platform Information
"""
from morphforge.simulation.base import Simulation
# from morphforge.management import PluginMgr
try:
import mredoc as mrd
except ImportError:
print 'Unable to import mredoc, you will be unable to produce pdf/html summaries'
[docs]class SummariserObject(object):
@classmethod
[docs] def build(cls, obj):
raise NotImplementedError()
[docs]class SummariserLibrary(object):
summarisers = {}
@classmethod
[docs] def register_summariser(cls, channel_baseclass, summariser_class):
# Add it to the dictionary of summarisers:
cls.summarisers[channel_baseclass] = summariser_class
@classmethod
[docs] def get_summarisier(cls, obj):
possible_summarisers = []
for (ChlType, summarisier) in cls.summarisers.iteritems():
if issubclass(type(obj), ChlType):
possible_summarisers.append(summarisier)
if len(possible_summarisers) == 0:
return None
if len(possible_summarisers) == 1:
return possible_summarisers[0]
else:
assert False, 'I have to many options for summarising: ' \
+ str(obj)
[docs]class _DotSummaryUtils(object):
@classmethod
[docs] def save_dot(cls, graph, format, **kwargs):
from morphforge.core import ObjectLabeller
name = ObjectLabeller.get_next_unamed_object_name(type(graph))
tmp_dir = LocMgr.get_tmp_path()
fname = '%s/dotout_%s.%s' % (tmp_dir, name, format)
graph.write_pdf(fname, **kwargs)
return fname
[docs]class SimulationMRedoc(object):
@classmethod
[docs] def build(cls, obj, options=None):
from morphforge.simulation.base import SimulationResult
from morphforge.stdimports import TagViewer
result = None
if isinstance( obj, SimulationResult):
result = obj
obj = obj.simulation
elif isinstance( obj, Simulation):
pass
elif isinstance(list):
assert False
else:
assert False, "Unexpected object passed to SimulationMRedoc: %s" %(obj)
if options is None:
options = SummariserOptions()
sim_redoc = SimulationMRedoc(obj, options=options).mredoc
if result is None:
return sim_redoc
else:
return mrd.Section('Simulation Summary',
sim_redoc,
mrd.Section("Results",
mrd.Image( TagViewer(result).fig.fig, fig_size=(6,3), max_font_size=7, subplots_adjust={'left':0.25,'right':0.95}),
)
)
[docs] def __init__(self, obj, options):
assert isinstance(obj, Simulation)
self.sim = obj
self.options = options
self.mredoc = self.build_simulation()
[docs] def build_simulation(self):
title = 'Simulation Summary: %s'%self.sim._sim_desc_str()
return mrd.Section(title,
mrd.TableOfContents(),
self.build_simulation_overview(),
self.build_simulation_details(),
#PluginMgr.summarise_all(),
)
[docs] def build_simulation_overview(self):
return mrd.SectionNewPage('Simulation Overview',
self.build_population_overview(),
self.build_population_complete_dot(),
self.build_singlecell_overview())
[docs] def build_simulation_details(self):
return mrd.SectionNewPage("Simulation Details",
self.build_singlecell_details(),
self.build_population_details(),
self.build_details_channels(),
self.build_details_synaptic_templates(),
)
# Overview in terms of populations
# -------------------------------------
# The details of the simulation:
[docs] def build_population_overview(self):
if not self.sim.neuron_populations:
return None
table = mrd.VerticalColTable(
"Population | Size | Type """,
[(pop.pop_name, len(pop), ",".join(pop.cell_types)) for pop in self.sim.neuron_populations]
)
table2 = mrd.VerticalColTable(
"Population | Size | Type """,
[(pop.synapse_pop_name, len(pop), ",".join(pop.synapse_types)) for pop in self.sim.synapse_populations]
)
return mrd.Section("Population Overview",
table, table2,
#self.build_population_overview_dot(),
self.build_population_complete_dot()
)
[docs] def build_population_complete_dot(self):
return DOTWriter(self.sim).build_population_complete_dot()
@classmethod
[docs] def _build_population_cell_table(cls, population):
return cls._build_cell_table(cell_list=population)
@classmethod
[docs] def _build_cell_table(cls, cell_list):
table = mrd.VerticalColTable('Name|Type|SA(um2)|\#sections/segments|Regions(SA(um2):nseg)|\#Pre/Post-Synapse|\#GapJunctions|Chls',
[(cell.name,
cell.cell_type_str,
"%.0f" % (cell.morphology.surface_area),
"%d:%d" % (len(cell.morphology), cell.segmenter.get_num_segment_total(cell)),
" ".join(["%s(%d:%d)" % (rgn.name, rgn.surface_area, cell.segmenter.get_num_segment_region(rgn)) for rgn in cell.morphology.regions]),
"%d %d" % (len(cell.presynaptic_connections), len(cell.postsynaptic_connections)),
"%d" % len(cell.electrical_connections),
" ".join([chl.name for chl in cell.biophysics.get_all_channels_applied_to_cell()]),
) for cell in cell_list])
return table
@classmethod
[docs] def _build_synapse_table(cls, synapses_list):
#for syn in synapses_list:
# print syn.name
# print syn.get_trigger().get_summary_string()
# print syn.get_postsynaptic_mechanism().cell_location.get_location_description_str()
# print syn.get_postsynaptic_mechanism()
# print syn.__dict__
##assert False
table = mrd.VerticalColTable('Name|Type|Trigger| PostSynaptic Cell|Receptor',
[(syn.name,
'<?>', #syn.syn_type_str,
'%s'%( syn.get_trigger().get_summary_string() ),
'%s'%(syn.get_postsynaptic_mechanism().cell_location.get_location_description_str() ),
'%s'%(syn.get_postsynaptic_mechanism().get_summary_description() ),
#'None',
) for syn in synapses_list])
return table
[docs] def build_population_details(self):
return mrd.Section('Population Details:',
*[self._build_population_details(pop) for pop in self.sim.neuron_populations]
)
[docs] def _build_population_details(self, pop):
return mrd.Section('Population: %s' % pop.pop_name,
self._build_population_cell_table(pop),
*[self.build_neuron_details(nrn) for nrn in pop]
)
# --------------------------------------------------------
# Single Cell Overview:
# --------------------------------------------------------
# The details of the simulation:
[docs] def build_singlecell_overview(self):
if self.sim.are_all_cells_in_pops:
return None
return mrd.HierachyScope(
self._build_singlecell_overview_cells(),
self._build_singlecell_overview_synapses(),
self._build_singlecell_overview_iclamps(),
self._build_singlecell_overview_vclamps())
[docs] def _build_singlecell_overview_cells(self):
return mrd.Section('Individual Cells',
self._build_cell_table(cell_list=self.sim.cells))
[docs] def _build_singlecell_overview_synapses(self):
return mrd.Section('Individual Synapses',
self._build_synapse_table(synapses_list=self.sim.synapses)
)
# Stim Tables:
[docs] def _build_singlecell_overview_stimtable(self, stims):
data = [(stim.name,
stim.location_summary_str,
stim.get_summary_description(),
) for stim in stims]
tbl = mrd.VerticalColTable('Name|Location|Description', data)
return tbl
[docs] def _build_singlecell_overview_iclamps(self):
return mrd.Section('Current Clamps',
self._build_singlecell_overview_stimtable(stims=self.sim.current_clamps))
[docs] def _build_singlecell_overview_vclamps(self):
return mrd.Section('Voltage Clamps',
self._build_singlecell_overview_stimtable(stims=self.sim.voltage_clamps))
[docs] def build_singlecell_details(self):
sub_sections = [self.build_neuron_details(nrn) for nrn in self.sim.cells]
return mrd.Section('Single Cell Details', *sub_sections)
@classmethod
[docs] def _build_details_channel(cls, chl):
sumcls = SummariserLibrary.get_summarisier(chl)
if not sumcls:
return mrd.Section('Summary of channel: %s <!! Summariser Missing !!>' % chl.name,
mrd.Paragraph('<Summariser Missing for type: %s>' % type(chl))
)
return sumcls.build(chl)
[docs] def build_details_channels(self):
channels = sorted( self.sim.get_all_channels(), key=lambda i: i.name)
return mrd.SectionNewPage('Channels Details',
*[ self._build_details_channel(chl) for chl in channels]
)
[docs] def _build_details_synaptic_templ(self,syn_tmpl):
#return 'Blah: %s' % syn.name
sumcls = SummariserLibrary.get_summarisier(syn_tmpl)
if not sumcls:
return mrd.Section('Summary of synaptic-template: %s <!! Summariser Missing !!>' % syn_tmpl.name,
mrd.Paragraph('<Summariser Missing for type: %s>' % type(syn_tmpl))
)
return sumcls.build(syn_tmpl)
[docs] def build_details_synaptic_templates(self):
synaptic_templates = self.sim.postsynaptic_templates
return mrd.SectionNewPage('Synaptic Template Details',
*[ self._build_details_synaptic_templ(syntemplate) for syntemplate in synaptic_templates]
)
# Individual Neuron details:
# -------------------------------
[docs] def _create_neuron_details_1_morphology(self, nrn):
morph = nrn.morphology
section_indexer = SectionIndexerDF(morph)
section_table = mrd.VerticalColTable(
'ID|Tags|Lateral Surface Area (um2)|Region|nseg|L|diam (prox/dist)',
[( section_indexer[sec],
sec.idtag,
'%.0f' % sec.area,
(sec.region.name if sec.region else ''),
nrn.cell_segmenter.get_num_segments(sec),
sec.length,
'%.1f/%.1f' % (sec.p_r * 2., sec.d_r * 2.)
) for sec in morph],
caption='%s:Morphology (Sections)' % nrn.name)
region_table = mrd.VerticalColTable(
'Region|Surface Area|\#Sections',
[(rgn.name, rgn.surface_area, len(rgn)) for rgn in nrn.morphology.regions],
caption='%s:Morphology (Regions)' % nrn.name
)
child_sections = [section_table, region_table]
# Include a picture with matplotlib?
if self.options.include_details_indvidual_neuron_morphology_mpl:
from morphforge.morphology.ui import MatPlotLibViewer
fig = MatPlotLibViewer(nrn.morphology, fig_kwargs={'figsize':(7, 7)}).fig
child_sections.append( mrd.Image(fig) )
return mrd.HierachyScope(*child_sections) #section_table, region_table, mrd.Image(fig), )
[docs] def _create_neuron_details_2b_pta(self, nrn):
passives = nrn.biophysics.get_applied_passives()
return mrd.VerticalColTable(
'PassiveProp|Priority|Targetter|Value',
[ (pta.passiveproperty,
pta.targetter.get_priority(),
pta.targetter.get_description(),
str(pta.value),
) for pta in passives],
caption='%s:Passive Properties' % nrn.name)
[docs] def _create_neuron_details_2_mta(self, nrn):
channels = nrn.biophysics.get_applied_mtas()
return mrd.VerticalColTable(
'Mechanism|Priority|Targetter|Applicator',
[ ( '%s ' % (mta.channel.name, ),
mta.targetter.get_priority(),
mta.targetter.get_description(),
mta.applicator.get_description(),
) for mta in channels],
caption='%s:Channels' % nrn.name)
[docs] def _create_neuron_details_3a_presynapses(self, nrn):
return mrd.VerticalColTable('Type|Distance From Soma', [],
caption='%s:Presynaptic Connections'
% nrn.name)
[docs] def _create_neuron_details_3b_postsynapses(self, nrn):
return mrd.VerticalColTable('Type|Distance From Soma', [],
caption='%s:Postsynaptic Connections'
% nrn.name)
[docs] def _create_neuron_details_3c_gapjunctions(self, nrn):
return mrd.VerticalColTable('Type|Distance From Soma', [],
caption='%s:Gap Junctions'
% nrn.name)
[docs] def _create_neuron_details_4_stimulation(self, nrn):
return mrd.VerticalColTable('Type|Distance From Soma', [],
caption='%s:Stimulation' % nrn.name)
[docs] def build_neuron_details(self, neuron):
#return mrd.SectionNewPage(
return mrd.Section(
'Neuron:%s' % neuron.name,
self._create_neuron_details_1_morphology(neuron),
self._create_neuron_details_2b_pta(neuron),
self._create_neuron_details_2_mta(neuron),
self._create_neuron_details_3a_presynapses(neuron),
self._create_neuron_details_3b_postsynapses(neuron),
self._create_neuron_details_3c_gapjunctions(neuron),
self._create_neuron_details_4_stimulation(neuron),
)
# -------------------------------
[docs]def build_connectivity_graph(synapse_pop, size=0.75):
import pylab
from matplotlib.ticker import MaxNLocator
prepop = synapse_pop.presynaptic_population
#if prepop:
# prepop_lut = prepop.build_cell_to_index_lut()
postpop = synapse_pop.postsynaptic_population
#postpop_lut = postpop.build_cell_to_index_lut()
connectivity = list()
for syn in synapse_pop:
if prepop:
pre_index = syn.get_presynaptic_cell().index_in_pop
else:
pre_index = 0
post_index = syn.get_postsynaptic_cell().index_in_pop
connectivity.append((pre_index, post_index))
prepop_len = (len(prepop) if prepop else 1)
postpop_len = len(postpop)
max_len = max( (prepop_len, postpop_len) )
figsize_raw =(size * (float(prepop_len)/max_len), size*(float(postpop_len)/max_len))
figsize = figsize_raw #figsize_raw[0]+0.75, figsize_raw[1]+0.75
print figsize
fig = pylab.figure(figsize=figsize, dpi=400 )
#ax = fig.add_subplot(1, 1, 1, aspect='equal')
ax = fig.add_axes([0, 0, 1, 1], aspect='equal')
xpts, ypts = zip(*connectivity)
ax.scatter(xpts, ypts, marker='s', s=7, edgecolors='none')
#ax.axis('equal')
ax.set_xlim(-0.5, prepop_len-0.5 )
ax.set_ylim(-0.5, postpop_len-0.5 )
ax.xaxis.set_major_locator(MaxNLocator(min(prepop_len, 3)))
ax.yaxis.set_major_locator(MaxNLocator(min(postpop_len, 3)))
ax.axes.get_xaxis().set_ticklabels([])
ax.axes.get_yaxis().set_ticklabels([])
#pylab.suptitle('Connectivity: %d synapses'%len(synapse_pop))
#pylab.show()
return fig
[docs]class DOTWriter(object):
[docs] def __init__(self, sim):
self.sim = sim
[docs] def build_population_complete_dot(self):
fig_count = 0
fig_out = '/tmp/dotimages/'
import pydot
graph = pydot.Dot('graphname', graph_type='digraph', size='7,7' , ratio='compress', compound='true', splines='true', sep='0.3' )
size = '0.55'
fontsize = '6'
kwargs_general = {
'fontsize': fontsize,
'fixedsize': 'True',
'width': size,
'height': size,
'fontname':'Helvetica'
}
cell_size='0.25'
kwargs_cell = { 'shape':'circle', 'fillcolor':'#80b3ffff', 'color':'#0066ffff', 'style':'filled', 'penwidth':'1', 'width':cell_size, 'height':cell_size }
kwargs_cc = {'shape':'circle', 'style':'filled', 'width':'0.05', }
kwargs_pop = {'style':'filled', 'color':'lightgrey', 'nodesep':'100' }
kwargs_synpop = {'shape':'none', 'fixedsize':'false' }
kwargs_synpop_img = {'shape':'square', 'labelloc':'b', 'scale':'false', 'fixedsize': 'true'}
kwargs_synpop_edge = {'penwidth':'3', 'color':'green', 'minlen':'50' }
kwargs_syn = { 'color':'darkgreen', 'penwidth':'1', 'arrowhead':'"tee"' }
# Map Simulation objects into dot objects:
obj2nodedict = {}
subgraphs = []
# Populations become subgraphs:
for population in self.sim.neuron_populations:
n = pydot.Cluster(population.pop_name, label=population.pop_name, **dict(kwargs_general.items() + kwargs_pop.items() ))
subgraphs.append(n)
obj2nodedict[population] = n
# Cells into Nodes
for cell in self.sim.cells:
n = pydot.Node(
cell.name,
label=cell.name if cell.population is None else '<%d>' % cell.index_in_pop,
**dict(kwargs_general.items()+ kwargs_cell.items())
)
obj2nodedict[cell] = n
if cell.population:
obj2nodedict[cell.population].add_node(n)
else:
graph.add_node(n)
for sg in subgraphs:
graph.add_subgraph(sg)
del subgraphs
# Synapse Populations are turned into a node, with edges from pre and
# to the post synaptic population:
for synpopindex, synpop in enumerate(self.sim.synapse_populations):
synpopcluster = pydot.Cluster('SynpopCluster'+synpop.synapse_pop_name)
# Create the connectivity graph:
connectivity_graph_figure = build_connectivity_graph(synpop)
fname = fig_out + '/synpop%d.png' % synpopindex
pylab.savefig(fname, dpi=400, bb_inches='tight')
n = pydot.Node(synpop.synapse_pop_name+'im', label='', image=fname, **dict(kwargs_general.items() + kwargs_synpop_img.items()))
synpopcluster.add_node(n)
label=''
label+= synpop.synapse_pop_name
len_prepop = len(synpop.presynaptic_population) if synpop.presynaptic_population else 1
pc_conn = 100. * len(synpop) / (len_prepop * len(synpop.postsynaptic_population))
#print pc_conn
#pc_conn=50.
#label+= '\\nType: %s'% (synpop.type)
label+= '\\nSynapses: %d (%d%%)'% (len(synpop), pc_conn )
#label= synpop.synapse_pop_name
n = pydot.Node(synpop.synapse_pop_name+'cap', label='"%s"'%label, **dict(kwargs_general.items() + kwargs_synpop.items()))
synpopcluster.add_node(n)
obj2nodedict[synpop] = synpopcluster
graph.add_subgraph(synpopcluster)
# Connect to pre- and post- synaptic pops:
post_pop = synpop.postsynaptic_population
e = pydot.Edge(synpopcluster.get_name(), obj2nodedict[post_pop].get_name(), **dict(kwargs_general.items() + kwargs_synpop_edge.items() ))
graph.add_edge(e)
pre_pop = synpop.presynaptic_population
if pre_pop is not None:
e = pydot.Edge( obj2nodedict[pre_pop].get_name(), synpopcluster.get_name(), **dict(kwargs_general.items() + kwargs_synpop_edge.items() ))
graph.add_edge(e)
else:
print 'NONE'
for (i, synapse) in enumerate(self.sim.synapses):
if synapse.population:
continue
pre_cell = synapse.get_presynaptic_cell()
post_cell = synapse.get_postsynaptic_cell()
if not pre_cell:
pre_n = pydot.Node(name='SpikeTimes%d' % i,
shape='point', color='lightsalmon',
style='filled', **kwargs_general)
graph.add_node(pre_n)
else:
pre_n = obj2nodedict[pre_cell]
post_n = obj2nodedict[post_cell]
syn_name = '%s' % synapse.name
e = pydot.Edge(pre_n, post_n, label=syn_name,
**dict(kwargs_general.items() + kwargs_syn.items() ) )
graph.add_edge(e)
stims = {}
# Simulations:
for cclamp in self.sim.current_clamps:
label = '"IClamp: %s\\n %s"' % (cclamp.name,
cclamp.location_summary_dot_str)
n = pydot.Node(
cclamp.name,
label=label,
**dict(kwargs_general.items()+ kwargs_cc.items())
)
stims[cclamp] = n
graph.add_node(n)
# Make the edge:
cell_node = obj2nodedict[cclamp.cell]
e = pydot.Edge(n, cell_node, label='', color='red', arrowhead='vee', arrowsize='0.75') # **kwargs)
graph.add_edge(e)
## Records:
#records = {}
#for record in self.sim.recordables:
# name = record.name
# # label = '"Record: %s\\n %s"' % (name, record.location_summary_dot_str )
# label = '"Record: %s\\n"' % name # , record.location_summary_dot_str )
# n = pydot.Node(
# name,
# shape='circle',
# style='filled',
# width='0.05',
# fixedsize='True',
# label=label,
# fontsize=fontsize,
# )
# records[record] = n
# graph.add_node(n)
# # Make the edge:
# if isinstance(record, NEURONRecordableOnLocation):
# post_node = obj2nodedict[record.cell_location.cell]
# e = pydot.Edge(n, post_node, label='', color='green')
# graph.add_edge(e)
# # cell_node = obj2nodedict[record.cell_location.cell]
graph.write_raw('example_cluster2.dot')
# Put the stimulations on:
#fname = _DotSummaryUtils.save_dot(graph, prog='dot')
fname = _DotSummaryUtils.save_dot(graph, format='pdf', prog='fdp')
#fname = _DotSummaryUtils.save_dot(graph, prog='osage')
#fname = _DotSummaryUtils.save_dot(graph, prog='neato')
return mrd.Section(
'Diagram Overview',
mrd.Figure(mrd.Image(fname))
)