connection.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Created on 03.10.2012
  4. @author: frank
  5. '''
  6. from .chunkfile import ChunkFile, ChunkFileWriter, FileFormat
  7. import struct
  8. import numpy as np
  9. import matplotlib.pyplot as plt
  10. import matplotlib.cm as cm
  11. import logging
  12. class Error(Exception):
  13. """base class for Connection errors."""
  14. pass
  15. class NumberOfNodesMismatchError(Error):
  16. def __init__(self, con_n_target, target_nodes):
  17. self.con_n_target = con_n_target
  18. self.target_nodes = target_nodes
  19. def __str__(self):
  20. ret = "too few target nodes: connection has "
  21. ret += str(self.con_n_target)
  22. ret += " but only " + str(len(self.target_nodes)) + " are given"
  23. return ret
  24. def main():
  25. c = Connection()
  26. # c.load_weight_file("../test/data/conExInhweights.dat")
  27. c.load_weight_file("../test/data/simdata_som02_gauss/conForward0weights.dat19")
  28. c.calc_pre_post_arrays()
  29. a = c.get_incomming_matrix(7690)
  30. print(a.sum())
  31. plt.imshow(a, cmap=cm.gray, interpolation="nearest")
  32. plt.show()
  33. class ConnectionHeader(object):
  34. """represents a header of an objsim weight file"""
  35. HEADER_LEN_V0 = 6*4
  36. HEADER_LEN_V1 = 7*4
  37. def __init__(self, header_bytes="", n_source=0, ns_x=0, ns_y=0, n_target=0, nt_x=0, nt_y=0, xfast=True):
  38. if header_bytes:
  39. self.from_string(header_bytes)
  40. else:
  41. self.n_source = n_source
  42. self.ns_x = ns_x
  43. self.ns_y = ns_y
  44. self.n_target = n_target
  45. self.nt_x = nt_x
  46. self.nt_y = nt_y
  47. self.xfast = xfast
  48. def from_string(self, header_bytes):
  49. header_len = len(header_bytes)
  50. if header_len < self.HEADER_LEN_V0:
  51. raise Error("wrong header length: " + str(header_len))
  52. self.n_source, self.ns_x, self.ns_y, self.n_target, self.nt_x, self.nt_y = struct.unpack('iiiiii', header_bytes[0:self.HEADER_LEN_V0])
  53. if header_len == self.HEADER_LEN_V1:
  54. xfast_int = struct.unpack('i', header_bytes[self.HEADER_LEN_V0:self.HEADER_LEN_V1])
  55. self.xfast = (xfast_int == 1)
  56. else:
  57. self.xfast = True
  58. def bytes(self, with_xfast=True):
  59. if with_xfast:
  60. return struct.pack('iiiiiii', self.n_source, self.ns_x, self.ns_y, self.n_target, self.nt_x, self.nt_y, self.xfast)
  61. else:
  62. return struct.pack('iiiiii', self.n_source, self.ns_x, self.ns_y, self.n_target, self.nt_x, self.nt_y)
  63. def get_members(self, names):
  64. ret = ""
  65. for n in names:
  66. ret += n + str(getattr(self, n))
  67. def __repr__(self):
  68. ret = "<ConnectionHeader "
  69. members = ["n_source", "ns_x", "ns_y", "n_target", "nt_x", "nt_y"]
  70. ret += " ".join([n + "=" + str(getattr(self, n)) for n in members])
  71. ret += ">"
  72. return ret
  73. class Connection(object):
  74. """class to access objsim weight files"""
  75. def __init__(self, file_path=None):
  76. self.con_header = None
  77. self.source_nr = None
  78. self.target_nr = None
  79. self.weights = None
  80. self.delays = None
  81. if file_path is not None:
  82. self.load_weight_file(file_path)
  83. def load_weight_file(self, file_path):
  84. """load an objsim weight file"""
  85. weight_file = ChunkFile(file_path)
  86. header_bytes = weight_file.read_chunk("VecHeader")
  87. self.con_header = ConnectionHeader(header_bytes)
  88. logging.debug(self.con_header)
  89. self.source_nr = weight_file.read_array("SourceNr", 'i')
  90. self.target_nr = weight_file.read_array("TargetNr", 'i')
  91. self.delays = weight_file.read_array("Delays", 'i')
  92. self.weights = weight_file.read_array("Weights", 'f')
  93. #print len(self.source_nr)
  94. #print self.source_nr[0:400]
  95. #print self.source_nr.max()
  96. #print weight_file.get_chunk_names()
  97. #print self.weights[0:200]
  98. #print self.weights.max()
  99. self.calc_pre_post_arrays()
  100. @classmethod
  101. def from_nest_con_list(cls, con_list, source_nodes, target_nodes, source_dim=None, target_dim=None, dt=0.25):
  102. import nest
  103. connection = cls()
  104. connection.weights = np.array(nest.GetStatus(con_list, 'weight'), dtype='f')
  105. connection.source_nr = np.array(nest.GetStatus(con_list, 'source'), dtype='i')
  106. connection.source_nr -= source_nodes[0]
  107. connection.target_nr = np.array(nest.GetStatus(con_list, 'target'), dtype='i')
  108. connection.target_nr -= target_nodes[0]
  109. delays_ms = np.array(nest.GetStatus(con_list, 'delay'))
  110. delays_int = np.array(delays_ms / dt, dtype='i')
  111. connection.delays = delays_int
  112. n_source = len(source_nodes)
  113. n_target = len(target_nodes)
  114. if source_dim is None:
  115. source_dim = [n_source, 1]
  116. if target_dim is None:
  117. target_dim = [n_target, 1]
  118. connection.con_header = ConnectionHeader(n_source=n_source,
  119. ns_x=source_dim[0],
  120. ns_y=source_dim[1],
  121. n_target=n_target,
  122. nt_x=target_dim[0],
  123. nt_y=target_dim[1],
  124. xfast=False)
  125. return connection
  126. def save(self, file_path, major=2, minor=1):
  127. file_format = FileFormat('VecConnection', major, minor)
  128. weight_file = ChunkFileWriter(file_path, file_format)
  129. weight_file.write_byte_chunk('VecHeader', self.con_header.bytes(with_xfast=(minor>0)))
  130. weight_file.write_int_array_chunk('SourceNr', self.source_nr)
  131. weight_file.write_int_array_chunk('TargetNr', self.target_nr)
  132. weight_file.write_int_array_chunk('Delays', self.delays)
  133. weight_file.write_float_array_chunk('Weights', self.weights)
  134. weight_file.close()
  135. def get_n_source(self):
  136. return self.con_header.n_source
  137. def get_n_target(self):
  138. return self.con_header.n_target
  139. def get_target_nx(self):
  140. return self.con_header.nt_x
  141. def get_target_ny(self):
  142. return self.con_header.nt_y
  143. def calc_pre_post_arrays(self):
  144. self.pre_syn_nr = [[] for x in range(self.con_header.n_target)]
  145. self.post_syn_nr = [[] for x in range(self.con_header.n_source)]
  146. n_synapses = len(self.source_nr)
  147. for i in range(n_synapses):
  148. self.pre_syn_nr[self.target_nr[i]].append(i)
  149. self.post_syn_nr[self.source_nr[i]].append(i)
  150. def get_incomming_matrix(self, target_nr):
  151. m = np.zeros(self.con_header.n_source)
  152. for syn_nr in self.pre_syn_nr[target_nr]:
  153. m[self.source_nr[syn_nr]] += self.weights[syn_nr]
  154. # numpy indexing order: [z,y,x]. higher to lower dimension from left to right
  155. return m.reshape(self.con_header.ns_y, self.con_header.ns_x)
  156. def get_incomming_weight_sum(self):
  157. """Calculate the sum of incomming for every postsynaptic neuron."""
  158. m = np.zeros(self.con_header.n_target)
  159. for target_nr in range(self.con_header.n_target):
  160. m[target_nr] = sum([self.weights[syn_nr] for syn_nr in self.pre_syn_nr[target_nr]])
  161. return m.reshape(self.con_header.nt_y, self.con_header.nt_x)
  162. def calc_nest_con_params(self, target_nodes):
  163. """
  164. Calculate NEST connection parameters to be used with nest.DataConnect
  165. :param target_nodes: list of NEST gid numbers for target nodes.
  166. Must be as long as number of target nodes of the connection object
  167. :rtype: [{'target': ..., 'weight': ..., 'delay': ...}, ...]
  168. to be used with nest.DataConnect
  169. :raises: NumberOfNodesMismatchError when target_nodes has too few items for this connection
  170. """
  171. n_source = self.get_n_source()
  172. n_target = self.get_n_target()
  173. if len(target_nodes) < n_target:
  174. raise NumberOfNodesMismatchError(n_target, target_nodes)
  175. params = []
  176. for i in range(n_source):
  177. synapses = self.post_syn_nr[i]
  178. if synapses:
  179. target_ids = (self.target_nr[s] for s in synapses)
  180. target = [float(target_nodes[t]) for t in target_ids]
  181. delay = [float(self.delays[s])+1.0 for s in synapses]
  182. weight = [float(self.weights[s]) for s in synapses]
  183. params.append({'target': target, 'weight': weight, 'delay': delay})
  184. logging.debug("n params = " + str(len(params)))
  185. return params
  186. if __name__ == '__main__':
  187. main()