Source code for elephant.current_source_density_src.utility_functions

# -*- coding: utf-8 -*-
"""
These are some useful functions used in CSD methods,
They include CSD source profiles to be used as ground truths,
placement of electrodes in 1D, 2D and 3D., etc
These scripts are based on Grzegorz Parka's,
Google Summer of Code 2014, INFC/pykCSD
This was written by :
Michal Czerwinski, Chaitanya Chintaluri
Laboratory of Neuroinformatics,
Nencki Institute of Experimental Biology, Warsaw.
"""
from __future__ import division

import numpy as np
from numpy import exp
import quantities as pq


[docs]def patch_quantities(): """patch quantities with the SI unit Siemens if it does not exist""" for symbol, prefix, definition, u_symbol in zip( ['siemens', 'S', 'mS', 'uS', 'nS', 'pS'], ['', '', 'milli', 'micro', 'nano', 'pico'], [pq.A / pq.V, pq.A / pq.V, 'S', 'mS', 'uS', 'nS'], [None, None, None, None, u'µS', None]): if type(definition) is str: definition = lastdefinition / 1000 if not hasattr(pq, symbol): setattr(pq, symbol, pq.UnitQuantity( prefix + 'siemens', definition, symbol=symbol, u_symbol=u_symbol)) lastdefinition = definition return
[docs]def check_for_duplicated_electrodes(elec_pos): """Checks for duplicate electrodes Parameters ---------- elec_pos : np.array Returns ------- has_duplicated_elec : Boolean """ unique_elec_pos = np.vstack({tuple(row) for row in elec_pos}) has_duplicated_elec = unique_elec_pos.shape == elec_pos.shape return has_duplicated_elec
[docs]def distribute_srcs_1D(X, n_src, ext_x, R_init): """Distribute sources in 1D equally spaced Parameters ---------- X : np.arrays points at which CSD will be estimated n_src : int number of sources to be included in the model ext_x : floats how much should the sources extend the area X R_init : float Same as R in 1D case Returns ------- X_src : np.arrays positions of the sources R : float effective radius of the basis element """ X_src = np.mgrid[(np.min(X) - ext_x):(np.max(X) + ext_x): np.complex(0, n_src)] R = R_init return X_src, R
[docs]def distribute_srcs_2D(X, Y, n_src, ext_x, ext_y, R_init): """Distribute n_src's in the given area evenly Parameters ---------- X, Y : np.arrays points at which CSD will be estimated n_src : int demanded number of sources to be included in the model ext_x, ext_y : floats how should the sources extend the area X, Y R_init : float demanded radius of the basis element Returns ------- X_src, Y_src : np.arrays positions of the sources nx, ny : ints number of sources in directions x,y new n_src = nx * ny may not be equal to the demanded number of sources R : float effective radius of the basis element """ Lx = np.max(X) - np.min(X) Ly = np.max(Y) - np.min(Y) Lx_n = Lx + (2 * ext_x) Ly_n = Ly + (2 * ext_y) [nx, ny, Lx_nn, Ly_nn, ds] = get_src_params_2D(Lx_n, Ly_n, n_src) ext_x_n = (Lx_nn - Lx) / 2 ext_y_n = (Ly_nn - Ly) / 2 X_src, Y_src = np.mgrid[(np.min(X) - ext_x_n):(np.max(X) + ext_x_n): np.complex(0, nx), (np.min(Y) - ext_y_n):(np.max(Y) + ext_y_n): np.complex(0, ny)] # d = round(R_init / ds) R = R_init # R = d * ds return X_src, Y_src, R
[docs]def get_src_params_2D(Lx, Ly, n_src): """Distribute n_src sources evenly in a rectangle of size Lx * Ly Parameters ---------- Lx, Ly : floats lengths in the directions x, y of the area, the sources should be placed n_src : int demanded number of sources Returns ------- nx, ny : ints number of sources in directions x, y new n_src = nx * ny may not be equal to the demanded number of sources Lx_n, Ly_n : floats updated lengths in the directions x, y ds : float spacing between the sources """ coeff = [Ly, Lx - Ly, -Lx * n_src] rts = np.roots(coeff) r = [r for r in rts if type(r) is not complex and r > 0] nx = r[0] ny = n_src / nx ds = Lx / (nx - 1) nx = np.floor(nx) + 1 ny = np.floor(ny) + 1 Lx_n = (nx - 1) * ds Ly_n = (ny - 1) * ds return (nx, ny, Lx_n, Ly_n, ds)
[docs]def distribute_srcs_3D(X, Y, Z, n_src, ext_x, ext_y, ext_z, R_init): """Distribute n_src sources evenly in a rectangle of size Lx * Ly * Lz Parameters ---------- X, Y, Z : np.arrays points at which CSD will be estimated n_src : int desired number of sources we want to include in the model ext_x, ext_y, ext_z : floats how should the sources extend over the area X,Y,Z R_init : float demanded radius of the basis element Returns ------- X_src, Y_src, Z_src : np.arrays positions of the sources in 3D space nx, ny, nz : ints number of sources in directions x,y,z new n_src = nx * ny * nz may not be equal to the demanded number of sources R : float updated radius of the basis element """ Lx = np.max(X) - np.min(X) Ly = np.max(Y) - np.min(Y) Lz = np.max(Z) - np.min(Z) Lx_n = Lx + 2 * ext_x Ly_n = Ly + 2 * ext_y Lz_n = Lz + 2 * ext_z (nx, ny, nz, Lx_nn, Ly_nn, Lz_nn, ds) = get_src_params_3D(Lx_n, Ly_n, Lz_n, n_src) ext_x_n = (Lx_nn - Lx) / 2 ext_y_n = (Ly_nn - Ly) / 2 ext_z_n = (Lz_nn - Lz) / 2 X_src, Y_src, Z_src = np.mgrid[(np.min(X) - ext_x_n):(np.max(X) + ext_x_n): np.complex(0, nx), (np.min(Y) - ext_y_n):(np.max(Y) + ext_y_n): np.complex(0, ny), (np.min(Z) - ext_z_n):(np.max(Z) + ext_z_n): np.complex(0, nz)] # d = np.round(R_init / ds) R = R_init return (X_src, Y_src, Z_src, R)
[docs]def get_src_params_3D(Lx, Ly, Lz, n_src): """Helps to evenly distribute n_src sources in a cuboid of size Lx * Ly * Lz Parameters ---------- Lx, Ly, Lz : floats lengths in the directions x, y, z of the area, the sources should be placed n_src : int demanded number of sources to be included in the model Returns ------- nx, ny, nz : ints number of sources in directions x, y, z new n_src = nx * ny * nz may not be equal to the demanded number of sources Lx_n, Ly_n, Lz_n : floats updated lengths in the directions x, y, z ds : float spacing between the sources (grid nodes) """ V = Lx * Ly * Lz V_unit = V / n_src L_unit = V_unit**(1. / 3.) nx = np.ceil(Lx / L_unit) ny = np.ceil(Ly / L_unit) nz = np.ceil(Lz / L_unit) ds = Lx / (nx - 1) Lx_n = (nx - 1) * ds Ly_n = (ny - 1) * ds Lz_n = (nz - 1) * ds return (nx, ny, nz, Lx_n, Ly_n, Lz_n, ds)
[docs]def generate_electrodes(dim, xlims=[0.1, 0.9], ylims=[0.1, 0.9], zlims=[0.1, 0.9], res=5): """Generates electrodes, helpful for FWD funtion. Parameters ---------- dim : int Dimensionality of the electrodes, 1,2 or 3 xlims : [start, end] Spatial limits of the electrodes ylims : [start, end] Spatial limits of the electrodes zlims : [start, end] Spatial limits of the electrodes res : int How many electrodes in each dimension Returns ------- ele_x, ele_y, ele_z : flattened np.array of the electrode pos """ if dim == 1: ele_x = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res)] ele_x = ele_x.flatten() return ele_x elif dim == 2: ele_x, ele_y = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res), ylims[0]: ylims[1]: np.complex(0, res)] ele_x = ele_x.flatten() ele_y = ele_y.flatten() return ele_x, ele_y elif dim == 3: ele_x, ele_y, ele_z = np.mgrid[xlims[0]: xlims[1]: np.complex(0, res), ylims[0]: ylims[1]: np.complex(0, res), zlims[0]: zlims[1]: np.complex(0, res)] ele_x = ele_x.flatten() ele_y = ele_y.flatten() ele_z = ele_z.flatten() return ele_x, ele_y, ele_z
[docs]def gauss_1d_dipole(x): """1D Gaussian dipole source is placed between 0 and 1 to be used to test the CSD Parameters ---------- x : np.array Spatial pts. at which the true csd is evaluated Returns ------- f : np.array The value of the csd at the requested points """ src = 0.5*exp(-((x-0.7)**2)/(2.*0.3))*(2*np.pi*0.3)**-0.5 snk = -0.5*exp(-((x-0.3)**2)/(2.*0.3))*(2*np.pi*0.3)**-0.5 f = src+snk return f
[docs]def large_source_2D(x, y): """2D Gaussian large source profile - to use to test csd Parameters ---------- x : np.array Spatial x pts. at which the true csd is evaluated y : np.array Spatial y pts. at which the true csd is evaluated Returns ------- f : np.array The value of the csd at the requested points """ zz = [0.4, -0.3, -0.1, 0.6] zs = [0.2, 0.3, 0.4, 0.2] f1 = 0.5965*exp( (-1*(x-0.1350)**2 - (y-0.8628)**2) /0.4464)* exp(-(-zz[0])**2 / zs[0]) /exp(-(zz[0])**2/zs[0]) f2 = -0.9269*exp( (-2*(x-0.1848)**2 - (y-0.0897)**2) /0.2046)* exp(-(-zz[1])**2 / zs[1]) /exp(-(zz[1])**2/zs[1]); f3 = 0.5910*exp( (-3*(x-1.3189)**2 - (y-0.3522)**2) /0.2129)* exp(-(-zz[2])**2 / zs[2]) /exp(-(zz[2])**2/zs[2]); f4 = -0.1963*exp( (-4*(x-1.3386)**2 - (y-0.5297)**2) /0.2507)* exp(-(-zz[3])**2 / zs[3]) /exp(-(zz[3])**2/zs[3]); f = f1+f2+f3+f4 return f
[docs]def small_source_2D(x, y): """2D Gaussian small source profile - to be used to test csd Parameters ---------- x : np.array Spatial x pts. at which the true csd is evaluated y : np.array Spatial y pts. at which the true csd is evaluated Returns ------- f : np.array The value of the csd at the requested points """ def gauss2d(x,y,p): rcen_x = p[0] * np.cos(p[5]) - p[1] * np.sin(p[5]) rcen_y = p[0] * np.sin(p[5]) + p[1] * np.cos(p[5]) xp = x * np.cos(p[5]) - y * np.sin(p[5]) yp = x * np.sin(p[5]) + y * np.cos(p[5]) g = p[4]*exp(-(((rcen_x-xp)/p[2])**2+ ((rcen_y-yp)/p[3])**2)/2.) return g f1 = gauss2d(x,y,[0.3,0.7,0.038,0.058,0.5,0.]) f2 = gauss2d(x,y,[0.3,0.6,0.038,0.058,-0.5,0.]) f3 = gauss2d(x,y,[0.45,0.7,0.038,0.058,0.5,0.]) f4 = gauss2d(x,y,[0.45,0.6,0.038,0.058,-0.5,0.]) f = f1+f2+f3+f4 return f
[docs]def gauss_3d_dipole(x, y, z): """3D Gaussian dipole profile - to be used to test csd. Parameters ---------- x : np.array Spatial x pts. at which the true csd is evaluated y : np.array Spatial y pts. at which the true csd is evaluated z : np.array Spatial z pts. at which the true csd is evaluated Returns ------- f : np.array The value of the csd at the requested points """ x0, y0, z0 = 0.3, 0.7, 0.3 x1, y1, z1 = 0.6, 0.5, 0.7 sig_2 = 0.023 A = (2*np.pi*sig_2)**-1 f1 = A*exp( (-(x-x0)**2 -(y-y0)**2 -(z-z0)**2) / (2*sig_2) ) f2 = -1*A*exp( (-(x-x1)**2 -(y-y1)**2 -(z-z1)**2) / (2*sig_2) ) f = f1+f2 return f