yinspire 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. data/README +24 -0
  2. data/bench/pq/Makefile +5 -0
  3. data/bench/pq/bench.cc +321 -0
  4. data/bench/pq/bench.rb +125 -0
  5. data/bench/pq/bench_binaryheap.h +46 -0
  6. data/bench/pq/bench_calendarqueue.h +58 -0
  7. data/bench/pq/bench_pairingheap.h +61 -0
  8. data/bench/pq/bench_stlpq.h +46 -0
  9. data/bench/pq/benchmark.h +225 -0
  10. data/bench/pq/distribution.h +93 -0
  11. data/bench/pq/make.rb +24 -0
  12. data/bin/yinspire +186 -0
  13. data/examples/nets/gereon2005.c.json +93723 -0
  14. data/examples/nets/gereon2005.yin +232650 -0
  15. data/examples/nets/skorpion.graphml +396 -0
  16. data/examples/nets/spiketrains_angle_180.txt +8 -0
  17. data/lib/Algorithms/Array.h +52 -0
  18. data/lib/Algorithms/BinaryHeap.h +265 -0
  19. data/lib/Algorithms/CalendarQueue.h +257 -0
  20. data/lib/Algorithms/IndexedBinaryHeap.h +90 -0
  21. data/lib/Algorithms/PairingHeap.h +169 -0
  22. data/lib/Allocators/ChunkedFreelistAllocator.h +96 -0
  23. data/lib/Allocators/MemoryAllocator.h +45 -0
  24. data/lib/Allocators/RubyMemoryAllocator.h +37 -0
  25. data/lib/Yinspire.rb +69 -0
  26. data/lib/Yinspire/All.rb +10 -0
  27. data/lib/Yinspire/Core/NeuralEntity.rb +133 -0
  28. data/lib/Yinspire/Core/Neuron.rb +162 -0
  29. data/lib/Yinspire/Core/Scheduling/NeuralEntity.rb +123 -0
  30. data/lib/Yinspire/Core/Scheduling/Simulator.rb +94 -0
  31. data/lib/Yinspire/Core/Simulator.rb +36 -0
  32. data/lib/Yinspire/Core/StimuliMixin.rb +103 -0
  33. data/lib/Yinspire/Core/Stimulus.rb +25 -0
  34. data/lib/Yinspire/Core/Synapse.rb +64 -0
  35. data/lib/Yinspire/Dumpers/Dumper.rb +19 -0
  36. data/lib/Yinspire/Dumpers/Dumper_Dot.rb +28 -0
  37. data/lib/Yinspire/Loaders/GraphML.rb +84 -0
  38. data/lib/Yinspire/Loaders/Loader.rb +31 -0
  39. data/lib/Yinspire/Loaders/Loader_GraphML.rb +97 -0
  40. data/lib/Yinspire/Loaders/Loader_JSON.rb +181 -0
  41. data/lib/Yinspire/Loaders/Loader_Spike.rb +42 -0
  42. data/lib/Yinspire/Loaders/Loader_Yin.rb +62 -0
  43. data/lib/Yinspire/Loaders/YinScanner.rb +247 -0
  44. data/lib/Yinspire/Models/Neuron_Base.rb +38 -0
  45. data/lib/Yinspire/Models/Neuron_Input.rb +12 -0
  46. data/lib/Yinspire/Models/Neuron_InputOutput.rb +39 -0
  47. data/lib/Yinspire/Models/Neuron_Output.rb +15 -0
  48. data/lib/Yinspire/Models/Neuron_SRM01.rb +50 -0
  49. data/lib/Yinspire/Models/Neuron_SRM02.rb +64 -0
  50. data/lib/Yinspire/Models/Synapse_Hebb.rb +67 -0
  51. data/pure_cpp/Makefile +22 -0
  52. data/pure_cpp/README +2 -0
  53. data/pure_cpp/src/algo/binary_heap.h +277 -0
  54. data/pure_cpp/src/algo/indexed_binary_heap.h +90 -0
  55. data/pure_cpp/src/json/json.cc +542 -0
  56. data/pure_cpp/src/json/json.h +182 -0
  57. data/pure_cpp/src/json/json_parser.cc +685 -0
  58. data/pure_cpp/src/json/json_parser.h +15 -0
  59. data/pure_cpp/src/json/json_parser.rl +213 -0
  60. data/pure_cpp/src/main.cc +49 -0
  61. data/pure_cpp/src/memory_allocator.h +45 -0
  62. data/pure_cpp/src/neural_entity.cc +208 -0
  63. data/pure_cpp/src/neural_entity.h +243 -0
  64. data/pure_cpp/src/neuron.cc +136 -0
  65. data/pure_cpp/src/neuron.h +70 -0
  66. data/pure_cpp/src/neuron_srm_01.cc +77 -0
  67. data/pure_cpp/src/neuron_srm_01.h +36 -0
  68. data/pure_cpp/src/simulator.cc +151 -0
  69. data/pure_cpp/src/simulator.h +116 -0
  70. data/pure_cpp/src/synapse.cc +117 -0
  71. data/pure_cpp/src/synapse.h +60 -0
  72. data/pure_cpp/src/types.h +18 -0
  73. data/run.rb +68 -0
  74. data/tools/conv_jsonc_to_yin.rb +165 -0
  75. data/tools/converter.rb +93 -0
  76. data/tools/json_writer.rb +122 -0
  77. data/yinspire.gemspec +20 -0
  78. metadata +156 -0
@@ -0,0 +1,181 @@
1
+ require 'Yinspire/Loaders/Loader'
2
+ require 'yaml' # YAML is a superset of JSON
3
+
4
+ class Loader_JSON < Loader
5
+
6
+ def load(filename)
7
+ @entities = Hash.new
8
+ data = YAML.load(File.read(filename))
9
+
10
+ case data['format']
11
+ when 'yinspire.1'
12
+ load_v1(data)
13
+ when 'yinspire.c'
14
+ load_c(data)
15
+ else
16
+ raise "invalid format"
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ #
23
+ # Format:
24
+ #
25
+ # {
26
+ # templates: {
27
+ # 'MyNeuron' => ['Neuron_SRM01', {tau_m: 0.5, :ref_weight: 0.1}],
28
+ # 'MySynapse' => ['Synapse', {weight: 1.0, delay: 0.5}]
29
+ # },
30
+ # entities: {
31
+ # 'id1' => ['MyNeuron', {tau_m: 5.1, const_threshold: 0.44}],
32
+ # 'id2' => ['Neuron_SRM01', {}],
33
+ # 'id3' => ['MySynapse'],
34
+ # 'id4' => 'MyNeuron'
35
+ # },
36
+ # connections: {
37
+ # 'id1' => ['id2', 'id3'],
38
+ # 'id2' => ['id1']
39
+ # },
40
+ # events: {
41
+ # 'id1' => [100.0, 101.0, timestamp_x, timestamp_y],
42
+ # 'id2' => [444, 555]
43
+ # }
44
+ # }
45
+ #
46
+ # Every "section" is optional.
47
+ #
48
+ def load_v1(data)
49
+ templates = data['templates'] || {}
50
+ entities = data['entities'] || {}
51
+ connections = data['connections'] || {}
52
+ events = data['events'] || {}
53
+
54
+ #
55
+ # construct entities
56
+ #
57
+ hash = Hash.new
58
+ entities.each do |id, entity_spec|
59
+ type, data = *entity_spec
60
+
61
+ if t = templates[type]
62
+ type, template_data = *t
63
+ hash.update(template_data) if template_data
64
+ end
65
+
66
+ hash.update(data) if data
67
+
68
+ create_entity(type, id, hash)
69
+
70
+ hash.clear
71
+ end
72
+
73
+ #
74
+ # connect them
75
+ #
76
+ connections.each do |src, destinations|
77
+ entity = @entities[src]
78
+ destinations.each do |dest|
79
+ entity.connect(@entities[dest])
80
+ end
81
+ end
82
+
83
+ #
84
+ # stimulate with events
85
+ #
86
+ events.each do |id, time_series|
87
+ entity = @entities[id]
88
+ time_series.each do |at|
89
+ entity.stimulate(at, Infinity, nil)
90
+ end
91
+ end
92
+ end
93
+
94
+ #
95
+ # This is a version for the pure C++ version of Yinspire. It doesn't
96
+ # use hashes for entities and connections (because the C++ JSON library
97
+ # does not implement efficient hash lookup).
98
+ #
99
+ # {
100
+ # templates: {
101
+ # 'MyNeuron' => ['Neuron_SRM01', {tau_m: 0.5, :ref_weight: 0.1}],
102
+ # 'MySynapse' => ['Synapse', {weight: 1.0, delay: 0.5}]
103
+ # },
104
+ # entities: [
105
+ # ['id1', 'MyNeuron'],
106
+ # ['id2', 'Neuron_SRM01'],
107
+ # ['id3', 'MySynapse'],
108
+ # ['id4', 'MyNeuron']
109
+ # ],
110
+ # connections: {
111
+ # ['id1', 'id2', 'id3'],
112
+ # ['id2', 'id1']
113
+ # },
114
+ # events: {
115
+ # 'id1' => [100.0, 101.0, timestamp_x, timestamp_y],
116
+ # 'id2' => [444, 555]
117
+ # }
118
+ # }
119
+ #
120
+ # Every "section" is optional.
121
+ #
122
+ # BUGS:
123
+ #
124
+ # The C++ version does not currently implement to specify an
125
+ # entity like this:
126
+ #
127
+ # ['id1', 'Neuron_SRM01', {...}]
128
+ #
129
+ def load_c(data)
130
+ templates = data['templates'] || {}
131
+ entities = data['entities'] || []
132
+ connections = data['connections'] || []
133
+ events = data['events'] || {}
134
+
135
+ #
136
+ # construct entities
137
+ #
138
+ hash = Hash.new
139
+ entities.each do |arr|
140
+ hash.clear
141
+
142
+ id, type, data = *arr
143
+
144
+ if t = templates[type]
145
+ type, template_data = *t
146
+ hash.update(template_data)
147
+ end
148
+
149
+ if data
150
+ hash.update(data)
151
+ raise # FIXME: C++ version is invalid, because it assume that there
152
+ # is no data!
153
+ end
154
+
155
+ create_entity(type, id, hash)
156
+ end
157
+
158
+ #
159
+ # connect them
160
+ #
161
+ connections.each do |arr|
162
+ src, *destinations = *arr
163
+ raise if destinations.empty?
164
+ entity = @entities[src] || raise
165
+ destinations.each do |dest|
166
+ entity.connect(@entities[dest] || raise)
167
+ end
168
+ end
169
+
170
+ #
171
+ # stimulate with events
172
+ #
173
+ events.each do |id, time_series|
174
+ entity = @entities[id] || raise
175
+ time_series.each do |at|
176
+ entity.stimulate(at, Infinity, nil)
177
+ end
178
+ end
179
+ end
180
+
181
+ end
@@ -0,0 +1,42 @@
1
+ require 'Yinspire/Loaders/Loader'
2
+
3
+ #
4
+ # Loader for spikes in the format:
5
+ #
6
+ # Id1 weight1@time1 time2 time3 ...
7
+ # Id2 time1 time2 time3 ...
8
+ # ...
9
+ #
10
+ # Weight values (e.g. 1.0@time1) are optional.
11
+ #
12
+ # Lines beginning with "#" are comments.
13
+ #
14
+ class Loader_Spike < Loader
15
+
16
+ def load(filename)
17
+ File.open(filename, 'r') do |f|
18
+ while line = f.gets
19
+ line.strip!
20
+ next if line =~ /^#/ # comment
21
+
22
+ #
23
+ # The following code is to help Matlab to generate spike trains
24
+ # more easily and allows spaces before and after the "@", e.g.
25
+ # "123 @ 444".
26
+ #
27
+ line.gsub!(/\s+@\s+/, '')
28
+
29
+ id, *spikes = line.split
30
+ raise if spikes.empty?
31
+ entity = @entities[id] || raise
32
+
33
+ spikes.each do |spike|
34
+ weight, at = spike.split("@")
35
+ weight, at = Infinity, weight if at.nil? # spike is a pure time-value
36
+ entity.stimulate(at.to_f, weight.to_f, nil)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,62 @@
1
+ require 'Yinspire/Loaders/Loader'
2
+ require 'Yinspire/Loaders/YinScanner'
3
+
4
+ #
5
+ # This is a human readable data format for describing neural nets
6
+ # as well as stimulations.
7
+ #
8
+ # Note that it is a streaming scanner/parser, i.e. it's important
9
+ # to put the template definitions before you actually use them.
10
+ # The same applies to stimulations and connections!
11
+ #
12
+ class Loader_Yin < Loader
13
+ require 'enumerator'
14
+
15
+ def load(filename)
16
+ templates = {}
17
+ hash = Hash.new
18
+ YinScanner.new(File.read(filename)).scan do |cmd|
19
+ case cmd.shift
20
+ when :entity
21
+ ids, type, prop_list = *cmd
22
+
23
+ hash.clear
24
+ if t = templates[type]
25
+ type, template_data = *t
26
+ hash.update(template_data) if template_data
27
+ end
28
+ hash.update(prop_list) if prop_list
29
+
30
+ ids.each {|id| create_entity(type, id, hash) }
31
+ when :connect
32
+ cmd.first.each_cons(2) do |from, to|
33
+ from.each {|f|
34
+ entity = @entities[f]
35
+ to.each {|t| entity.connect(@entities[t]) }
36
+ }
37
+ end
38
+ when :stimulate
39
+ ids, stimuls = *cmd
40
+
41
+ ids.map! {|id| @entities[id] }
42
+
43
+ stimuls.each do |sti|
44
+ at, weight = *sti
45
+ weight ||= Infinity
46
+ ids.each {|entity|
47
+ entity.stimulate(at, weight, nil)
48
+ }
49
+ end
50
+ when :template
51
+ ids, base_type, prop_list = *cmd
52
+ ids.each do |id|
53
+ raise if templates[id]
54
+ templates[id] = [base_type, prop_list]
55
+ end
56
+ else
57
+ raise
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,247 @@
1
+ class YinScanner
2
+ require 'strscan'
3
+
4
+ def initialize(str)
5
+ @s = StringScanner.new(str)
6
+ @inf = 1.0/0.0
7
+ end
8
+
9
+ def scan
10
+ while cmd = scan_command()
11
+ yield cmd
12
+ end
13
+ skip_ws()
14
+ raise "ParseError" unless @s.eos?
15
+ end
16
+
17
+ protected
18
+
19
+ def skip_ws
20
+ while @s.skip(/(\s+)/) or # skip whitespace
21
+ @s.skip(/[#](.*)/) # skip comments
22
+ end
23
+ end
24
+
25
+ def scan_property
26
+ pos = @s.pos
27
+ if name = scan_id()
28
+ skip_ws()
29
+ if @s.skip(/=/)
30
+ value = scan_value()
31
+ if value != nil
32
+ return name, value
33
+ end
34
+ end
35
+ end
36
+
37
+ @s.pos = pos
38
+ return nil
39
+ end
40
+
41
+ def scan_command
42
+ skip_ws()
43
+ type = nil
44
+ if str = @s.scan(/(TEMPLATE|ENTITY|CONNECT|STIMULATE)\s+/)
45
+ str.strip!
46
+ str.downcase!
47
+ type = str.to_sym
48
+ end
49
+
50
+ ids = scan_idlist()
51
+ raise "ParseError" if type != nil and ids.empty?
52
+ return nil if ids.empty?
53
+
54
+ scanned_type = scan_type()
55
+
56
+ raise "ParseError" if (type and type != scanned_type) or scanned_type.nil?
57
+
58
+ case scanned_type
59
+ when :entity, :template
60
+ id = scan_id()
61
+ raise "ParseError" unless id
62
+ prop_list = scan_propertylist()
63
+ return scanned_type, ids, id, prop_list
64
+ when :stimulate
65
+ stimuls = scan_stimulationlist()
66
+ raise "ParseError" unless stimuls
67
+ return scanned_type, ids, stimuls
68
+ when :connect
69
+ conns = []
70
+ conns << ids
71
+ loop do
72
+ l = scan_idlist()
73
+ break if l.empty?
74
+ conns << l
75
+
76
+ skip_ws()
77
+ break unless @s.skip(/->/)
78
+ end
79
+ raise "ParseError" if conns.size < 2
80
+ return scanned_type, conns
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ def scan_propertylist
87
+ pos = @s.pos
88
+ skip_ws()
89
+ props = {}
90
+ if @s.skip(/[{]/)
91
+ loop do
92
+ name, value = scan_property()
93
+ break unless name
94
+ props[name] = value
95
+ end
96
+ skip_ws()
97
+ return props if @s.skip(/[}]/)
98
+ end
99
+
100
+ @s.pos = pos
101
+ return nil
102
+ end
103
+
104
+ def scan_value
105
+ skip_ws()
106
+ if str = @s.scan(/[+-]?[0-9]+([.][0-9]+([eE][+-]?[0-9]+)?)?/)
107
+ str.to_f
108
+ elsif @s.skip(/[+]?Inf(inity)?/i)
109
+ @inf
110
+ elsif @s.skip(/[-]?Inf(inity)?/i)
111
+ -@inf
112
+ elsif @s.skip(/true/)
113
+ true
114
+ elsif @s.skip(/false/)
115
+ false
116
+ else
117
+ nil
118
+ end
119
+ end
120
+
121
+ # no skip_ws!
122
+ def scan_float
123
+ if str = @s.scan(/[+-]?[0-9]+([.][0-9]+([eE][+-]?[0-9]+)?)?/)
124
+ str.to_f
125
+ elsif @s.skip(/[+]?Inf(inity)?/i)
126
+ @inf
127
+ elsif @s.skip(/[-]?Inf(inity)?/i)
128
+ -@inf
129
+ else
130
+ nil
131
+ end
132
+ end
133
+
134
+ def scan_stimulationlist
135
+ pos = @s.pos
136
+ skip_ws()
137
+ stimuls = []
138
+ if @s.skip(/[{]/)
139
+ loop do
140
+ arr = scan_stimulation()
141
+ break unless arr
142
+ stimuls << arr
143
+ end
144
+ skip_ws()
145
+ return stimuls if @s.skip(/[}]/)
146
+ end
147
+
148
+ @s.pos = pos
149
+ return nil
150
+ end
151
+
152
+ # [weight@]at
153
+ # no skip_ws!
154
+ #
155
+ def scan_stimulation
156
+ pos = @s.pos
157
+ skip_ws()
158
+
159
+ if at = scan_float()
160
+ if @s.skip(/@/)
161
+ weight = at
162
+ if at = scan_float()
163
+ return at, weight
164
+ end
165
+ else
166
+ return at
167
+ end
168
+ end
169
+
170
+ @s.pos = pos
171
+ return nil
172
+ end
173
+
174
+ def scan_id
175
+ pos = @s.pos
176
+ skip_ws()
177
+ if @s.skip(/["]/)
178
+ id = @s.scan(/[^"]+/)
179
+ unless @s.skip(/["]/)
180
+ @s.pos = pos
181
+ id = nil
182
+ end
183
+ else
184
+ id = @s.scan(/\w+/)
185
+ end
186
+ return id
187
+ end
188
+
189
+ def scan_idlist
190
+ ids = []
191
+ loop do
192
+ id = scan_id()
193
+ break unless id
194
+ ids << id
195
+ skip_ws()
196
+ break unless @s.skip(/,/) # we expect a "," here
197
+ end
198
+ ids
199
+ end
200
+
201
+ def scan_type
202
+ skip_ws()
203
+ if @s.skip(/[=]/) then :entity
204
+ elsif @s.skip(/->/) then :connect
205
+ elsif @s.skip(/[!]/) then :stimulate
206
+ elsif @s.skip(/[<]/) then :template
207
+ else
208
+ nil
209
+ end
210
+ end
211
+ end
212
+
213
+ if __FILE__ == $0
214
+ s = YinScanner.new(<<-EOS)
215
+ #
216
+ # This is a comment
217
+ # This also.
218
+ #
219
+ # Command names like "TEMPLATE", "ENTITY", "CONNECT" and "STIMULATE"
220
+ # are optional.
221
+ #
222
+
223
+ TEMPLATE InputType < Neuron_Input {
224
+ const_threshold = +1.2e+200
225
+ last_spike_time = -Infinity
226
+ }
227
+
228
+ ENTITY Input1, "input2", Input2, Input3 = InputType
229
+ ENTITY Input4 = InputType
230
+
231
+ Input5, Input6 = Neuron_SRM01 {
232
+ mem_pot = 10.0
233
+ }
234
+
235
+ Syn1, Syn2, Syn3 = Synapse {
236
+ weight = 2.3
237
+ delay = 0.4
238
+ }
239
+
240
+ CONNECT Input1 -> Syn1, Syn2 -> Input5
241
+
242
+ STIMULATE Input1, Input5 ! {
243
+ 123@4.4 Inf@23.3 4.5 # weight defaults to Infinity
244
+ }
245
+ EOS
246
+ s.scan {|cmd| p cmd}
247
+ end