yinspire 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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