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,36 @@
1
+ require 'Yinspire/Core/NeuralEntity'
2
+ require 'Yinspire/Core/Scheduling/Simulator'
3
+
4
+ class Simulator
5
+
6
+ #
7
+ # The tolerance (time difference) up to which local stimuli are
8
+ # accumulated.
9
+ #
10
+ property :stimuli_tolerance, 'simtime', :init => Infinity
11
+
12
+ #
13
+ # Statistics counter
14
+ #
15
+ property :event_counter, 'uint'
16
+ property :fire_counter, 'uint'
17
+
18
+ stub_method :record_fire, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}
19
+
20
+ #
21
+ # Overwrite!
22
+ #
23
+ def record_fire(at, weight, source)
24
+ end
25
+
26
+ attr_reader :entities
27
+
28
+ def initialize
29
+ @entities = Hash.new
30
+ end
31
+
32
+ def run(stop_at=nil)
33
+ schedule_run(stop_at || Infinity)
34
+ end
35
+
36
+ end
@@ -0,0 +1,103 @@
1
+ require 'Yinspire/Core/Stimulus'
2
+
3
+ #
4
+ # Module contains code to store local stimuli in a priority queue. Used
5
+ # by several Neuron models.
6
+ #
7
+ module StimuliMixin; cplus2ruby
8
+
9
+ #
10
+ # Each NeuralEntity has it's own local stimuli priority queue.
11
+ # Neurons make use of this whereas Synapses do not.
12
+ #
13
+ # Nevertheless we put this into the base class for simplicity reasons
14
+ # and as it's quite low overhead (12 bytes).
15
+ #
16
+ property :stimuli_pq, 'BinaryHeap<Stimulus, MemoryAllocator<Stimulus> >'
17
+
18
+ #
19
+ # Returns a Ruby array in the form [at1, weight1, at2, weight2]
20
+ # for +stimuli_pq+.
21
+ #
22
+ method :stimuli_pq_to_a, {:returns => Object}, %{
23
+ VALUE ary = rb_ary_new();
24
+ @stimuli_pq.each(Stimulus::dump_to_a, &ary);
25
+ return ary;
26
+ }
27
+
28
+ #
29
+ # Add a Stimuli to the local priority queue.
30
+ #
31
+ method :stimuli_add, {:at => 'simtime'},{:weight => 'real'}, %{
32
+ Stimulus s; s.at = at; s.weight = weight;
33
+
34
+ if (@simulator->stimuli_tolerance >= 0.0)
35
+ {
36
+ Stimulus *parent = @stimuli_pq.find_parent(s);
37
+
38
+ if (parent != NULL && (s.at - parent->at) <= @simulator->stimuli_tolerance)
39
+ {
40
+ parent->weight += s.weight;
41
+ return;
42
+ }
43
+ }
44
+
45
+ @stimuli_pq.push(s);
46
+ schedule(@stimuli_pq.top().at);
47
+ }
48
+
49
+ #
50
+ # Consume all Stimuli until +till+ and return the sum of the weights.
51
+ #
52
+ method :stimuli_sum, {:till => 'simtime'},{:returns => 'real'}, %{
53
+ real weight = 0.0;
54
+
55
+ while (!@stimuli_pq.empty() && @stimuli_pq.top().at <= till)
56
+ {
57
+ weight += @stimuli_pq.top().weight;
58
+ @stimuli_pq.pop();
59
+ }
60
+
61
+ /*
62
+ * NOTE: we don't have to remove the entity from the schedule if the
63
+ * pq is empty.
64
+ */
65
+ if (!@stimuli_pq.empty())
66
+ {
67
+ schedule(@stimuli_pq.top().at);
68
+ }
69
+
70
+ return weight;
71
+ }
72
+
73
+ #
74
+ # Consume all Stimuli until +till+ and return the sum of the weights.
75
+ # This treats infinitive values specially and instead of summing them,
76
+ # it sets +is_inf+ to +true+.
77
+ #
78
+ method :stimuli_sum_inf, {:till => 'simtime'},{:is_inf => 'bool&'},{:returns => 'real'}, %{
79
+ real weight = 0.0;
80
+ is_inf = false;
81
+
82
+ while (!@stimuli_pq.empty() && @stimuli_pq.top().at <= till)
83
+ {
84
+ if (isinf(@stimuli_pq.top().weight))
85
+ {
86
+ is_inf = true;
87
+ }
88
+ else
89
+ {
90
+ weight += @stimuli_pq.top().weight;
91
+ }
92
+ @stimuli_pq.pop();
93
+ }
94
+
95
+ if (!@stimuli_pq.empty())
96
+ {
97
+ schedule(@stimuli_pq.top().at);
98
+ }
99
+
100
+ return weight;
101
+ }
102
+
103
+ end
@@ -0,0 +1,25 @@
1
+ #
2
+ # The data structure used for storing a fire impluse or any other form
3
+ # of stimulation.
4
+ #
5
+ class Stimulus
6
+
7
+ cplus2ruby :no_wrap => true
8
+ cplus2ruby :order => -1
9
+
10
+ property :at, :simtime
11
+ property :weight, :real
12
+
13
+ static_method :less, {:a => 'const Stimulus&'}, {:b => 'const Stimulus&'}, {:returns => 'bool'}, %{
14
+ return (a.at < b.at);
15
+ }, :inline => true
16
+
17
+ #
18
+ # Appends +at+ and +weight+ to the Ruby array passed as +ary+.
19
+ #
20
+ static_method :dump_to_a, {:s => 'const Stimulus&'},{:ary => 'void*'}, %{
21
+ rb_ary_push(*((VALUE*)ary), rb_float_new(s.at));
22
+ rb_ary_push(*((VALUE*)ary), rb_float_new(s.weight));
23
+ }
24
+
25
+ end
@@ -0,0 +1,64 @@
1
+ require 'Yinspire/Core/NeuralEntity'
2
+ require 'Yinspire/Core/Neuron'
3
+
4
+ #
5
+ # Base class of all Synapses. Defines the structure that is special for
6
+ # a Synapse, i.e. that a Synapse has a pre and a post-Neuron. Also
7
+ # each Synapse has a +weight+ and a +delay+.
8
+ #
9
+ class Synapse < NeuralEntity
10
+
11
+ property :pre_neuron, Neuron
12
+ property :post_neuron, Neuron
13
+
14
+ #
15
+ # Those pointers are part of an internal linked-list that
16
+ # starts at a Neuron and connects all pre-synapses of an Neuron
17
+ # together. In the same way it connects all post-synapses of an
18
+ # Neuron together.
19
+ #
20
+ property :next_pre_synapse, Synapse
21
+ property :next_post_synapse, Synapse
22
+ property :prev_pre_synapse, Synapse
23
+ property :prev_post_synapse, Synapse
24
+
25
+ #
26
+ # The fire weight of a Synapse.
27
+ #
28
+ property :weight, 'real', :marshal => true
29
+
30
+ #
31
+ # The propagation delay of a Synapse.
32
+ #
33
+ property :delay, 'simtime', :marshal => true
34
+
35
+ #
36
+ # Adding a pre synapse. Target must be a Neuron.
37
+ #
38
+ def connect(target)
39
+ target.add_pre_synapse(self)
40
+ end
41
+
42
+ def disconnect(target)
43
+ target.delete_pre_synapse(self)
44
+ end
45
+
46
+ def each_connection
47
+ yield self.post_neuron
48
+ end
49
+
50
+ #
51
+ # Only propagate the stimulation if it doesn't originate from the
52
+ # post Neuron. Stimuli from a post Neuron are handled by a specific
53
+ # Synapse class (e.g. Hebb).
54
+ #
55
+ # NOTE: We ignore the weight parameter that is passed by the Neuron.
56
+ #
57
+ method :stimulate, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}, %{
58
+ if (source != @post_neuron)
59
+ {
60
+ @post_neuron->stimulate(at + @delay, @weight, this);
61
+ }
62
+ }
63
+
64
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Common super class of all Dumpers.
3
+ #
4
+ class Dumper
5
+
6
+ def initialize(simulator)
7
+ @simulator = simulator
8
+ @entities = @simulator.entities
9
+ end
10
+
11
+ def dump_entities
12
+ entities = {}
13
+ @entities.each {|id, entity|
14
+ entities[id] = [entity.entity_type || raise, entity.dump]
15
+ }
16
+ entities
17
+ end
18
+
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'Yinspire/Dumpers/Dumper'
2
+
3
+ #
4
+ # Dumper for the GraphViz[1] dot format.
5
+ #
6
+ # Only dumps the net, not the stimulis.
7
+ #
8
+ # [1]: http://www.graphviz.org/
9
+ #
10
+ class Dumper_Dot < Dumper
11
+
12
+ #
13
+ # NOTE: Unconnected synapses are not shown.
14
+ #
15
+ def dump(out)
16
+ out << "digraph {\n"
17
+ out << "node [shape = circle];\n"
18
+
19
+ @entities.each_value {|entity|
20
+ next unless entity.kind_of?(Neuron)
21
+ entity.each_connection do |syn|
22
+ out << "#{entity.id.inspect} -> #{syn.post_neuron.id.inspect} [label = #{syn.id.inspect} ];\n"
23
+ end
24
+ }
25
+ out << "}\n"
26
+ end
27
+
28
+ end
@@ -0,0 +1,84 @@
1
+ require 'rexml/document'
2
+
3
+ class GraphML
4
+
5
+ class Graph < Struct.new(:id, :data, :nodes, :edges); end
6
+ class Node < Struct.new(:id, :data); end
7
+ class Edge < Struct.new(:id, :data, :source, :target); end
8
+
9
+ attr_reader :graphs
10
+
11
+ def self.parse(io)
12
+ doc = REXML::Document.new(io)
13
+ new().parse(doc)
14
+ end
15
+
16
+ def initialize
17
+ @keys = Hash.new
18
+ @graphs = Hash.new
19
+ @ids = Hash.new
20
+ end
21
+
22
+ def parse(doc)
23
+ parse_keys(doc.root)
24
+ parse_graphs(doc.root)
25
+ return self
26
+ end
27
+
28
+ private
29
+
30
+ def check_id(id)
31
+ raise "duplicate id: #{ id }" if @ids.has_key?(id)
32
+ @ids[id] = true
33
+ id
34
+ end
35
+
36
+ def parse_keys(root)
37
+ root.elements.each('key') do |el|
38
+ id = check_id(el.attributes['id'])
39
+ (@keys[el.attributes['for']] ||= {})[id] = true
40
+ end
41
+ end
42
+
43
+ def parse_graphs(root)
44
+ root.elements.each('graph') do |g|
45
+ graph = GraphML::Graph.new
46
+ graph.id = check_id(g.attributes['id'])
47
+ graph.data = parse_data(g, 'graph')
48
+ graph.nodes = Hash.new
49
+ graph.edges = Hash.new
50
+ @graphs[graph.id] = graph
51
+
52
+ g.elements.each('node') do |el|
53
+ node = GraphML::Node.new
54
+ node.id = check_id(el.attributes['id'])
55
+ node.data = parse_data(el, 'node')
56
+ graph.nodes[node.id] = node
57
+ end
58
+
59
+ g.elements.each('edge') do |el|
60
+ # FIXME: for now always assume directed edges
61
+ edge = GraphML::Edge.new
62
+ edge.id = check_id(el.attributes['id'])
63
+ edge.source = graph.nodes[el.attributes['source']] || raise
64
+ edge.target = graph.nodes[el.attributes['target']] || raise
65
+ edge.data = parse_data(el, 'edge')
66
+ graph.edges[edge.id] = edge
67
+ end
68
+ end
69
+ end
70
+
71
+ def parse_data(root, for_el)
72
+ hash = Hash.new
73
+ root.elements.each('data') do |el|
74
+ key, value = el.attributes['key'], el.text
75
+ raise unless @keys[for_el][key]
76
+ hash[key] = value
77
+ end
78
+ hash
79
+ end
80
+ end
81
+
82
+ if __FILE__ == $0
83
+ p GraphML.parse(File.open("../../../examples/nets/skorpion.graphml"))
84
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Common super class of all Loaders.
3
+ #
4
+ # Uses Cplus2Ruby property annotations to automatically assign
5
+ # properties:
6
+ #
7
+ # property :name, :marshal => true, :init => 123
8
+ #
9
+ class Loader
10
+
11
+ def initialize(simulator)
12
+ @simulator = simulator
13
+ @entities = @simulator.entities
14
+ end
15
+
16
+ protected
17
+
18
+ #
19
+ # Create an object with id +id+ of class +entity_type+, where
20
+ # +entity_type+ is a string.
21
+ #
22
+ # Argument +data+ is a hash that contains the property values.
23
+ #
24
+ def create_entity(entity_type, id, data)
25
+ entity = NeuralEntity.new_from_name(entity_type, id, @simulator)
26
+ entity.load(data)
27
+ raise if @entities[id]
28
+ @entities[id] = entity
29
+ end
30
+
31
+ end
@@ -0,0 +1,97 @@
1
+ #
2
+ # Load a neuronal net from GraphML format.
3
+ #
4
+ # Copyright (c) 2007, 2008 by Michael Neumann (mneumann@ntecs.de)
5
+ #
6
+
7
+ require 'Yinspire/Loaders/Loader'
8
+ require 'Yinspire/Loaders/GraphML'
9
+
10
+ class Loader_GraphML < Loader
11
+
12
+ TYPE_MAP = {
13
+ 'NEURONTYPE_KBLIF' => 'Neuron_SRM01',
14
+ 'NEURONTYPE_EKERNEL' => 'Neuron_SRM02',
15
+ 'SYNAPSE_DEFAULT' => 'Synapse',
16
+ 'SYNAPSE_HEBB' => 'Synapse_Hebb'
17
+ }
18
+
19
+ PARAM_MAP = {
20
+ 'absRefPeriod' => ['abs_refr_duration', 'real'],
21
+ 'neuronLFT' => ['last_fire_time', 'real'],
22
+ 'neuronLSET' => ['last_spike_time', 'real'],
23
+ 'neuron_tauM' => ['tau_m', 'real'],
24
+ 'neuron_tauRef' => ['tau_ref', 'real'],
25
+ 'neuron_constThreshold' => ['const_threshold', 'real'],
26
+ 'neuron_refWeight' => ['ref_weight', 'real'],
27
+ 'neuron_arpTime' => ['abs_refr_duration', 'real'],
28
+ 'synapse_weight' => ['weight', 'real'],
29
+ 'synapse_delay' => ['delay', 'real'],
30
+ 'neuronPSP' => ['mem_pot', 'real'],
31
+ 'neuronReset' => ['reset', 'real'],
32
+ 'neuron_tauM' => ['tau_m', 'real'],
33
+ 'neuron_tauRecov' => ['tau_ref', 'real'],
34
+ 'neuron_uReset' => ['u_reset', 'real'],
35
+ 'neuron_threshold' => ['const_threshold', 'real']
36
+ }
37
+
38
+ def load(file)
39
+ File.open(file) do |f|
40
+ gml = GraphML.parse(f)
41
+ g = gml.graphs.values.first
42
+ default_neuron_type = g.data['graph_default_neuron_type']
43
+ default_synapse_type = g.data['graph_default_synapse_type']
44
+
45
+ #
46
+ # Create Neurons
47
+ #
48
+ g.nodes.each_value {|node|
49
+ create(node.id, node.data, default_neuron_type, 'neuron_type')
50
+ }
51
+
52
+ #
53
+ # Create Synapses
54
+ #
55
+ g.edges.each_value {|edge|
56
+ create(edge.id, edge.data, default_synapse_type, 'synapse_type')
57
+ }
58
+
59
+ #
60
+ # Create Connections between Neurons and Synapses.
61
+ #
62
+ g.edges.each_value {|edge|
63
+ a = @entities[edge.source.id] || raise
64
+ b = @entities[edge.id] || raise
65
+ c = @entities[edge.target.id] || raise
66
+ a.connect(b)
67
+ b.connect(c)
68
+ }
69
+ end
70
+ end
71
+
72
+ protected
73
+
74
+ #
75
+ # Parameter +kind+ is either of "neuron_type" or "synapse_type".
76
+ #
77
+ def create(id, data, default_type, kind)
78
+ entity_type = (TYPE_MAP[data[kind] || default_type] || raise)
79
+ data.delete(kind)
80
+ create_entity(entity_type, id, conv_params(data))
81
+ end
82
+
83
+ def conv_params(data)
84
+ hash = {}
85
+ data.each {|k,v|
86
+ name, type = PARAM_MAP[k] || raise
87
+ case type
88
+ when 'real'
89
+ hash[name] = v.strip.to_f
90
+ else
91
+ raise
92
+ end
93
+ }
94
+ return hash
95
+ end
96
+
97
+ end