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.
- data/README +24 -0
- data/bench/pq/Makefile +5 -0
- data/bench/pq/bench.cc +321 -0
- data/bench/pq/bench.rb +125 -0
- data/bench/pq/bench_binaryheap.h +46 -0
- data/bench/pq/bench_calendarqueue.h +58 -0
- data/bench/pq/bench_pairingheap.h +61 -0
- data/bench/pq/bench_stlpq.h +46 -0
- data/bench/pq/benchmark.h +225 -0
- data/bench/pq/distribution.h +93 -0
- data/bench/pq/make.rb +24 -0
- data/bin/yinspire +186 -0
- data/examples/nets/gereon2005.c.json +93723 -0
- data/examples/nets/gereon2005.yin +232650 -0
- data/examples/nets/skorpion.graphml +396 -0
- data/examples/nets/spiketrains_angle_180.txt +8 -0
- data/lib/Algorithms/Array.h +52 -0
- data/lib/Algorithms/BinaryHeap.h +265 -0
- data/lib/Algorithms/CalendarQueue.h +257 -0
- data/lib/Algorithms/IndexedBinaryHeap.h +90 -0
- data/lib/Algorithms/PairingHeap.h +169 -0
- data/lib/Allocators/ChunkedFreelistAllocator.h +96 -0
- data/lib/Allocators/MemoryAllocator.h +45 -0
- data/lib/Allocators/RubyMemoryAllocator.h +37 -0
- data/lib/Yinspire.rb +69 -0
- data/lib/Yinspire/All.rb +10 -0
- data/lib/Yinspire/Core/NeuralEntity.rb +133 -0
- data/lib/Yinspire/Core/Neuron.rb +162 -0
- data/lib/Yinspire/Core/Scheduling/NeuralEntity.rb +123 -0
- data/lib/Yinspire/Core/Scheduling/Simulator.rb +94 -0
- data/lib/Yinspire/Core/Simulator.rb +36 -0
- data/lib/Yinspire/Core/StimuliMixin.rb +103 -0
- data/lib/Yinspire/Core/Stimulus.rb +25 -0
- data/lib/Yinspire/Core/Synapse.rb +64 -0
- data/lib/Yinspire/Dumpers/Dumper.rb +19 -0
- data/lib/Yinspire/Dumpers/Dumper_Dot.rb +28 -0
- data/lib/Yinspire/Loaders/GraphML.rb +84 -0
- data/lib/Yinspire/Loaders/Loader.rb +31 -0
- data/lib/Yinspire/Loaders/Loader_GraphML.rb +97 -0
- data/lib/Yinspire/Loaders/Loader_JSON.rb +181 -0
- data/lib/Yinspire/Loaders/Loader_Spike.rb +42 -0
- data/lib/Yinspire/Loaders/Loader_Yin.rb +62 -0
- data/lib/Yinspire/Loaders/YinScanner.rb +247 -0
- data/lib/Yinspire/Models/Neuron_Base.rb +38 -0
- data/lib/Yinspire/Models/Neuron_Input.rb +12 -0
- data/lib/Yinspire/Models/Neuron_InputOutput.rb +39 -0
- data/lib/Yinspire/Models/Neuron_Output.rb +15 -0
- data/lib/Yinspire/Models/Neuron_SRM01.rb +50 -0
- data/lib/Yinspire/Models/Neuron_SRM02.rb +64 -0
- data/lib/Yinspire/Models/Synapse_Hebb.rb +67 -0
- data/pure_cpp/Makefile +22 -0
- data/pure_cpp/README +2 -0
- data/pure_cpp/src/algo/binary_heap.h +277 -0
- data/pure_cpp/src/algo/indexed_binary_heap.h +90 -0
- data/pure_cpp/src/json/json.cc +542 -0
- data/pure_cpp/src/json/json.h +182 -0
- data/pure_cpp/src/json/json_parser.cc +685 -0
- data/pure_cpp/src/json/json_parser.h +15 -0
- data/pure_cpp/src/json/json_parser.rl +213 -0
- data/pure_cpp/src/main.cc +49 -0
- data/pure_cpp/src/memory_allocator.h +45 -0
- data/pure_cpp/src/neural_entity.cc +208 -0
- data/pure_cpp/src/neural_entity.h +243 -0
- data/pure_cpp/src/neuron.cc +136 -0
- data/pure_cpp/src/neuron.h +70 -0
- data/pure_cpp/src/neuron_srm_01.cc +77 -0
- data/pure_cpp/src/neuron_srm_01.h +36 -0
- data/pure_cpp/src/simulator.cc +151 -0
- data/pure_cpp/src/simulator.h +116 -0
- data/pure_cpp/src/synapse.cc +117 -0
- data/pure_cpp/src/synapse.h +60 -0
- data/pure_cpp/src/types.h +18 -0
- data/run.rb +68 -0
- data/tools/conv_jsonc_to_yin.rb +165 -0
- data/tools/converter.rb +93 -0
- data/tools/json_writer.rb +122 -0
- data/yinspire.gemspec +20 -0
- metadata +156 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'Yinspire/Core/Neuron'
|
2
|
+
require 'Yinspire/Core/StimuliMixin'
|
3
|
+
|
4
|
+
class Neuron_Base < Neuron
|
5
|
+
|
6
|
+
include StimuliMixin
|
7
|
+
|
8
|
+
#
|
9
|
+
# Duration of the absolute refraction period.
|
10
|
+
#
|
11
|
+
property :abs_refr_duration, 'simtime', :marshal => true
|
12
|
+
|
13
|
+
#
|
14
|
+
# Last spike time
|
15
|
+
#
|
16
|
+
property :last_spike_time, 'simtime', :init => -Infinity, :marshal => true
|
17
|
+
|
18
|
+
#
|
19
|
+
# Last fire time
|
20
|
+
#
|
21
|
+
property :last_fire_time, 'simtime', :init => -Infinity, :marshal => true
|
22
|
+
|
23
|
+
#
|
24
|
+
# Whether this neuron is a hebb neuron or not. A hebb neuron also
|
25
|
+
# stimulates it's pre synapses upon firing.
|
26
|
+
#
|
27
|
+
property :hebb, 'bool', :init => false, :marshal => true
|
28
|
+
|
29
|
+
method :stimulate_synapses, {:at => 'simtime'}, {:weight => 'real'}, %{
|
30
|
+
if (@hebb) stimulate_pre_synapses(at, weight);
|
31
|
+
stimulate_post_synapses(at, weight);
|
32
|
+
}
|
33
|
+
|
34
|
+
method :stimulate, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}, %{
|
35
|
+
stimuli_add(at, weight);
|
36
|
+
}
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'Yinspire/Models/Neuron_Base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Common base class for input and output neurons. They behave almost
|
5
|
+
# the same except for what action is performed once a neuron fires.
|
6
|
+
#
|
7
|
+
class Neuron_InputOutput < Neuron_Base
|
8
|
+
|
9
|
+
virtual :fire
|
10
|
+
method :fire, {:at => 'simtime'},{:weight => 'real'}, nil
|
11
|
+
|
12
|
+
#
|
13
|
+
# Process each stimuli separately, i.e. it does NOT
|
14
|
+
# add stimuli with the same timestamp together.
|
15
|
+
#
|
16
|
+
method :process, {:at => 'simtime'}, %{
|
17
|
+
simtime _at;
|
18
|
+
real _weight;
|
19
|
+
|
20
|
+
while (!@stimuli_pq.empty() && @stimuli_pq.top().at <= at)
|
21
|
+
{
|
22
|
+
_at = @stimuli_pq.top().at;
|
23
|
+
_weight = @stimuli_pq.top().weight;
|
24
|
+
@stimuli_pq.pop();
|
25
|
+
@simulator->record_fire(_at, _weight, this);
|
26
|
+
fire(_at, _weight);
|
27
|
+
}
|
28
|
+
|
29
|
+
/*
|
30
|
+
* NOTE: we don't have to remove the entity from the schedule if the
|
31
|
+
* pq is empty.
|
32
|
+
*/
|
33
|
+
if (!@stimuli_pq.empty())
|
34
|
+
{
|
35
|
+
schedule(@stimuli_pq.top().at);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'Yinspire/Models/Neuron_InputOutput'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Output Neuron. Used to record fire events.
|
5
|
+
#
|
6
|
+
class Neuron_Output < Neuron_InputOutput
|
7
|
+
|
8
|
+
stub_method :fire, {:at => 'simtime'},{:weight => 'real'}
|
9
|
+
|
10
|
+
#
|
11
|
+
# Overwrite
|
12
|
+
#
|
13
|
+
def fire(at, weight) end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'Yinspire/Models/Neuron_Base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Formerly known as KernelbasedLIF
|
5
|
+
#
|
6
|
+
class Neuron_SRM01 < Neuron_Base
|
7
|
+
|
8
|
+
property :tau_m, 'real', :marshal => true
|
9
|
+
property :tau_ref, 'real', :marshal => true
|
10
|
+
property :ref_weight, 'real', :marshal => true
|
11
|
+
property :mem_pot, 'real', :marshal => true
|
12
|
+
property :const_threshold, 'real', :marshal => true
|
13
|
+
|
14
|
+
method :stimulate, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}, %{
|
15
|
+
if (at >= @last_fire_time + @abs_refr_duration)
|
16
|
+
{
|
17
|
+
@simulator->event_counter++;
|
18
|
+
super::stimulate(at, weight, source);
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
method :process, {:at => 'simtime'}, %{
|
23
|
+
real weight = stimuli_sum(at);
|
24
|
+
const real delta = at - @last_fire_time - @abs_refr_duration;
|
25
|
+
|
26
|
+
if (delta < 0.0) return;
|
27
|
+
|
28
|
+
/*
|
29
|
+
* Calculate new membrane potential
|
30
|
+
*/
|
31
|
+
|
32
|
+
@mem_pot = weight + @mem_pot * real_exp( -(at - @last_spike_time)/@tau_m );
|
33
|
+
@last_spike_time = at;
|
34
|
+
|
35
|
+
/*
|
36
|
+
* Calculate dynamic threshold
|
37
|
+
*/
|
38
|
+
const real dynamic_threshold = @ref_weight * real_exp(-delta/@tau_ref);
|
39
|
+
|
40
|
+
if (@mem_pot >= @const_threshold + dynamic_threshold)
|
41
|
+
{
|
42
|
+
/* Fire */
|
43
|
+
@mem_pot = 0.0;
|
44
|
+
@last_fire_time = at;
|
45
|
+
@simulator->record_fire(at, Infinity, this);
|
46
|
+
stimulate_synapses(at, Infinity);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'Yinspire/Models/Neuron_Base'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Formerly known as SpecialEKernel
|
5
|
+
#
|
6
|
+
class Neuron_SRM02 < Neuron_Base
|
7
|
+
|
8
|
+
property :tau_m, 'real', :marshal => true
|
9
|
+
property :tau_ref, 'real', :marshal => true
|
10
|
+
property :reset, 'real', :marshal => true
|
11
|
+
property :u_reset, 'real', :marshal => true
|
12
|
+
property :mem_pot, 'real', :marshal => true
|
13
|
+
property :const_threshold, 'real', :marshal => true
|
14
|
+
|
15
|
+
method :stimulate, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}, %{
|
16
|
+
@simulator->event_counter++;
|
17
|
+
super::stimulate(at, weight, source);
|
18
|
+
}
|
19
|
+
|
20
|
+
method :process, {:at => 'simtime'}, %{
|
21
|
+
real weight = stimuli_sum(at);
|
22
|
+
|
23
|
+
/*
|
24
|
+
* Calculate new membrane potential
|
25
|
+
*/
|
26
|
+
|
27
|
+
@mem_pot = weight + @mem_pot * real_exp( -(at - @last_spike_time)/@tau_m );
|
28
|
+
@last_spike_time = at;
|
29
|
+
|
30
|
+
if (at < @last_fire_time + @abs_refr_duration)
|
31
|
+
return;
|
32
|
+
|
33
|
+
/*
|
34
|
+
* Calculate dynamic reset
|
35
|
+
*/
|
36
|
+
const real delta = at - @last_fire_time - @abs_refr_duration;
|
37
|
+
const real dynamic_reset = @reset * real_exp(-delta/@tau_ref);
|
38
|
+
|
39
|
+
if (@mem_pot >= @const_threshold + dynamic_reset)
|
40
|
+
{
|
41
|
+
/* Fire */
|
42
|
+
|
43
|
+
if (@abs_refr_duration > 0.0)
|
44
|
+
{
|
45
|
+
schedule(at + @abs_refr_duration);
|
46
|
+
}
|
47
|
+
|
48
|
+
if (isinf(@mem_pot))
|
49
|
+
{
|
50
|
+
@mem_pot = 0.0;
|
51
|
+
@reset = @u_reset;
|
52
|
+
}
|
53
|
+
else
|
54
|
+
{
|
55
|
+
@reset = dynamic_reset + @u_reset;
|
56
|
+
}
|
57
|
+
@last_fire_time = at;
|
58
|
+
|
59
|
+
@simulator->record_fire(at, Infinity, this);
|
60
|
+
stimulate_synapses(at, Infinity);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Synapse_Hebb < Synapse
|
2
|
+
|
3
|
+
property :last_post_neuron_fire_time, 'simtime', :init => -Infinity, :marshal => true
|
4
|
+
property :current_post_neuron_fire_time, 'simtime', :init => -Infinity, :marshal => true
|
5
|
+
property :learning_rate, 'real', :init => 0.01, :marshal => true
|
6
|
+
property :decrease_rate, 'real', :init => 0.00005, :marshal => true
|
7
|
+
property :pre_synaptic_spikes, 'Array<simtime>'
|
8
|
+
|
9
|
+
#
|
10
|
+
# Default arguments for learning_window method
|
11
|
+
#
|
12
|
+
DEFAULT_LW_ARGS = "1, 1, 10, 8"
|
13
|
+
|
14
|
+
method :stimulate, {:at => 'simtime'},{:weight => 'real'},{:source => NeuralEntity}, %{
|
15
|
+
if (source != @post_neuron)
|
16
|
+
{
|
17
|
+
@pre_synaptic_spikes.push(at);
|
18
|
+
|
19
|
+
if (@last_post_neuron_fire_time > 0.0)
|
20
|
+
{
|
21
|
+
real delta_time = @last_post_neuron_fire_time - at;
|
22
|
+
real delta_weight = @learning_rate * learning_window(delta_time, #{DEFAULT_LW_ARGS});
|
23
|
+
|
24
|
+
if (@pre_synaptic_spikes.size() > 1)
|
25
|
+
{
|
26
|
+
delta_time = @pre_synaptic_spikes[@pre_synaptic_spikes.size() - 2] - at;
|
27
|
+
}
|
28
|
+
|
29
|
+
delta_weight += @decrease_rate * delta_time;
|
30
|
+
@weight += (1.0 - real_fabs(@weight)) * delta_weight;
|
31
|
+
}
|
32
|
+
|
33
|
+
@post_neuron->stimulate(at+@delay, @weight, this);
|
34
|
+
}
|
35
|
+
else
|
36
|
+
{
|
37
|
+
@last_post_neuron_fire_time = @current_post_neuron_fire_time;
|
38
|
+
@current_post_neuron_fire_time = at;
|
39
|
+
|
40
|
+
real delta_weight = 0.0;
|
41
|
+
|
42
|
+
for (int i=0; i < @pre_synaptic_spikes.size(); i++)
|
43
|
+
{
|
44
|
+
delta_weight += @learning_rate * learning_window(at - @pre_synaptic_spikes[i], #{DEFAULT_LW_ARGS});
|
45
|
+
}
|
46
|
+
|
47
|
+
@weight += (1.0 - real_fabs(@weight)) * delta_weight;
|
48
|
+
@pre_synaptic_spikes.clear();
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
static_method :learning_window,
|
53
|
+
{:delta_x => 'real'},
|
54
|
+
{:pos_ramp => 'real'},{:neg_ramp => 'real'},
|
55
|
+
{:pos_decay => 'real'},{:neg_decay => 'real'},
|
56
|
+
{:returns => 'real'}, %{
|
57
|
+
if (delta_x >= 0)
|
58
|
+
{
|
59
|
+
return (pos_ramp * delta_x * real_exp(-delta_x/pos_decay));
|
60
|
+
}
|
61
|
+
else
|
62
|
+
{
|
63
|
+
return (neg_ramp * delta_x * real_exp(delta_x/neg_decay));
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
end
|
data/pure_cpp/Makefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
CC=g++
|
2
|
+
#PROFILE=-O0 -pg -g
|
3
|
+
CFLAGS=-DNDEBUG -O3 -Winline -Wall -DWITHOUT_MMAP -I${PWD}/src
|
4
|
+
|
5
|
+
DEPS=src/algo/binary_heap.h src/algo/indexed_binary_heap.h src/memory_allocator.h \
|
6
|
+
src/neuron.h src/neuron_srm_01.h src/simulator.h \
|
7
|
+
src/synapse.h src/types.h \
|
8
|
+
src/neural_entity.cc src/neuron.cc src/neuron_srm_01.cc \
|
9
|
+
src/simulator.cc src/synapse.cc \
|
10
|
+
src/main.cc \
|
11
|
+
src/json/json.h src/json/json_parser.h src/json/json.cc src/json/json_parser.cc \
|
12
|
+
Makefile
|
13
|
+
|
14
|
+
inspire: ${DEPS}
|
15
|
+
${CC} ${CFLAGS} `find src -name '*.cc'` -o inspire ${LDFLAGS}
|
16
|
+
|
17
|
+
src/json/json_parser.cc: src/json/json_parser.rl
|
18
|
+
ragel src/json/json_parser.rl | rlgen-cd -o src/json/json_parser.cc
|
19
|
+
|
20
|
+
clean:
|
21
|
+
rm -f inspire
|
22
|
+
|
data/pure_cpp/README
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
/*
|
2
|
+
* An implicit Binary Heap.
|
3
|
+
*
|
4
|
+
* Copyright (c) 2007, 2008 by Michael Neumann (mneumann@ntecs.de)
|
5
|
+
*
|
6
|
+
* NOTE: We start counting from 1 in the elements array!
|
7
|
+
*
|
8
|
+
* Template parameters:
|
9
|
+
*
|
10
|
+
* E: Element type
|
11
|
+
* Alloc: Allocator
|
12
|
+
* Acc: Accessor struct. Defines ordering relation (less).
|
13
|
+
* MIN_CAPA: minimum number of elements
|
14
|
+
*
|
15
|
+
* Example:
|
16
|
+
*
|
17
|
+
* struct Acc
|
18
|
+
* {
|
19
|
+
* static inline bool less(const int& a, const int& b)
|
20
|
+
* {
|
21
|
+
* return a < b;
|
22
|
+
* }
|
23
|
+
* };
|
24
|
+
*
|
25
|
+
* BinaryHeap<int, MemoryAllocator, Acc> heap;
|
26
|
+
* heap.push(4);
|
27
|
+
* heap.pop();
|
28
|
+
* ...
|
29
|
+
*
|
30
|
+
*/
|
31
|
+
|
32
|
+
#ifndef __YINSPIRE__BINARY_HEAP__
|
33
|
+
#define __YINSPIRE__BINARY_HEAP__
|
34
|
+
|
35
|
+
#include <assert.h>
|
36
|
+
|
37
|
+
/*
|
38
|
+
* This is used to be able to keep track of
|
39
|
+
* an elements index in the IndexedBinaryHeap subclass.
|
40
|
+
* Unused in this class.
|
41
|
+
*/
|
42
|
+
template <typename E>
|
43
|
+
struct BinaryHeapDummyIndexer
|
44
|
+
{
|
45
|
+
static inline void index_changed(E& e, unsigned int i)
|
46
|
+
{
|
47
|
+
/* DUMMY */
|
48
|
+
}
|
49
|
+
};
|
50
|
+
|
51
|
+
template <typename E, class Alloc, class Acc=E, class Idx=BinaryHeapDummyIndexer<E>, unsigned int MIN_CAPA=1024>
|
52
|
+
class BinaryHeap
|
53
|
+
{
|
54
|
+
typedef unsigned int I; // index type
|
55
|
+
|
56
|
+
public:
|
57
|
+
|
58
|
+
BinaryHeap()
|
59
|
+
{
|
60
|
+
this->capacity = 0;
|
61
|
+
this->size_ = 0;
|
62
|
+
this->elements = NULL; // we do lazy allocation!
|
63
|
+
}
|
64
|
+
|
65
|
+
~BinaryHeap()
|
66
|
+
{
|
67
|
+
if (this->elements != NULL)
|
68
|
+
{
|
69
|
+
Alloc::free(this->elements+1);
|
70
|
+
}
|
71
|
+
this->elements = NULL;
|
72
|
+
}
|
73
|
+
|
74
|
+
inline E&
|
75
|
+
top() const
|
76
|
+
{
|
77
|
+
assert(this->size > 0);
|
78
|
+
return this->elements[1];
|
79
|
+
}
|
80
|
+
|
81
|
+
void
|
82
|
+
pop()
|
83
|
+
{
|
84
|
+
remove(1);
|
85
|
+
}
|
86
|
+
|
87
|
+
inline void
|
88
|
+
remove(I i)
|
89
|
+
{
|
90
|
+
assert(i <= this->size_);
|
91
|
+
|
92
|
+
//
|
93
|
+
// Element i is removed from the heap and as such becomes
|
94
|
+
// a "bubble" (free element). Move the bubble until
|
95
|
+
// the bubble becomes a leaf element.
|
96
|
+
//
|
97
|
+
Idx::index_changed(this->elements[i], 0); // detach from heap
|
98
|
+
I bubble = move_bubble_down(i);
|
99
|
+
|
100
|
+
//
|
101
|
+
// Now take the last element and insert it at the position of
|
102
|
+
// the bubble. In case the bubble is already the last element we
|
103
|
+
// are done.
|
104
|
+
//
|
105
|
+
if (bubble != this->size_)
|
106
|
+
{
|
107
|
+
insert_and_bubble_up(bubble, this->elements[this->size_]);
|
108
|
+
}
|
109
|
+
--this->size_;
|
110
|
+
}
|
111
|
+
|
112
|
+
void
|
113
|
+
push(const E& element)
|
114
|
+
{
|
115
|
+
if (this->size_ >= this->capacity) resize(2*this->capacity);
|
116
|
+
insert_and_bubble_up(++this->size_, element);
|
117
|
+
}
|
118
|
+
|
119
|
+
inline I
|
120
|
+
size() const
|
121
|
+
{
|
122
|
+
return this->size_;
|
123
|
+
}
|
124
|
+
|
125
|
+
inline bool
|
126
|
+
empty() const
|
127
|
+
{
|
128
|
+
return (this->size_ == 0);
|
129
|
+
}
|
130
|
+
|
131
|
+
/*
|
132
|
+
* A return value of +false+ means that the element wasn't accumulated with
|
133
|
+
* another element and as such has to be inserted into the heap.
|
134
|
+
*/
|
135
|
+
bool
|
136
|
+
accumulate(const E& element, bool (*accum)(E&,const E&,void*), void *data)
|
137
|
+
{
|
138
|
+
I i;
|
139
|
+
|
140
|
+
//
|
141
|
+
// Find the position of the first element that is less than +element+.
|
142
|
+
//
|
143
|
+
for (i = this->size_; i != 0 && Acc::less(element, this->elements[i]); i /= 2);
|
144
|
+
|
145
|
+
assert(i == 0 || Acc::less(this->elements[i], element));
|
146
|
+
|
147
|
+
if (i == 0 || !accum(this->elements[i], element, data))
|
148
|
+
{
|
149
|
+
return false;
|
150
|
+
}
|
151
|
+
return true;
|
152
|
+
}
|
153
|
+
|
154
|
+
/*
|
155
|
+
* Iterate over all elements (non-destructive)
|
156
|
+
*/
|
157
|
+
void
|
158
|
+
each(void (*yield)(const E&, void*), void *data)
|
159
|
+
{
|
160
|
+
for (I i=1; i <= this->size_; i++)
|
161
|
+
{
|
162
|
+
yield(this->elements[i], data);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
protected:
|
167
|
+
|
168
|
+
/*
|
169
|
+
* Insert +element+ into the heap beginning from
|
170
|
+
* +i+ and searching upwards to the root for the
|
171
|
+
* right position (heap ordered) to insert.
|
172
|
+
*
|
173
|
+
* Element at index +i+ MUST be empty, i.e. unused!
|
174
|
+
*/
|
175
|
+
inline void
|
176
|
+
insert_and_bubble_up(I i, const E& element)
|
177
|
+
{
|
178
|
+
for (;i >= 2 && Acc::less(element, this->elements[i/2]); i /= 2)
|
179
|
+
{
|
180
|
+
store_element(i, this->elements[i/2]);
|
181
|
+
}
|
182
|
+
|
183
|
+
// finally store it into the determined hole
|
184
|
+
store_element(i, element);
|
185
|
+
}
|
186
|
+
|
187
|
+
/*
|
188
|
+
* Move the bubble (empty element) at +i+ down in direction
|
189
|
+
* to the leaves. When the bubble reaches a leaf, stop and
|
190
|
+
* return the index of the leaf element which is now empty.
|
191
|
+
*/
|
192
|
+
inline I
|
193
|
+
move_bubble_down(I i)
|
194
|
+
{
|
195
|
+
const I sz = this->size_;
|
196
|
+
I right_child = i * 2 + 1;
|
197
|
+
|
198
|
+
while (right_child <= sz)
|
199
|
+
{
|
200
|
+
if (Acc::less(this->elements[right_child-1], this->elements[right_child]))
|
201
|
+
{
|
202
|
+
--right_child; // minimum child is left child
|
203
|
+
}
|
204
|
+
|
205
|
+
store_element(i, this->elements[right_child]);
|
206
|
+
i = right_child;
|
207
|
+
right_child = i * 2 + 1;
|
208
|
+
}
|
209
|
+
|
210
|
+
//
|
211
|
+
// Edge case (comparison with the last element)
|
212
|
+
//
|
213
|
+
if (right_child-1 == sz)
|
214
|
+
{
|
215
|
+
store_element(i, this->elements[right_child-1]);
|
216
|
+
i = right_child-1;
|
217
|
+
}
|
218
|
+
|
219
|
+
return i;
|
220
|
+
}
|
221
|
+
|
222
|
+
/*
|
223
|
+
* The 0'th element is never used (accessed), so
|
224
|
+
* we allocate only "capacity" elements (instead of capacity+1)
|
225
|
+
* and move the pointer one element before the begin of the
|
226
|
+
* allocated memory.
|
227
|
+
*/
|
228
|
+
void
|
229
|
+
resize(I new_capacity)
|
230
|
+
{
|
231
|
+
E *new_elements;
|
232
|
+
|
233
|
+
if (new_capacity < MIN_CAPA) this->capacity = MIN_CAPA;
|
234
|
+
else this->capacity = new_capacity;
|
235
|
+
|
236
|
+
//
|
237
|
+
// We do lazy allocation!
|
238
|
+
//
|
239
|
+
if (this->elements != NULL)
|
240
|
+
{
|
241
|
+
new_elements = Alloc::realloc_n(this->elements+1, this->capacity);
|
242
|
+
}
|
243
|
+
else
|
244
|
+
{
|
245
|
+
new_elements = Alloc::alloc_n(this->capacity);
|
246
|
+
}
|
247
|
+
|
248
|
+
assert(new_elements != NULL);
|
249
|
+
assert(this->capacity >= this->size);
|
250
|
+
|
251
|
+
//
|
252
|
+
// move pointer so that we "introduce" a zero'th
|
253
|
+
// element.
|
254
|
+
//
|
255
|
+
this->elements = new_elements-1;
|
256
|
+
}
|
257
|
+
|
258
|
+
/*
|
259
|
+
* FIXME: cannot overwrite method in a subclass
|
260
|
+
* The only purpose of this method is that we overwrite it in the
|
261
|
+
* subclass IndexedBinaryHeap to keep track of an elements index.
|
262
|
+
*/
|
263
|
+
inline void
|
264
|
+
store_element(I i, const E& element)
|
265
|
+
{
|
266
|
+
this->elements[i] = element;
|
267
|
+
Idx::index_changed(this->elements[i], i);
|
268
|
+
}
|
269
|
+
|
270
|
+
protected:
|
271
|
+
|
272
|
+
I size_;
|
273
|
+
E *elements;
|
274
|
+
I capacity;
|
275
|
+
};
|
276
|
+
|
277
|
+
#endif
|