spatial_network_layout.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import random as rng
  2. import numpy as np
  3. from matplotlib.patches import Ellipse
  4. class Interneuron:
  5. def __init__(self, x, y, a, b, phi):
  6. self.x = x
  7. self.y = y
  8. self.a = a # Semimajor axis
  9. self.b = b # Semiminor axis
  10. self.phi = phi
  11. self.c = 2.0 * a # Sum distance
  12. self.foc_len = np.sqrt(a ** 2 - b ** 2)
  13. self.area = np.pi * a * b
  14. def get_p1(self):
  15. x1 = self.x - self.foc_len * np.cos(self.phi)
  16. y1 = self.y - self.foc_len * np.sin(self.phi)
  17. return x1, y1
  18. def get_p2(self):
  19. x2 = self.x + self.foc_len * np.cos(self.phi)
  20. y2 = self.y + self.foc_len * np.sin(self.phi)
  21. return x2, y2
  22. def get_ellipse(self):
  23. ell = Ellipse((self.x, self.y), 2.0 * self.a, 2.0 * self.b,
  24. angle=self.phi * 180. / np.pi, linewidth=1, fill=False, zorder=2)
  25. return ell
  26. def get_area(self):
  27. return np.pi * self.a * self.b
  28. # TODO: move to plotting utils
  29. def get_position_mesh(ex_positions):
  30. n_ex = int(np.sqrt(len(ex_positions)))
  31. x_wrong = np.array([x for x, _ in ex_positions]).reshape((n_ex, n_ex))
  32. y_wrong = np.array([y for _, y in ex_positions]).reshape((n_ex, n_ex))
  33. x = x_wrong[0, :]
  34. y = y_wrong[:, 0]
  35. xmin = np.min(x)
  36. xmax = np.max(x)
  37. dx = (xmax - xmin) / (x.shape[0] - 1)
  38. ymin = np.min(y)
  39. ymax = np.max(y)
  40. dy = (ymax - ymin) / (y.shape[0] - 1)
  41. X, Y = np.meshgrid(np.linspace(xmin, xmax + dx, n_ex + 1, endpoint=True) - dx / 2, np.linspace(ymin, ymax + dy, n_ex + 1, endpoint=True) - dy / 2)
  42. return X, Y
  43. def get_entropy_of_axonal_coverage(ex_neuron_tuning, ie_connections):
  44. ex_phase_vals_per_interneuron = []
  45. phase_entropy_per_interneuron = []
  46. n_hist_bins = 10
  47. for connected_idxs in ie_connections:
  48. ex_phase_vals_of_p = [ex_neuron_tuning[idx] for idx in connected_idxs]
  49. phase_entropy = 0.0
  50. ex_phase_vals_per_interneuron.append(ex_phase_vals_of_p)
  51. ex_phase_hist, _ = np.histogram(ex_phase_vals_of_p, n_hist_bins)
  52. n_ex_of_p = float(len(ex_phase_vals_of_p))
  53. ex_phase_hist = 1. / n_ex_of_p * ex_phase_hist.astype(float)
  54. for n_phi in ex_phase_hist:
  55. if n_phi != 0:
  56. phase_entropy -= n_phi * np.log(n_phi)
  57. phase_entropy_per_interneuron.append(phase_entropy)
  58. return phase_entropy_per_interneuron
  59. def get_excitatory_neurons_in_inhibitory_axonal_clouds(ex_neuron_positions, inhibitory_axonal_clouds):
  60. ie_connections = []
  61. for p in inhibitory_axonal_clouds:
  62. ie_conn_p = []
  63. x_p_1, y_p_1 = p.get_p1()
  64. x_p_2, y_p_2 = p.get_p2()
  65. for ex_idx, ex_pos in enumerate(ex_neuron_positions):
  66. x_ex, y_ex = ex_pos
  67. d_1 = np.sqrt((x_ex - x_p_1) ** 2 + (y_ex - y_p_1) ** 2)
  68. d_2 = np.sqrt((x_ex - x_p_2) ** 2 + (y_ex - y_p_2) ** 2)
  69. if d_1 + d_2 < p.c:
  70. ie_conn_p.append(ex_idx)
  71. ie_connections.append(ie_conn_p)
  72. return ie_connections
  73. def get_excitatory_phases_in_inhibitory_axon(ex_neuron_positions, ex_neuron_tunings, inhibitory_neuron):
  74. ex_phase_vals = []
  75. x_p_1, y_p_1 = inhibitory_neuron.get_p1()
  76. x_p_2, y_p_2 = inhibitory_neuron.get_p2()
  77. for ex_idx, ex_pos in enumerate(ex_neuron_positions):
  78. x_ex, y_ex = ex_pos
  79. d_1 = np.sqrt((x_ex - x_p_1) ** 2 + (y_ex - y_p_1) ** 2)
  80. d_2 = np.sqrt((x_ex - x_p_2) ** 2 + (y_ex - y_p_2) ** 2)
  81. if d_1 + d_2 < inhibitory_neuron.c:
  82. ex_phase_vals.append(ex_neuron_tunings[ex_idx])
  83. return ex_phase_vals
  84. # TODO: There must be some methods twice
  85. def get_interneuron_entropy(ex_phase_vals, entropy_bins):
  86. phase_entropy = 0.
  87. ex_phase_hist, _ = np.histogram(ex_phase_vals, entropy_bins)
  88. ex_phase_prob = ex_phase_hist / len(ex_phase_vals)
  89. for prob in ex_phase_prob:
  90. if prob > 0.0:
  91. phase_entropy += -prob * np.log(prob)
  92. return phase_entropy
  93. def create_interneuron_sheet_entropy_max_orientation(ex_positions, ex_tunings, number_of_interneurons, long_axis,
  94. short_axis, max_x, max_y, trial_orientations=30,
  95. number_of_bins=30, placement_jitter=0.0, seed=0):
  96. inhibitory_axonal_clouds = initialize_axonal_clouds_on_grid_with_random_orientation(number_of_interneurons,
  97. long_axis, short_axis, max_x,
  98. max_y, placement_jitter, seed)
  99. interneuron_positions = [(inh.x, inh.y) for inh in inhibitory_axonal_clouds]
  100. '''
  101. Determine initial tunings and entropy values
  102. '''
  103. entropies_of_interneurons = [0.]
  104. if trial_orientations > 0:
  105. entropies_of_interneurons, optimal_orientations = get_optimal_orientations_for_maximum_entropy(ex_positions,
  106. ex_tunings,
  107. interneuron_positions,
  108. long_axis,
  109. short_axis,
  110. number_of_bins,
  111. trial_orientations)
  112. for opt_orientation, axon in zip(optimal_orientations, inhibitory_axonal_clouds):
  113. axon.phi = opt_orientation
  114. return inhibitory_axonal_clouds, np.mean(entropies_of_interneurons)
  115. def get_optimal_orientations_for_maximum_entropy(ex_positions, ex_tunings, interneuron_positions, long_axis, short_axis,
  116. number_of_bins, trial_orientations):
  117. entropy_bins = np.linspace(-np.pi, np.pi, number_of_bins)
  118. entropies_of_interneurons = []
  119. optimal_orientations = []
  120. # for i_neuron in tqdm(inhibitory_axonal_clouds, desc="Maximizing entropy by rotation"):
  121. for x, y in interneuron_positions:
  122. orientations = np.linspace(-np.pi, np.pi, trial_orientations)
  123. entropy_for_orientation = []
  124. for orientation in orientations:
  125. perturbed_interneuron = Interneuron(x, y, long_axis, short_axis, orientation)
  126. ex_phase_vals = get_excitatory_phases_in_inhibitory_axon(ex_positions, ex_tunings, perturbed_interneuron)
  127. entropy_for_orientation.append(get_interneuron_entropy(ex_phase_vals, entropy_bins))
  128. optimal_orientations.append(orientations[np.argmax(entropy_for_orientation)])
  129. entropies_of_interneurons.append(np.max(entropy_for_orientation))
  130. return entropies_of_interneurons, optimal_orientations
  131. def initialize_axonal_clouds_on_grid_with_random_orientation(number_of_interneurons, long_axis, short_axis, sheet_x,
  132. sheet_y, placement_jitter=0.0, seed=0):
  133. '''
  134. Start with uniform distribution
  135. '''
  136. rng.seed(seed)
  137. interneuron_a = long_axis
  138. interneuron_b = short_axis
  139. interneuron_n = number_of_interneurons
  140. x_n = int(np.sqrt(interneuron_n))
  141. y_n = int(np.sqrt(interneuron_n))
  142. x_step = int(sheet_x / (x_n + 1))
  143. y_step = int(sheet_y / (y_n + 1))
  144. inhibitory_axonal_clouds = []
  145. pj = placement_jitter
  146. for n in range(interneuron_n):
  147. theta = rng.random() * 2. * np.pi
  148. pj_x = pj * np.cos(theta)
  149. pj_y = pj * np.sin(theta)
  150. inhibitory_axonal_clouds.append(Interneuron(((n % x_n) + 1) * x_step + pj_x,
  151. (int(n / y_n) + 1) * y_step + pj_y,
  152. interneuron_a, interneuron_b, theta))
  153. return inhibitory_axonal_clouds
  154. def create_grid_of_excitatory_neurons(sheet_size, n_e_per_row, tuning_map):
  155. x = np.linspace(0, sheet_size, n_e_per_row)
  156. y = np.linspace(0, sheet_size, n_e_per_row)
  157. ex_neuron_positions = []
  158. ex_neuron_tuning = []
  159. for y_idx, yy in enumerate(y):
  160. for x_idx, xx in enumerate(x):
  161. noise_value = tuning_map(xx, yy)
  162. ex_neuron_positions.append([xx, yy])
  163. ex_neuron_tuning.append(noise_value)
  164. return ex_neuron_positions, ex_neuron_tuning