def plot_latent_compartment_state(t, z, state, compartment, axs=None, colors=['k'], linewidth=1): dtype = compartment.latent_dtype lb = compartment.latent_lb ub = compartment.latent_ub D = sz_dtype(dtype) z_comp = get_item_at_path(z, compartment.path) z = as_matrix(z_comp, D) z_names = extract_names_from_dtype(dtype) # Compute the channel currents s_comp = get_item_at_path(state, compartment.path) N_ch = len(compartment.channels) Is = [s_comp[ch.name]['I'] for ch in compartment.channels] # if fig is None: # fig,axs = plt.subplots(D,1) # else: # axs = fig.get_axes() # # Make sure axs is a list of axes, even if it is length 1 # if not isinstance(axs, (list, np.ndarray)): # axs = [axs] if axs is None: axs = [] for d in np.arange(D): ax = plt.subplot2grid((D,3), (d,0), colspan=2) axs.append(ax) ax = plt.subplot2grid((D,3), (0,2), rowspan=D) axs.append(ax) for d in np.arange(D): axs[d].plot(t, z[d,:], color=colors[d % len(colors)], linewidth=linewidth) axs[d].set_ylabel(z_names[d]) yl = list(axs[d].get_ylim()) if np.isfinite(lb[d]): yl[0] = lb[d] if np.isfinite(ub[d]): yl[1] = ub[d] axs[d].set_ylim(yl) # Plot the channel densities C = len(compartment.channels) gs = [ch.g.value for ch in compartment.channels] names = [ch.name for ch in compartment.channels] axs[-1].bar(np.arange(C), gs, facecolor=colors[0], alpha=0.5) axs[-1].set_xticks(np.arange(C)) axs[-1].set_xticklabels(map(lambda n: '$g_%s$' % n, names)) axs[-1].set_title('Channel densities') axs[-1].set_ylim([0,30]) # if not fig_given: # plt.show() return axs
def simple_logp(self, latent, observations): """ We want to compute the log probability of a set of observations given the latent trajectories up to time t. This is challenging (especially in our current particle MCMC framework, since we are not set up for observation models that depend on previous as well as current states), so for now we'll just do something simple as an approximation. :param latent: :param observations: :return: """ x_comp = utils.get_item_at_path(latent, self.compartment.path) latent_V = x_comp['V'] o = utils.get_item_at_path(observations, self.path) observed_V = o['V'] # Set the effective variance to be 'filterbins' times the variance # of the mean. This is motivated by thinking of each instantaneous # voltage measurement as an i.i.d. sample whose mean is the measured # fluorescence sigma_eff = np.sqrt(self.filterbins) * self.sigma.value logp = -0.5/sigma_eff**2 * (observed_V-latent_V)**2 return logp
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) n = x_ch['n'] # Compute the alpha and beta as a function of V alpha, beta = self.alpha_beta(x_comp) # Compute the channel state updates dxdt['n'] = alpha * (1.0-n) - beta*n return dxdt
def simple_logp(self, latent, observations): """ We want to compute the log probability of a set of observations given the latent trajectories up to time t. This is challenging (especially in our current particle MCMC framework, since we are not set up for observation models that depend on previous as well as current states), so for now we'll just do something simple as an approximation. :param latent: :param observations: :return: """ x_comp = utils.get_item_at_path(latent, self.compartment.path) latent_V = x_comp['V'] o = utils.get_item_at_path(observations, self.path) observed_V = o['V'] # Set the effective variance to be 'filterbins' times the variance # of the mean. This is motivated by thinking of each instantaneous # voltage measurement as an i.i.d. sample whose mean is the measured # fluorescence sigma_eff = np.sqrt(self.filterbins) * self.sigma.value logp = -0.5 / sigma_eff**2 * (observed_V - latent_V)**2 return logp
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ state = np.zeros(latent.shape, dtype=self.state_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) state['I'] = x_ch['a'] * x_ch['b'] * (x_comp['V'] - self.E.value) return state
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ state = np.zeros(len(latent), dtype=self.state_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) state['I'] = (x_ch['s'] ** 2) * x_ch['r'] * (x_comp['V'] - self.E.value) #print "x_ch: ", x_ch['s'] return state
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ state = np.zeros(latent.shape, dtype=self.state_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) #print "c: ", x_ch['c'] #print "Ca: ", x_comp['[Ca]'] / 250 #print "min: ", np.min(1, x_comp['[Ca]'] / 250) #print "ans: ", x_ch['c'] * np.min(1, x_comp['[Ca]'] / 250) * (x_comp['V'] - self.E.value) state['I'] = x_ch['c'] * np.minimum(1, x_comp['[Ca]'] / 250) * (x_comp['V'] - self.E.value) return state
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) s_comp = get_item_at_path(latent, self.path) i_comp = get_item_at_path(inpt, self.path) # To compute dV/dt we need the ionic current in this compartment I_ionic = 0 I_calcium = 0 for c in self.channels: s_ch = get_item_at_path(state, c.path) I_ionic += c.g.value * s_ch['I'] if c.moves_calcium: I_calcium += c.g.value * s_ch['I'] dxdt['V'] = -1.0/self.C.value * I_ionic # We model [Ca] as per page 83 of Traub 1994 dxdt['[Ca]'] = -1 * I_calcium * self.Phi - s_comp['[Ca]'] * self.Beta # TODO Figure out how to handle coupling with other compartments # dxdt['V'] += 1.0/self.neuron.C*self.neuron.W(k,:)*(V-Vk) # Add in driving current dxdt['V'] += 1.0/self.C.value * i_comp['I'] for c in self.channels: if not (c.latent_dtype is None or c.latent_dtype == []): tmp = c.kinetics(latent, inpt, state) dxdt[c.name] = tmp return dxdt
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) s_comp = get_item_at_path(latent, self.path) i_comp = get_item_at_path(inpt, self.path) # To compute dV/dt we need the ionic current in this compartment I_ionic = 0 I_calcium = 0 for c in self.channels: s_ch = get_item_at_path(state, c.path) I_ionic += c.g.value * s_ch['I'] if c.moves_calcium: I_calcium += c.g.value * s_ch['I'] dxdt['V'] = -1.0 / self.C.value * I_ionic # We model [Ca] as per page 83 of Traub 1994 dxdt['[Ca]'] = -1 * I_calcium * self.Phi - s_comp['[Ca]'] * self.Beta # TODO Figure out how to handle coupling with other compartments # dxdt['V'] += 1.0/self.neuron.C*self.neuron.W(k,:)*(V-Vk) # Add in driving current dxdt['V'] += 1.0 / self.C.value * i_comp['I'] for c in self.channels: if not (c.latent_dtype is None or c.latent_dtype == []): tmp = c.kinetics(latent, inpt, state) dxdt[c.name] = tmp return dxdt
def update(self, model, cache=None): """ TODO: Handle truncated Gaussians noise for channel params """ # Keep sufficient stats for a Gaussian noise model N = 0 beta_V_hat = 0 # beta_ch_hat = 0 # Get the latent voltages for ds in model.data_sequences: latent = as_matrix(ds.latent) # The transition model is a noisy Hodgkin Huxley proposal prop = TruncatedHodgkinHuxleyProposal( model.population, ds.t, ds.input, as_matrix(np.zeros(1, dtype=model.population.latent_dtype))) dt = ds.t[1:] - ds.t[:-1] # import pdb; pdb.set_trace() pred = np.zeros((latent.shape[0], ds.T - 1)) for t in np.arange(ds.T - 1): pt, _, _ = prop.sample_next(t, latent[:, t, np.newaxis], t + 1) pred[:, t] = pt.ravel() # pred2,_ = prop.sample_next(np.arange(ds.T-1), # latent[:,:-1], # np.arange(1,ds.T)) # if not np.allclose(pred, pred2): # import pdb; pdb.set_trace() for neuron in model.population.neurons: for compartment in neuron.compartments: V = get_item_at_path( as_sarray(latent, model.population.latent_dtype), compartment.path)['V'] Vpred = get_item_at_path( as_sarray(pred, model.population.latent_dtype), compartment.path)['V'] dV = (Vpred - V[1:]) / dt # Update sufficient stats N += len(dV) beta_V_hat += (dV**2).sum() # Sample a new beta_V sig2_V = 1.0 / np.random.gamma( hypers['a_sig_V'].value + N / 2., 1.0 / (hypers['b_sig_V'].value + beta_V_hat / 2.)) hypers['sig_V'].value = np.sqrt(sig2_V)
def sample(self, latent, noise=True): """ Sample an observable voltage given the latent state :param latent_state: :return: """ o = np.zeros(latent.shape, self.observed_dtype) x_comp = utils.get_item_at_path(latent, self.compartment.path) V = x_comp['V'] assert V.ndim == 1, "V should be 1d" # Reshape into chunks of size filterbins V2 = V.reshape((-1,self.filterbins,)) assert V2[0,1] == V[1], "Reshape failed!" # Compute the average Vlp = V2.sum(axis=1)/np.float(self.filterbins) if noise: # Add noise to the average Vlp += self.sigma.value * np.random.randn(*Vlp.shape) # Reshape into the size of V Vlp = np.repeat(Vlp, self.filterbins) Vlp = Vlp[:len(V)] o['V'] = Vlp return o
def logp(self, latent, observations): """ Compute the log probability of the observations given the latent states :param latent: :param observations: :return: """ x_comp = utils.get_item_at_path(latent, self.compartment.path) latent_V = x_comp['V'] o = utils.get_item_at_path(observations, self.path) observed_V = o['V'] logp = -0.5/self.sigma.value**2 * (observed_V-latent_V)**2 return logp
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ state = np.zeros(latent.shape, dtype=self.state_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) # Alert: Is this true? V = x_comp['V'] G = (10.6408 - 14.6408*np.exp(-V/42.7671)) / V gam = 0.1 state['I'] = G * (x_ch['O1'] + gam*x_ch['O2']) * (x_comp['V'] - self.E.value) return state
def logp(self, latent, observations): """ Compute the log probability of the observations given the latent states :param latent: :param observations: :return: """ x_comp = utils.get_item_at_path(latent, self.compartment.path) latent_V = x_comp['V'] o = utils.get_item_at_path(observations, self.path) observed_V = o['V'] logp = -0.5 / self.sigma.value**2 * (observed_V - latent_V)**2 return logp
def sample(self, latent, noise=True): """ Sample an observable voltage given the latent state :param latent_state: :return: """ o = np.zeros(latent.shape, self.observed_dtype) x_comp = utils.get_item_at_path(latent, self.compartment.path) V = x_comp['V'] assert V.ndim == 1, "V should be 1d" # Reshape into chunks of size filterbins V2 = V.reshape(( -1, self.filterbins, )) assert V2[0, 1] == V[1], "Reshape failed!" # Compute the average Vlp = V2.sum(axis=1) / np.float(self.filterbins) if noise: # Add noise to the average Vlp += self.sigma.value * np.random.randn(*Vlp.shape) # Reshape into the size of V Vlp = np.repeat(Vlp, self.filterbins) Vlp = Vlp[:len(V)] o['V'] = Vlp return o
def update(self, model, cache=None): """ TODO: Handle truncated Gaussians noise for channel params """ # Keep sufficient stats for a Gaussian noise model N = 0 beta_V_hat = 0 # beta_ch_hat = 0 # Get the latent voltages for ds in model.data_sequences: latent = as_matrix(ds.latent) # The transition model is a noisy Hodgkin Huxley proposal prop = TruncatedHodgkinHuxleyProposal(model.population, ds.t, ds.input, as_matrix(np.zeros(1, dtype=model.population.latent_dtype))) dt = ds.t[1:] - ds.t[:-1] # import pdb; pdb.set_trace() pred = np.zeros((latent.shape[0], ds.T-1)) for t in np.arange(ds.T-1): pt,_,_ = prop.sample_next(t, latent[:,t,np.newaxis], t+1) pred[:,t] = pt.ravel() # pred2,_ = prop.sample_next(np.arange(ds.T-1), # latent[:,:-1], # np.arange(1,ds.T)) # if not np.allclose(pred, pred2): # import pdb; pdb.set_trace() for neuron in model.population.neurons: for compartment in neuron.compartments: V = get_item_at_path(as_sarray(latent, model.population.latent_dtype), compartment.path)['V'] Vpred = get_item_at_path(as_sarray(pred, model.population.latent_dtype), compartment.path)['V'] dV = (Vpred - V[1:])/dt # Update sufficient stats N += len(dV) beta_V_hat += (dV**2).sum() # Sample a new beta_V sig2_V = 1.0/np.random.gamma(hypers['a_sig_V'].value + N/2., 1.0/(hypers['b_sig_V'].value + beta_V_hat/2.)) hypers['sig_V'].value = np.sqrt(sig2_V)
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ state = np.zeros(latent.shape, dtype=self.state_dtype) state['V'] = get_item_at_path(latent, self.path)['V'] for c in self.channels: state[c.name] = c.evaluate_state(latent, inpt) return state
def get_dV_and_currents(self, model): c = self.get_compartment(model) # Get the sub-structured arrays for this comp chs = c.channels dts = [] dVc_dts = [] Iscs = [] for data in model.data_sequences: t = data.t T = data.T inpt = data.input state = data.states s_comp = get_item_at_path(state, c.path) i_comp = get_item_at_path(inpt, c.path) Vc = s_comp['V'] dVc_dt = c.C.value * self.gradient_V(t, Vc) dt = np.concatenate((np.diff(t), np.array([t[-1] - t[-2]]))) # Some of this change is due to injected current dVc_dt -= i_comp['I'] dts.append(dt) dVc_dts.append(dVc_dt) # Now compute the per channel currents in this compartment Isc = np.zeros((T, 0)) for ch in chs: if s_comp[ch.name].dtype.names is not None and \ 'I' in s_comp[ch.name].dtype.names: Isc = np.concatenate( (Isc, -1.0 * s_comp[ch.name]['I'][:, np.newaxis]), axis=1) Iscs.append(Isc) # Concatenate the dVs and the currents together dt = np.concatenate(dts, axis=0) dVc_dt = np.concatenate(dVc_dts, axis=0) Isc = np.concatenate(Iscs, axis=0) return dVc_dt, Isc, dt
def get_dV_and_currents(self, model): c = self.get_compartment(model) # Get the sub-structured arrays for this comp chs = c.channels dts = [] dVc_dts = [] Iscs = [] for data in model.data_sequences: t = data.t T = data.T inpt = data.input state = data.states s_comp = get_item_at_path(state, c.path) i_comp = get_item_at_path(inpt, c.path) Vc = s_comp['V'] dVc_dt = c.C.value * self.gradient_V(t, Vc) dt = np.concatenate((np.diff(t), np.array([t[-1]-t[-2]]))) # Some of this change is due to injected current dVc_dt -= i_comp['I'] dts.append(dt) dVc_dts.append(dVc_dt) # Now compute the per channel currents in this compartment Isc = np.zeros((T,0)) for ch in chs: if s_comp[ch.name].dtype.names is not None and \ 'I' in s_comp[ch.name].dtype.names: Isc = np.concatenate((Isc, -1.0*s_comp[ch.name]['I'][:,np.newaxis]), axis=1) Iscs.append(Isc) # Concatenate the dVs and the currents together dt = np.concatenate(dts, axis=0) dVc_dt = np.concatenate(dVc_dts, axis=0) Isc = np.concatenate(Iscs, axis=0) return dVc_dt, Isc, dt
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) V = x_comp['V'] # Use resting potential of zero V_ref = V + 60 m = x_ch['m'] h = x_ch['h'] # Compute the alpha and beta as a function of V am1 = 0.32*(13.1-V_ref)/(exp((13.1-V_ref)/4)-1) ah1 = 0.128*exp((17.0-V_ref)/18.0) bm1 = 0.28*(V_ref-40.1)/(exp((V_ref-40.1)/5.0)-1.0) bh1 = 4.0/(1.0+exp((40.-V_ref)/5.0)) # Compute the channel state updates dxdt['m'] = am1*(1.-m) - bm1*m dxdt['h'] = ah1*(1.-h) - bh1*h return dxdt
def set_input(self, t, inpt): """ Set the current in each of the connected compartments to the stimulus value at time t :param t: :param inpt: :return: """ for c in self.compartments: c_inpt = get_item_at_path(inpt, c.path) c_inpt['Irr'] = self.stimuluspattern.intensity(t)
def sample(self, latent): """ Sample an observable voltage given the latent state :param latent_state: :return: """ o = np.zeros(latent.shape, self.observed_dtype) x_comp = utils.get_item_at_path(latent, self.compartment.path) V = x_comp['V'] o['V'] = V + self.sigma.value * np.random.randn(*V.shape) return o
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) V = x_comp['V'] # Offset to resting potential of 0 V_ref = 60 + V # Compute the alpha and beta as a function of V aa1 = 0.02*(13.1-V_ref)/(exp((13.1-V_ref)/10.)-1) ba1 = 0.0175*(V_ref-40.1)/(exp((V_ref-40.1)/10.)-1) # Inactivation variable b ab1 = 0.0016*exp((-13.0-V_ref)/18.0) bb1 = 0.05/(1+exp((10.1-V_ref)/5.0)) # Compute the channel state updates dxdt['a'] = aa1*(1-x_ch['a']) - ba1*x_ch['a'] dxdt['b'] = ab1*(1-x_ch['b']) - bb1*x_ch['b'] return dxdt
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) V = x_comp['V'] m = x_ch['m'] h = x_ch['h'] # Compute the alpha and beta as a function of V am1 = 0.1*(V+35.)/(1-exp(-(V+35.)/10.)) ah1 = 0.07*exp(-(V+50.)/20.) bm1 = 4.*exp(-(V+65.)/18.) bh1 = 1./(exp(-(V+35.)/10.)+1.) # Compute the channel state updates dxdt['m'] = am1*(1.-m) - bm1*m dxdt['h'] = ah1*(1.-h) - bh1*h return dxdt
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) V = x_comp['V'] c_Ca = x_comp['[Ca]'] q = x_ch['q'] # Offset to resting potential of 0 V_ref = 60 + V # % Compute the alpha and beta as a function of V aq1 = np.min((0.2e-4)*c_Ca, 0.01) bq1 = 0.001 # % Compute the channel state updates dxdt['q'] = aq1*(1-q) - bq1*q return dxdt
def evaluate_state(self, latent, inpt): """ Evaluate the state of this compartment """ N = len(latent) state = np.zeros(N, dtype=self.state_dtype) my_latent = get_item_at_path(latent, self.path) state['V'] = my_latent['V'] state['[Ca]'] = my_latent['[Ca]'] for c in self.channels: if c.latent_dtype is not None: state[c.name] = c.evaluate_state(latent, inpt) return state
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) V = x_comp['V'] # Offset to resting potential of 0 V_ref = 60 + V alpha = 1.6 / (1 + np.exp(-.072 * (V_ref - 65))) beta = .02 * (V_ref - 51.1) / (np.exp((V_ref - 51.1) / 5) - 1) r_alpha = np.exp(-V_ref / 20) / 200 r_alpha[V_ref <= 0] = .005 r_beta = 0.005 - r_alpha dxdt['s'] = alpha * (1 - x_ch['s']) - beta * x_ch['s'] dxdt['r'] = r_alpha * (1 - x_ch['r']) - r_beta * x_ch['r'] return dxdt
def get_my(self, inpt): if not self.name in inpt.dtype.names: return utils.get_item_at_path(inpt, self.compartment.path) else: return inpt[self.name]
def update(self, model, cache=None): """ Sample the conductances of the neuron given its latent state variables """ # c = self.compartment c = self.get_compartment(model) # Get the sub-structured arrays for this comp chs = c.channels # Get a list of conductances for this compartment gs = [ch.g for ch in chs] gsc = np.array([g.value for g in gs]) dVc_dts = [] Iscs = [] for data in model.data_sequences: t = data.t T = data.T inpt = data.input state = data.states s_comp = get_item_at_path(state, c.path) i_comp = get_item_at_path(inpt, c.path) Vc = s_comp['V'] dVc_dt = c.C.value * self.gradient_V(t, Vc) dt = np.concatenate((np.diff(t), np.array([t[-1]-t[-2]]))) # Some of this change is due to injected current dVc_dt -= i_comp['I'][:-1] dVc_dts.append(dVc_dt) # Now compute the per channel currents in this compartment Isc = np.zeros((T-1,0)) for ch in chs: if s_comp[ch.name].dtype.names is not None and \ 'I' in s_comp[ch.name].dtype.names: Isc = np.concatenate((Isc, -1.0*s_comp[ch.name]['I'][:-1,np.newaxis]), axis=1) Iscs.append(Isc) # Concatenate the dVs and the currents together dVc_dt = np.concatenate(dVc_dts, axis=0) Isc = np.concatenate(Iscs, axis=0) # Sample each conductance in turn given that # C*dVc_dt ~ N(I_in - np.dot(gsc, Isc), sig_V^2) for (i,ch) in enumerate(chs): i_rem = np.concatenate((np.arange(i), np.arange(i+1,len(chs)))) gsc_rem = gsc[i_rem] Isc_rem = Isc[:,i_rem] # prior = gamma(alphas[ch], scale=1.0/betas[ch]) prior = ch.g.distribution dV_resid = dVc_dt - np.dot(Isc_rem, gsc_rem).ravel() from scipy.stats import norm sig_V = np.asscalar(hypers['sig_V'].value) # lkhd = norm(dV_resid[:,np.newaxis], sig_V) lkhd = lambda g: (-0.5/sig_V**2 * (np.dot(Isc[:,i][:,np.newaxis], g) - dV_resid[:,np.newaxis])**2).sum(axis=0) def _logp(g): sh = g.shape g2 = np.atleast_2d(g) lp = prior.logp(g2) # lp += lkhd.logpdf(np.dot(Isc[:,i][:, np.newaxis], g2)).sum(0) lp += lkhd(g2) return lp.reshape(sh) # Evaluate at a few points around the prior mode to seed the ARS xs = np.array([.0001, .001, .01, .05, .1, 1.0, 2.0, 4.0, 7.0, 10.0]) v_xs = _logp(xs) assert np.all(np.isfinite(v_xs)) # Try for a few attempts to find the right bound gsc[i] = adaptive_rejection_sample(_logp, xs, v_xs, [ch.g.lower_bound, ch.g.upper_bound]) # Update the channel conductance parameter ch.g.value = gsc[i]
def kinetics(self, latent, inpt, state): """ Compute the state kinetics, d{latent}/dt, according to the Hodgkin-Huxley eqns, given current state x and external or given variables y. latent: latent state variables of the neuron, e.g. voltage per compartment, channel activation variables, etc. inpt: observations, e.g. supplied irradiance, calcium concentration, injected current, etc. state: evaluated state of the neuron including channel currents, etc. returns: dxdt: Rate of change of the latent state variables. """ # import pdb; pdb.set_trace() # Initialize dxdt for each latent state dxdt = np.zeros(latent.shape, dtype=self.latent_dtype) i_comp = get_item_at_path(inpt, self.compartment.path) x_comp = get_item_at_path(latent, self.compartment.path) x_ch = get_item_at_path(latent, self.path) I = i_comp['Irr'] V = x_comp['V'] p = x_ch['p'] # Compute the voltage-sensitive rate constants for state transitions Gd1 = 0.075 + 0.043*np.tanh(-(V+20)/20) Gd2 = 0.05 # Gr = 4.34587e5 * np.exp(-0.0211539274*V) Gr = 0.001 # Define a state variable for time and irradiance dependent activation # function for ChR2 (post isomerization) theta = 100*I # Optical stimulation protocol tau_chr2 = 1.3 # Time constant for ChR2 S0 = 0.5*(1+np.tanh(120*(theta-0.1))) dxdt['p'] = (S0-p)/tau_chr2 # Define the light-sensitive rate constants for state transitions lamda = 470 # Wavelength of max absorption for retinal eps1 = 0.8535 # quantum efficiency for photon absorption from C1 eps2 = 0.14 # quantum efficiency for photon absorption from C2 w_loss = 0.77; F = 0.00006*I*lamda/w_loss #Photon flux (molecules/photon/sec) # F = (sig_ret/hc)*I*lamda/w_loss*1e-9; % Photon flux (molecules/photon/sec) # Light sensitive rates for C1->01 and C2->O2 transition k1 = eps1 * F * p k2 = eps2 * F * p # Light sensitive O1->02 transitions e12d = 0.011 e12c1 = 0.005 e12c2 = 0.024 e12 = e12d + e12c1*np.log(1+I/e12c2) # Light sensitive O2->O1 transitions e21d = 0.008 e21c1 = 0.004 e21c2 = 0.024 e21 = e21d + e21c1*np.log(1+I/e21c2) dxdt['O1'] = k1 * x_ch['C1'] - (Gd1 + e12) * x_ch['O1'] + e21 * x_ch['O2'] dxdt['O2'] = k2 * x_ch['C2'] - (Gd2 + e21) * x_ch['O2'] + e12 * x_ch['O1'] dxdt['C1'] = Gr * x_ch['C2'] + Gd1 * x_ch['O1'] - k1 * x_ch['C1'] dxdt['C2'] = Gd2 * x_ch['O2'] + (k2 + Gr) * x_ch['C2'] return dxdt
def update(self, model, cache=None): """ Sample the conductances of the neuron given its latent state variables """ # c = self.compartment c = self.get_compartment(model) # Get the sub-structured arrays for this comp chs = c.channels # Get a list of conductances for this compartment gs = [ch.g for ch in chs] gsc = np.array([g.value for g in gs]) dVc_dts = [] Iscs = [] for data in model.data_sequences: t = data.t T = data.T inpt = data.input state = data.states s_comp = get_item_at_path(state, c.path) i_comp = get_item_at_path(inpt, c.path) Vc = s_comp['V'] dVc_dt = c.C.value * self.gradient_V(t, Vc) dt = np.concatenate((np.diff(t), np.array([t[-1] - t[-2]]))) # Some of this change is due to injected current dVc_dt -= i_comp['I'][:-1] dVc_dts.append(dVc_dt) # Now compute the per channel currents in this compartment Isc = np.zeros((T - 1, 0)) for ch in chs: if s_comp[ch.name].dtype.names is not None and \ 'I' in s_comp[ch.name].dtype.names: Isc = np.concatenate( (Isc, -1.0 * s_comp[ch.name]['I'][:-1, np.newaxis]), axis=1) Iscs.append(Isc) # Concatenate the dVs and the currents together dVc_dt = np.concatenate(dVc_dts, axis=0) Isc = np.concatenate(Iscs, axis=0) # Sample each conductance in turn given that # C*dVc_dt ~ N(I_in - np.dot(gsc, Isc), sig_V^2) for (i, ch) in enumerate(chs): i_rem = np.concatenate((np.arange(i), np.arange(i + 1, len(chs)))) gsc_rem = gsc[i_rem] Isc_rem = Isc[:, i_rem] # prior = gamma(alphas[ch], scale=1.0/betas[ch]) prior = ch.g.distribution dV_resid = dVc_dt - np.dot(Isc_rem, gsc_rem).ravel() from scipy.stats import norm sig_V = np.asscalar(hypers['sig_V'].value) # lkhd = norm(dV_resid[:,np.newaxis], sig_V) lkhd = lambda g: (-0.5 / sig_V**2 * (np.dot(Isc[:, i][:, np.newaxis], g) - dV_resid[:, np.newaxis])**2).sum(axis=0) def _logp(g): sh = g.shape g2 = np.atleast_2d(g) lp = prior.logp(g2) # lp += lkhd.logpdf(np.dot(Isc[:,i][:, np.newaxis], g2)).sum(0) lp += lkhd(g2) return lp.reshape(sh) # Evaluate at a few points around the prior mode to seed the ARS xs = np.array( [.0001, .001, .01, .05, .1, 1.0, 2.0, 4.0, 7.0, 10.0]) v_xs = _logp(xs) assert np.all(np.isfinite(v_xs)) # Try for a few attempts to find the right bound gsc[i] = adaptive_rejection_sample( _logp, xs, v_xs, [ch.g.lower_bound, ch.g.upper_bound]) # Update the channel conductance parameter ch.g.value = gsc[i]
def plot_latent_compartment_V_and_I(t, data_sequence, compartment, observation, axs=None, colors=['k'], linewidth=1): Z = data_sequence.latent S = data_sequence.states O = data_sequence.observations V = get_item_at_path(Z, compartment.path)['V'] Ca = get_item_at_path(Z, compartment.path)['[Ca]'] if isinstance(observation, NewDirectCompartmentVoltage): F = get_item_at_path(O, observation.path)['V'] if isinstance(observation, LowPassCompartmentVoltage): F = get_item_at_path(O, observation.path)['V'] elif isinstance(observation, LinearFluorescence): F = get_item_at_path(O, observation.path)['Flr'] else: F = None # Check for inputs I = np.zeros_like(t) try: I = get_item_at_path(data_sequence.input, compartment.path)['I'] except: # No input current pass try: I = get_item_at_path(data_sequence.input, compartment.path)['Irr'] except: # No input irradiance pass # Compute the channel currents s_comp = get_item_at_path(S, compartment.path) # Num rows = N_ch (oner per channel current) + # 1 (input) + 1 (voltage) + 1 (calcium) D = len(compartment.channels) + 3 # Set the relative width of the time series to the conductances r = 3 if axs is None: axs = [] for d in np.arange(D): ax = plt.subplot2grid((D,r+1), (d,0), colspan=r) axs.append(ax) # Add one more axis for the concentrations ax = plt.subplot2grid((D,r+1), (0,r), rowspan=D) axs.append(ax) # Plot the voltage) axs[0].plot(t, I, color='b', lw=linewidth) axs[0].set_ylabel('$I_{%s}$' % compartment.name) # Plot the voltage axs[1].plot(t, V, color=colors[0], lw=linewidth) axs[1].set_ylabel('$V_{%s}$' % compartment.name) # Plot the calcium axs[2].plot(t, Ca, color=colors[0], lw=linewidth) axs[2].set_ylabel('$[Ca]_{%s}$' % compartment.name) if F is not None: axs[1].plot(t, F, color='b', lw=linewidth) for i,ch in enumerate(compartment.channels): I = s_comp[ch.name]['I'] axs[i+3].plot(t, I, color=colors[i % len(colors)], linewidth=linewidth) axs[i+3].set_ylabel('$I_{%s}$' % ch.name) # Plot the channel densities C = len(compartment.channels) gs = [ch.g.value for ch in compartment.channels] names = [ch.name for ch in compartment.channels] axs[-1].bar(np.arange(C), gs, facecolor=colors[0], alpha=0.5) axs[-1].set_xticks(0.5+np.arange(C)) axs[-1].set_xticklabels(map(lambda n: '$g_{%s}$' % n, names)) axs[-1].set_title('Channel densities') return axs