File: synapses_create_generator.pyx

package info (click to toggle)
brian 2.9.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,872 kB
  • sloc: python: 51,820; cpp: 2,033; makefile: 108; sh: 72
file content (219 lines) | stat: -rw-r--r-- 8,683 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
{# USES_VARIABLES { _synaptic_pre, _synaptic_post, rand, N,
                 N_pre, N_post, _source_offset, _target_offset } #}
{# WRITES_TO_READ_ONLY_VARIABLES { _synaptic_pre, _synaptic_post, N} #}
{# ITERATE_ALL { _idx } #}
{% extends 'common_group.pyx' %}

######################## TEMPLATE SUPPORT CODE ##############################

{% block template_support_code %}
cdef int _buffer_size = 1024
cdef int[:] _prebuf = _numpy.zeros(_buffer_size, dtype=_numpy.int32)
cdef int[:] _postbuf = _numpy.zeros(_buffer_size, dtype=_numpy.int32)
cdef int _curbuf = 0
cdef int _raw_pre_idx
cdef int _raw_post_idx

cdef void _flush_buffer(buf, dynarr, int buf_len):
    cdef size_t _curlen = dynarr.shape[0]
    cdef size_t _newlen = _curlen+buf_len
    # Resize the array
    dynarr.resize(_newlen)
    # Get the potentially newly created underlying data arrays
    data = dynarr.data
    data[_curlen:_curlen+buf_len] = buf[:buf_len]

{% endblock %}

######################## MAIN CODE ##############################

{% block maincode %}

    cdef int* _prebuf_ptr = &(_prebuf[0])
    cdef int* _postbuf_ptr = &(_postbuf[0])

    global _curbuf

    cdef size_t oldsize = len({{_dynamic__synaptic_pre}})
    cdef size_t newsize

    # The following variables are only used for probabilistic connections
    {% if iterator_func=='sample' %}
    cdef int _iter_sign
    {% if iterator_kwds['sample_size'] == 'fixed' %}
    cdef bool _selection_algo
    cdef set[int] _selected_set = set[int]()
    cdef set[int].iterator _selected_it
    cdef int _n_selected
    cdef int _n_dealt_with
    cdef int _n_total
    cdef double _U
    {% else %}
    cdef bool _jump_algo
    cdef double _log1p, _pconst
    cdef size_t _jump
    {% endif %}
    {% endif %}

    {# For a connect call j='k+i for k in range(0, N_post, 2) if k+i < N_post'
    "j" is called the "result index" (and "_post_idx" the "result index array", etc.)
    "i" is called the "outer index" (and "_pre_idx" the "outer index array", etc.)
    "k" is called the inner variable #}

    # scalar code
    _vectorisation_idx = 1
    {{scalar_code['setup_iterator']|autoindent}}
    {{scalar_code['generator_expr']|autoindent}}
    {{scalar_code['create_cond']|autoindent}}
    {{scalar_code['update']|autoindent}}

    for _{{outer_index}} in range({{outer_index_size}}):
        _raw{{outer_index_array}} = _{{outer_index}} + {{outer_index_offset}}

        {% if not result_index_condition %}
        {{vector_code['create_cond']|autoindent}}
        if not _cond:
            continue
        {% endif %}
        {{vector_code['setup_iterator']|autoindent}}
        {% if iterator_func=='range' %}
        for {{inner_variable}} in range(_iter_low, _iter_high, _iter_step):
        {% elif iterator_func=='sample' %}
        {% if iterator_kwds['sample_size'] == 'fixed' %}
        # Note that the following code is written in a slightly convoluted way,
        # but we have to plug it together with the following code that checks
        # for the fulfillment of the condition.
        _n_selected = 0
        _n_dealt_with = 0
        with _cython.cdivision(True):
            if _iter_step > 0:
                _n_total = (_iter_high - _iter_low - 1) // _iter_step + 1
            else:
                _n_total = (_iter_low - _iter_high - 1) // -_iter_step + 1
            # Value determined by benchmarking, see github PR #1280
            _selection_algo = 1.0*_iter_size / _n_total > 0.06
        if _iter_size > _n_total:
            {% if skip_if_invalid %}
            _iter_size = _n_total
            {% else %}
            raise IndexError(f"Requested sample size {_iter_size} is bigger than the "
                             f"population size {_n_total}.")
            {% endif %}
        elif _iter_size < 0:
            {% if skip_if_invalid %}
            continue
            {% else %}
            raise IndexError(f"Requested sample size {_iter_size} is negative.")
            {% endif %}
        if _selection_algo:
            {{inner_variable}} = _iter_low - _iter_step
        else:
            # For the tracking algorithm, we have to first create all values
            # to make sure they will be iterated in sorted order
            _selected_set.clear()
            while _n_selected < _iter_size:
                _r = <int> (_rand(_vectorisation_idx) * _n_total)
                while not _selected_set.insert(_r).second:  # .second will be False if duplicate
                    _r = <int> (_rand(_vectorisation_idx) * _n_total)
                _n_selected += 1
            _n_selected = 0
            _selected_it = _selected_set.begin()

        while _n_selected < _iter_size:
            if _selection_algo:
                {{inner_variable}} += _iter_step
                # Selection sampling technique
                # See section 3.4.2 of Donald E. Knuth, AOCP, Vol 2,
                # Seminumerical Algorithms
                _n_dealt_with += 1
                _U = _rand(_vectorisation_idx)
                if (_n_total - _n_dealt_with) * _U >= _iter_size - _n_selected:
                    continue
            else:
                {{inner_variable}} = _iter_low + _deref(_selected_it)*_iter_step
                _preinc(_selected_it)
            _n_selected += 1

        {% else %}
        if _iter_p==0:
            continue
        if _iter_step < 0:
            _iter_sign = -1
        else:
            _iter_sign = 1
        _jump_algo = _iter_p<0.25
        if _jump_algo:
            _log1p = log(1-_iter_p)
        else:
            _log1p = 1.0 # will be ignored
        _pconst = 1.0/_log1p
        {{inner_variable}} = _iter_low-_iter_step
        while _iter_sign*({{inner_variable}} + _iter_step) < _iter_sign*_iter_high:
            {{inner_variable}} += _iter_step
            if _jump_algo:
                _jump = <int>(log(_rand(_vectorisation_idx))*_pconst)*_iter_step
                {{inner_variable}} += _jump
                if _iter_sign*{{inner_variable}} >= _iter_sign*_iter_high:
                    break
            else:
                if _rand(_vectorisation_idx)>=_iter_p:
                    continue
        {% endif %}
        {% endif %}

            {{vector_code['generator_expr']|autoindent}}
            _raw{{result_index_array}} = _{{result_index}} + {{result_index_offset}}

            {% if result_index_condition %}
            {% if result_index_used %}
            {# The condition could index outside of array range #}
            if _{{result_index}}<0 or _{{result_index}}>={{result_index_size}}:
                {% if skip_if_invalid %}
                continue
                {% else %}
                # Note that with Jinja using a lot of curly braces, it is a better
                # solution to use the outdated % syntax instead of f-strings here.
                raise IndexError("index {{result_index}}=%d outside allowed range from 0 to %d" % (_{{result_index}}, {{result_index_size}}-1))
                {% endif %}
            {% endif %}
            {{vector_code['create_cond']|autoindent}}
            {% endif %}
            {% if if_expression!='True' and result_index_condition %}
            if not _cond:
                continue
            {% endif %}
            {% if not result_index_used %}
            {# Otherwise, we already checked before #}
            if _{{result_index}}<0 or _{{result_index}}>={{result_index_size}}:
                {% if skip_if_invalid %}
                continue
                {% else %}
                raise IndexError("index j=%d outside allowed range from 0 to %d" % (_{{result_index}}, {{result_index_size}}-1))
                {% endif %}
            {% endif %}
            {{vector_code['update']|autoindent}}

            for _repetition in range(_n):
                _prebuf_ptr[_curbuf] = _pre_idx
                _postbuf_ptr[_curbuf] = _post_idx
                _curbuf += 1
                # Flush buffer
                if _curbuf==_buffer_size:
                    _flush_buffer(_prebuf, {{_dynamic__synaptic_pre}}, _curbuf)
                    _flush_buffer(_postbuf, {{_dynamic__synaptic_post}}, _curbuf)
                    _curbuf = 0

    # Final buffer flush
    _flush_buffer(_prebuf, {{_dynamic__synaptic_pre}}, _curbuf)
    _flush_buffer(_postbuf, {{_dynamic__synaptic_post}}, _curbuf)
    _curbuf = 0  # reset the buffer for the next run

    newsize = len({{_dynamic__synaptic_pre}})
    # now we need to resize all registered variables and set the total number
    # of synapse (via Python)
    _owner._resize(newsize)

    # And update N_incoming, N_outgoing and synapse_number
    _owner._update_synapse_numbers(oldsize)

{% endblock %}