This file is indexed.

/usr/share/pyshared/brian/stdp.py is in python-brian 1.3.1-1build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
'''
Spike-timing-dependent plasticity
'''
# See BEP-2-STDP
from inspection import *
from equations import *
from monitor import SpikeMonitor, RecentStateMonitor
from network import NetworkOperation
from neurongroup import NeuronGroup
from stateupdater import get_linear_equations, LinearStateUpdater
from scipy.linalg import expm
from scipy import dot, eye, zeros, array, clip, exp, Inf
from stdunits import ms
from connections import DelayConnection, DenseConstructionMatrix, SparseConnectionVector
import re
from utils.documentation import flattened_docstring
from copy import copy
import warnings
from itertools import izip
from numpy import arange, floor
from clock import Clock
from units import second
from utils.separate_equations import separate_equations
from log import *
from globalprefs import *
from optimiser import freeze

__all__ = ['STDP', 'ExponentialSTDP']


class STDPUpdater(SpikeMonitor):
    '''
    Updates STDP variables at spike times
    '''
    def __init__(self, source, C, vars, code, namespace, delay=0 * ms):
        '''
        source = source group
        C = connection
        vars = variable names
        M = matrix of the linear differential system
        code = code to execute for every spike
        namespace = namespace for the code
        delay = transmission delay 
        '''
        super(STDPUpdater, self).__init__(source, record=False, delay=delay)
        self._code = code # update code
        self._namespace = namespace # code namespace
        self.C = C

    def propagate(self, spikes):
        if len(spikes):
            self._namespace['spikes'] = spikes
            self._namespace['w'] = self.C.W
            exec self._code in self._namespace


class DelayedSTDPUpdater(SpikeMonitor):
    def __init__(self, C, reverse, delay_expr, max_delay,
                 vars, other_vars, varmon, othervarmon, code, namespace, delay=0 * ms):
        if reverse:
            source = C.target
            self.get_times_seq = 'get_cols'
        else:
            source = C.source
            self.get_times_seq = 'get_rows'
        super(DelayedSTDPUpdater, self).__init__(source, record=False, delay=delay)
        self._code = code # update code
        self._namespace = namespace # code namespace
        self.C = C
        self.vars = vars
        self.other_vars = other_vars
        self.varmon = varmon
        self.othervarmon = othervarmon
        delay_expr = re.sub(r'\bmax_delay\b', str(float(max_delay)), delay_expr)
        delay_expr = 'lambda d:' + delay_expr
        self.delay_expr = eval(delay_expr)

    def propagate(self, spikes):
        if len(spikes):
            if isinstance(self.get_times_seq, str):
                self.get_times_seq = getattr(self.C.delayvec, self.get_times_seq)
            times_seq = self.get_times_seq(spikes)
            times_seq = [self.delay_expr(times) for times in times_seq]
            for var in self.other_vars:
                delayed_values = self.othervarmon[var].get_past_values_sequence(times_seq)
                self._namespace[var + '__delayed_values_seq'] = delayed_values
            self._namespace['spikes'] = spikes
            self._namespace['w'] = self.C.W
            exec self._code in self._namespace


class STDP(NetworkOperation):
    '''
    Spike-timing-dependent plasticity    

    Initialised with arguments:

    ``C``
        Connection object to apply STDP to.
    ``eqs``
        Differential equations (with units)
    ``pre``
        Python code for presynaptic spikes, use the reserved symbol ``w`` to
        refer to the synaptic weight.
    ``post``
        Python code for postsynaptic spikes, use the reserved symbol ``w`` to
        refer to the synaptic weight.
    ``wmin``
        Minimum weight (default 0), weights are restricted to be within this
        value and wmax.
    ``wmax``
        Maximum weight (default unlimited), weights are restricted to be within
        wmin and this value.
    ``delay_pre``
        Presynaptic delay
    ``delay_post``
        Postsynaptic delay (backward propagating spike)
    
    The STDP object works by specifying a set of differential equations
    associated to each synapse (``eqs``) and two rules to specify what should
    happen when a presynaptic neuron fires (``pre``) and when a postsynaptic
    neuron fires (``post``). The equations should be standard set of equations
    in the usual string format. The ``pre`` and ``post`` rules should be a
    sequence of statements to be executed triggered on pre- and post-synaptic
    spikes. The sequence of statements can be separated by a ``;`` or by
    using a multiline string. The reserved symbol ``w`` can be used to refer
    to the synaptic weight of the associated synapse.
    
    This framework allows you to implement most STDP rules. Specifying
    differential equations and pre- and post-synaptic event code allows for a
    much more efficient implementation than specifying, for example, the
    spike pair weight modification function, but does unfortunately require
    transforming the definition into this form.
    
    There is one restriction on the equations that can be implemented in this
    system, they need to be separable into independent pre- and post-synaptic
    systems (this is done automatically). In this way, synaptic variables and
    updates can be stored per neuron rather than per synapse.
        
    **Example**
    
    ::
    
        eqs_stdp = """
        dA_pre/dt  = -A_pre/tau_pre   : 1
        dA_post/dt = -A_post/tau_post : 1
        """
        stdp = STDP(synapses, eqs=eqs_stdp, pre='A_pre+=delta_A_pre; w+=A_post',
                    post='A_post+=delta_A_post; w+=A_pre', wmax=gmax)

    **STDP variables**
    
    You can access the pre- and post-synaptic variables as follows::
    
        stdp = STDP(...)
        print stdp.A_pre
    
    Alternatively, you can access the group of pre/post-synaptic variables
    as::
    
        stdp.pre_group
        stdp.post_group
    
    These latter attributes can be passed to a :class:`StateMonitor` to
    record their activity, for example. However, note that in the case of
    STDP acting on a connection with heterogeneous delays, the recent values
    of these variables are automatically monitored and these can be
    accesses as follows::
    
        stdp.G_pre_monitors['A_pre']
        stdp.G_post_monitors['A_post']
    
    **Technical details**
    
    The equations are split into two groups, pre and post. Two groups are created
    to carry these variables and to update them (these are implemented as
    :class:`NeuronGroup` objects). As well as propagating spikes from the source
    and target of ``C`` via ``C``, spikes are also propagated to the respective
    groups created. At spike propagation time the weight values are updated.
    '''
    def __init__(self, C, eqs, pre, post, wmin=0, wmax=Inf, level=0, clock=None, delay_pre=None, delay_post=None):
        '''
        C: connection object
        eqs: differential equations (with units)
        pre: Python code for presynaptic spikes
        post: Python code for postsynaptic spikes
        wmax: maximum weight (default unlimited)
        delay_pre: presynaptic delay
        delay_post: postsynaptic delay (backward propagating spike)
        '''
        if get_global_preference('usecstdp') and get_global_preference('useweave'):
            from experimental.c_stdp import CSTDP
            log_warn('brian.stdp', 'Using experimental C STDP class.')
            self.__class__ = CSTDP
            CSTDP.__init__(self, C, eqs, pre, post, wmin=wmin, wmax=wmax,
                           level=level + 1, clock=clock, delay_pre=delay_pre,
                           delay_post=delay_post)
            return
        NetworkOperation.__init__(self, lambda:None, clock=clock)
        # Convert to equations object
        if isinstance(eqs, Equations):
            eqs_obj = eqs
        else:
            eqs_obj = Equations(eqs, level=level + 1)
        # handle multi-line pre, post equations and multi-statement equations separated by ;
        if '\n' in pre:
            pre = flattened_docstring(pre)
        elif ';' in pre:
            pre = '\n'.join([line.strip() for line in pre.split(';')])
        if '\n' in post:
            post = flattened_docstring(post)
        elif ';' in post:
            post = '\n'.join([line.strip() for line in post.split(';')])

        # Check units
        eqs_obj.compile_functions()
        eqs_obj.check_units()
        # Get variable names
        vars = eqs_obj._diffeq_names
        # Find which ones are directly modified (e.g. regular expression matching; careful with comments)
        vars_pre = [var for var in vars if var in modified_variables(pre)]
        vars_post = [var for var in vars if var in modified_variables(post)]

        # additional dependencies are used to ensure that if there are multiple
        # pre/post separated equations they are grouped together as one
        #
        # We should replace with this code here, in case there are no pre/post synaptic variables
        #additional_deps =[]
        #if len(vars_pre)>0:
        #    additional_deps.append('__pre_deps='+'+'.join(vars_pre))
        #if len(vars_post)>0:
        #    additional_deps.append('__post_deps='+'+'.join(vars_post))
        additional_deps = ['__pre_deps='+'+'.join(vars_pre),
                           '__post_deps='+'+'.join(vars_post)]
        separated_equations = separate_equations(eqs_obj, additional_deps)
        if not len(separated_equations) == 2:
            raise ValueError('Equations should separate into pre and postsynaptic variables.')
        sep_pre, sep_post = separated_equations
        for v in vars_pre:
            if v in sep_post._diffeq_names:
                sep_pre, sep_post = sep_post, sep_pre
                break
        index_pre = [i for i in range(len(vars)) if vars[i] in vars_pre or vars[i] in sep_pre._diffeq_names]
        index_post = [i for i in range(len(vars)) if vars[i] in vars_post or vars[i] in sep_post._diffeq_names]

        vars_pre = array(vars)[index_pre]
        vars_post = array(vars)[index_post]

        # Check pre/post consistency
        shared_vars = set(vars_pre).intersection(vars_post)
        if shared_vars != set([]):
            raise Exception, str(list(shared_vars)) + " are both presynaptic and postsynaptic!"

        # Substitute equations/aliases into pre/post code
        def substitute_eqs(code):
            for name in sep_pre._eq_names[-1::-1]+sep_post._eq_names[-1::-1]: # reverse order, as in equations.py
                if name in sep_pre._eq_names:
                    expr = sep_pre._string[name]
                else:
                    expr = sep_post._string[name]
                code = re.sub("\\b" + name + "\\b", '(' + expr + ')', code)
            return code
        pre = substitute_eqs(pre)
        post = substitute_eqs(post)

        # Create namespaces for pre and post codes
        pre_namespace = namespace(pre, level=level + 1)
        post_namespace = namespace(post, level=level + 1)
        pre_namespace['clip'] = clip
        post_namespace['clip'] = clip
        pre_namespace['Inf'] = Inf
        post_namespace['Inf'] = Inf
        pre_namespace['enumerate'] = enumerate
        post_namespace['enumerate'] = enumerate

        # freeze pre and post (otherwise units will cause problems)
        all_vars = list(vars_pre) + list(vars_post) + ['w']
        pre = '\n'.join(freeze(line.strip(), all_vars, pre_namespace) for line in pre.split('\n'))
        post = '\n'.join(freeze(line.strip(), all_vars, post_namespace) for line in post.split('\n'))

        # Neuron groups
        G_pre = NeuronGroup(len(C.source), model=sep_pre, clock=self.clock)
        G_post = NeuronGroup(len(C.target), model=sep_post, clock=self.clock)
        G_pre._S[:] = 0
        G_post._S[:] = 0
        self.pre_group = G_pre
        self.post_group = G_post
        var_group = {} # maps variable name to group
        for v in vars_pre:
            var_group[v] = G_pre
        for v in vars_post:
            var_group[v] = G_post
        self.var_group = var_group

        # Create updaters and monitors
        if isinstance(C, DelayConnection):
            G_pre_monitors = {} # these get values put in them later
            G_post_monitors = {}
            max_delay = C._max_delay * C.target.clock.dt

            def gencode(incode, vars, other_vars, wreplacement):
                num_immediate = num_delayed = 0
                reordering_warning = False
                incode_lines = [line.strip() for line in incode.split('\n')]
                outcode_immediate = 'for _i in spikes:\n'
                # delayed variables
                outcode_delayed = 'for _j, _i in enumerate(spikes):\n'
                for var in other_vars:
                    outcode_delayed += '    ' + var + '__delayed = ' + var + '__delayed_values_seq[_j]\n'
                for line in incode_lines:
                    if not line.strip(): continue
                    m = re.search(r'\bw\b\s*[^><=]?=', line) # lines of the form w = ..., w *= ..., etc.
                    for var in vars:
                        line = re.sub(r'\b' + var + r'\b', var + '[_i]', line)
                    for var in other_vars:
                        line = re.sub(r'\b' + var + r'\b', var + '__delayed', line)
                    if m:
                        num_delayed += 1
                        outcode_delayed += '    ' + line + '\n'
                    else:
                        if num_delayed!=0 and not reordering_warning:
                            log_warn('brian.stdp', 'STDP operations are being re-ordered for delay connection, results may be wrong.')
                            reordering_warning = True
                        num_immediate += 1
                        outcode_immediate += '    ' + line + '\n'
                outcode_delayed = re.sub(r'\bw\b', wreplacement, outcode_delayed)
                outcode_delayed += '\n    %(w)s = clip(%(w)s, %(min)e, %(max)e)' % {'min':wmin, 'max':wmax, 'w':wreplacement}
                return (outcode_immediate, outcode_delayed)

            pre_immediate, pre_delayed = gencode(pre, vars_pre, vars_post, 'w[_i,:]')
            post_immediate, post_delayed = gencode(post, vars_post, vars_pre, 'w[:,_i]')
            log_debug('brian.stdp', 'PRE CODE IMMEDIATE:\n'+pre_immediate)
            log_debug('brian.stdp', 'PRE CODE DELAYED:\n'+pre_delayed)
            log_debug('brian.stdp', 'POST CODE:\n'+post_immediate+post_delayed)
            pre_delay_expr = 'max_delay-d'
            post_delay_expr = 'd'
            pre_code_immediate = compile(pre_immediate, "Presynaptic code immediate", "exec")
            pre_code_delayed = compile(pre_delayed, "Presynaptic code delayed", "exec")
            post_code = compile(post_immediate + post_delayed, "Postsynaptic code", "exec")

            if delay_pre is not None or delay_post is not None:
                raise ValueError("Must use delay_pre=delay_post=None for the moment.")
            max_delay = C._max_delay * C.target.clock.dt
            # Ensure that the source and target neuron spikes are kept for at least the
            # DelayConnection's maximum delay
            C.source.set_max_delay(max_delay)
            C.target.set_max_delay(max_delay)
            # create forward and backward Connection objects or SpikeMonitor objects
            pre_updater_immediate = STDPUpdater(C.source, C, vars=vars_pre,
                                           code=pre_code_immediate, namespace=pre_namespace, delay=0 * ms)
            pre_updater_delayed = DelayedSTDPUpdater(C, reverse=False, delay_expr=pre_delay_expr, max_delay=max_delay,
                                            vars=vars_pre, other_vars=vars_post,
                                            varmon=G_pre_monitors, othervarmon=G_post_monitors,
                                            code=pre_code_delayed, namespace=pre_namespace, delay=max_delay)
            post_updater = DelayedSTDPUpdater(C, reverse=True, delay_expr=post_delay_expr, max_delay=max_delay,
                                            vars=vars_post, other_vars=vars_pre,
                                            varmon=G_post_monitors, othervarmon=G_pre_monitors,
                                            code=post_code, namespace=post_namespace, delay=0 * ms)
            updaters = [pre_updater_immediate, pre_updater_delayed, post_updater]
            self.contained_objects += updaters
            vars_pre_ind = dict((var, i) for i, var in enumerate(vars_pre))
            vars_post_ind = dict((var, i) for i, var in enumerate(vars_post))
            self.G_pre_monitors = G_pre_monitors
            self.G_post_monitors = G_post_monitors
            self.G_pre_monitors.update(((var, RecentStateMonitor(G_pre, vars_pre_ind[var], duration=(C._max_delay + 1) * C.target.clock.dt, clock=G_pre.clock)) for var in vars_pre))
            self.G_post_monitors.update(((var, RecentStateMonitor(G_post, vars_post_ind[var], duration=(C._max_delay + 1) * C.target.clock.dt, clock=G_post.clock)) for var in vars_post))
            self.contained_objects += self.G_pre_monitors.values()
            self.contained_objects += self.G_post_monitors.values()

        else:
            # Indent and loop
            pre = re.compile('^', re.M).sub('    ', pre)
            post = re.compile('^', re.M).sub('    ', post)
            pre = 'for _i in spikes:\n' + pre
            post = 'for _i in spikes:\n' + post

            # Pre code
            for var in vars_pre: # presynaptic variables (vectorisation)
                pre = re.sub(r'\b' + var + r'\b', var + '[_i]', pre)
            pre = re.sub(r'\bw\b', 'w[_i,:]', pre) # synaptic weight
            # Post code
            for var in vars_post: # postsynaptic variables (vectorisation)
                post = re.sub(r'\b' + var + r'\b', var + '[_i]', post)
            post = re.sub(r'\bw\b', 'w[:,_i]', post) # synaptic weight

            # Bounds: add one line to pre/post code (clip(w,min,max,w))
            # or actual code? (rather than compiled string)
            if wmax==Inf:
                pre += '\n    w[_i,:]=clip(w[_i,:],%(min)e,Inf)' % {'min':wmin}
                post += '\n    w[:,_i]=clip(w[:,_i],%(min)e,Inf)' % {'min':wmin}
            else:
                pre += '\n    w[_i,:]=clip(w[_i,:],%(min)e,%(max)e)' % {'min':wmin, 'max':wmax}
                post += '\n    w[:,_i]=clip(w[:,_i],%(min)e,%(max)e)' % {'min':wmin, 'max':wmax}
            log_debug('brian.stdp', 'PRE CODE:\n'+pre)
            log_debug('brian.stdp', 'POST CODE:\n'+post)
            # Compile code
            pre_code = compile(pre, "Presynaptic code", "exec")
            post_code = compile(post, "Postsynaptic code", "exec")

            connection_delay = C.delay * C.source.clock.dt
            if (delay_pre is None) and (delay_post is None): # same delays as the Connnection C
                delay_pre = connection_delay
                delay_post = 0 * ms
            elif delay_pre is None:
                delay_pre = connection_delay - delay_post
                if delay_pre < 0 * ms: raise AttributeError, "Presynaptic delay is too large"
            elif delay_post is None:
                delay_post = connection_delay - delay_pre
                if delay_post < 0 * ms: raise AttributeError, "Postsynaptic delay is too large"
            # create forward and backward Connection objects or SpikeMonitor objects
            pre_updater = STDPUpdater(C.source, C, vars=vars_pre, code=pre_code, namespace=pre_namespace, delay=delay_pre)
            post_updater = STDPUpdater(C.target, C, vars=vars_post, code=post_code, namespace=post_namespace, delay=delay_post)
            updaters = [pre_updater, post_updater]
            self.contained_objects += [pre_updater, post_updater]

        # Put variables in namespaces
        for i, var in enumerate(vars_pre):
            for updater in updaters:
                updater._namespace[var] = G_pre._S[i]
        for i, var in enumerate(vars_post):
            for updater in updaters:
                updater._namespace[var] = G_post._S[i]

        self.contained_objects += [G_pre, G_post]

    def __call__(self):
        pass

    def __getattr__(self, name):
        if name == 'var_group':
            # this seems mad - the reason is that getattr is only called if the thing hasn't
            # been found using the standard methods of finding attributes, which for var_index
            # should have worked, this is important because the next line looks for var_index
            # and if we haven't got a var_index we don't want to get stuck in an infinite
            # loop
            raise AttributeError
        if not hasattr(self, 'var_group'):
            # only provide lookup of variable names if we have some variable names, i.e.
            # if the var_index attribute exists
            raise AttributeError
        G = self.var_group[name]
        return G.state_(name)

    def __setattr__(self, name, val):
        if not hasattr(self, 'var_group') or name not in self.var_group:
            object.__setattr__(self, name, val)
        else:
            G = self.var_group[name]
            G.state_(name)[:] = val


class ExponentialSTDP(STDP):
    '''
    Exponential STDP.
    
    Initialised with the following arguments:
    
    ``taup``, ``taum``, ``Ap``, ``Am``
        Synaptic weight change (relative to the maximum weight wmax)::
        
            f(s) = Ap*exp(-s/taup) if s >0
            f(s) = Am*exp(s/taum) if s <0
                    
    ``interactions``
      * 'all': contributions from all pre-post pairs are added
      * 'nearest': only nearest-neighbour pairs are considered
      * 'nearest_pre': nearest presynaptic spike, all postsynaptic spikes
      * 'nearest_post': nearest postsynaptic spike, all presynaptic spikes
    
    ``wmin=0``
        minimum synaptic weight
        
    ``wmax``
        maximum synaptic weight
        
    ``update``
      * 'additive': modifications are additive (independent of synaptic weight)
        (or "hard bounds")
      * 'multiplicative': modifications are multiplicative (proportional to w)
        (or "soft bounds")
      * 'mixed': depression is multiplicative, potentiation is additive
    
    See documentation for :class:`STDP` for more details.
    '''
    def __init__(self, C, taup, taum, Ap, Am, interactions='all', wmin=0, wmax=None,
                 update='additive', delay_pre=None, delay_post=None, clock=None):
        if wmax is None:
            raise AttributeError, "You must specify the maximum synaptic weight"
        wmax = float(wmax) # removes units

        eqs = Equations('''
        dA_pre/dt=-A_pre/taup : 1
        dA_post/dt=-A_post/taum : 1''', taup=taup, taum=taum, wmax=wmax)
        if interactions == 'all':
            pre = 'A_pre+=Ap'
            post = 'A_post+=Am'
        elif interactions == 'nearest':
            pre = 'A_pre=Ap'
            post = 'A_post=Am'
        elif interactions == 'nearest_pre':
            pre = 'A_pre=Ap'
            post = 'A_post=+Am'
        elif interactions == 'nearest_post':
            pre = 'A_pre+=Ap'
            post = 'A_post=Am'
        else:
            raise AttributeError, "Unknown interaction type " + interactions

        if update == 'additive':
            Ap *= wmax
            Am *= wmax
            pre += '\nw+=A_post'
            post += '\nw+=A_pre'
        elif update == 'multiplicative':
            if Am < 0:
                pre += '\nw*=(1+A_post)'
            else:
                pre += '\nw+=(wmax-w)*A_post'
            if Ap < 0:
                post += '\nw*=(1+A_pre)'
            else:
                post += '\nw+=(wmax-w)*A_pre'
        elif update == 'mixed':
            if Am < 0 and Ap > 0:
                Ap *= wmax
                pre += '\nw*=(1+A_post)'
                post += '\nw+=A_pre'
            elif Am > 0 and Ap < 0:
                Am *= wmax
                post += '\nw*=(1+A_pre)'
                pre += '\nw+=A_post'
            else:
                if Am > 0:
                    raise AttributeError, "There is no depression in STDP rule"
                else:
                    raise AttributeError, "There is no potentiation in STDP rule"
        else:
            raise AttributeError, "Unknown update type " + update
        STDP.__init__(self, C, eqs=eqs, pre=pre, post=post, wmin=wmin, wmax=wmax, delay_pre=delay_pre, delay_post=delay_post, clock=clock)

if __name__ == '__main__':
    pass