-
Notifications
You must be signed in to change notification settings - Fork 8
/
input_ff_rec_learnoutput_nengo_ocl.py
1432 lines (1365 loc) · 93.2 KB
/
input_ff_rec_learnoutput_nengo_ocl.py
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
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
# (c) Sep 2015 Aditya Gilra, EPFL.
"""
learning of arbitrary feed-forward or recurrent transforms
in Nengo simulator
written by Aditya Gilra (c) Sep 2015.
"""
# these give import warning, import them before rate_evolve which converts warnings to errors
import input_rec_transform_nengo_plot as myplot
OCL = True#False # use nengo_ocl or nengo to simulate
if OCL: import nengo_ocl
import nengo
## Note that rate_evolve.py converts warnings to errors
## so import nengo first as importing nengo generates
## UserWarning: IPython.nbformat.current is deprecated.
from rate_evolve import *
import warnings # later I've set the warnings to errors off.
from scipy.integrate import odeint
from scipy.interpolate import interp1d
#import pickle
# pickle constructs the object in memory, use shelve for direct to/from disk
import shelve, contextlib
from os.path import isfile
import os,sys
########################
### Constants/parameters
########################
###
### Overall parameter control ###
###
errorLearning = True # obsolete, leave it True; error-based PES learning OR algorithmic
recurrentLearning = True # obsolete, leave it True; learning on ff+rec both
plastDecoders = False # whether to just have plastic decoders or plastic weights
inhibition = False#True and not plastDecoders # clip ratorOut weights to +ve only and have inh interneurons
learnIfNoInput = False # obsolete, leave False; Learn only when input is off (so learning only on error current)
errorFeedback = False # Forcefeed the error into the network (used only if errorLearning)
# NOTE: Don't feedback error when learning only the output weights
learnFunction = True # obsolete, leave True; whether to learn a non-linear function or a linear matrix
#funcType = 'LinOsc' # if learnFunction, then linear oscillator (same as learnFunction=False)
funcType = 'vanderPol' # if learnFunction, then vanderPol oscillator
#funcType = 'Lorenz' # if learnFunction, then Lorenz system
#funcType = 'robot1' # if learnFunction, then one-link arm robot
# if 'robot' in funcType, then no reset of angle/velocity after each trial
# if 'rob' in funcType, then no input to angle, only torque to velocity
#funcType = 'rob1SL' # if learnFunction, then one-link arm: robot sans limites
initLearned = False and recurrentLearning and not inhibition
# whether to start with learned weights (bidirectional/unclipped)
# currently implemented only for recurrent learning
fffType = '' # whether feedforward function is linear or non-linear
#fffType = '_nonlin' # whether feedforward function is linear or non-linear
#fffType = '_nonlin2' # whether feedforward function is linear or non-linear
testLearned = False # whether to test the learning, uses weights from continueLearning, but doesn't save again.
#testLearnedOn = '_seed2by8.0amplVaryHeights'
#testLearnedOn = '_trials_seed2by50.0amplVaryMultiTime'
testLearnedOn = '_trials_seed2by50.0amplVaryHeightsScaled'
#testLearnedOn = '__' # doesn't load any weights if file not found! use with initLearned say.
# the filename-string of trialClamp+inputType used during learning, for testLearned or continueLearning
saveSpikes = True # save the spikes if testLearned and saveSpikes
if funcType == 'vanderPol' and not testLearned:
trialClamp = True # whether to reset reference and network into trials during learning (or testing if testLearned)
else:
trialClamp = False # whether to reset reference and network into trials during learning (or testing if testLearned)
continueLearning = True#False # whether to load old weights and continue learning from there
# IMP: here this acts to load old weights and start learning only output weights!
copycatLayer = True#False # use copycat layer OR odeint rate_evolve
copycatPreLearned = copycatLayer # load pre-learned weights into InEtoEexpect & EtoEexpect connections OR Wdesired function
# system doesn't learn with Wdesired function i.e. (copycatLayer and not copycatPreLearned), FF connection mismatch issue possibly
#copycatWeightsFile = '../data/ff_ocl_Nexc3000_noinptau_seeds2344_weightErrorCutoff0.0_nodeerr_learn_rec_nocopycat_func_vanderPol_trials_seed2by50.0amplVaryHeightsScaled_10000.0s_endweights.shelve'
copycatWeightsFile = '../data/outlearn_ocl_wt20ms_Nexc3000_seeds2344_weightErrorCutoff0.0_nodeerr_learn_rec_noErrFB_nocopycat_func_vanderPol_trials_seed2by50.0amplVaryHeightsScaled_10000.0s_endweights.shelve'
zeroLowWeights = False # set to zero weights below a certain value
weightErrorCutoff = 0. # Do not pass any abs(error) for weight change below this value
randomInitWeights = False#True and not plastDecoders and not inhibition
# start from random initial weights instead of zeros
# works only for weights, not decoders as decoders are calculated from transform
randomWeightSD = 1e-4 # old - perhaps approx SD of weight distribution EtoE for linear, for InEtoE, Wdyn2/20 is used
#randomWeightSD = 0.045 # this is approx SD of weight distribution EtoE, for InEtoE, Wdyn2/20 is used
# for the vanderPol 0.05 (here initialized ~Gaussian)
#randomWeightSD = 0 # imp: set this to 0 to use scrambled init weights from copycatWeightsFile
weightRegularize = False # include a weight decay term to regularize weights
randomDecodersType = '' # choose one of these
#randomDecodersType = '_random'
#randomDecodersType = '_1plusrandom'
#randomDecodersType = '_xplusrandom'
randomDecodersFactor = 0.625 # x instead of 1, in x+random
randomDecodersAmount = 2. # the relative randomness to 1, in 1plusrandom
sparseWeights = False
sparsityFake = 0.15 # CAUTION: this is only for filename; actual value is set in nengo/builder/connection.py
shuffled_rec = True#False # whether to shuffle rec weights
shuffled_ff = True#False # whether to shuffle ff weights
###
### Nengo model params ###
###
seedR0 = 2 # seed set while defining the Nengo model
seedR1 = 3 # another seed for the first layer
# some seeds give just flat lines for Lorenz! Why?
seedR2 = 4 # another seed for the second layer
# this is just for reproducibility
# seed for the W file is in rate_evolve.py
# output is very sensitive to this seedR
# as I possibly don't have enough neurons
# to tile the input properly (obsolete -- for high dim)
seedR4 = 4#5 # for the nengonetexpect layer to generate reference signal
seedRin = 3#2
np.random.seed([seedRin])# this seed generates the inpfn below (and non-nengo anything random)
tau = 0.02 # second # as for the rate network
#tau = 0.1 # second
# original is 0.02, but 0.1 gives longer response
tau_AMPA = 1e-3 # second # fast E to I connections
spikingNeurons = False # whether to use Ensemble (LIF neurons) or just Node
# the L2 has to be neurons to apply PES learning rule,
# rest can be Ensemble or Node
if spikingNeurons:
neuronType = nengo.neurons.LIF()
# use LIF neurons for all ensembles
else:
#neuronType = nengo.neurons.LIFRate()
# use LIFRate neurons for all ensembles
# only about 10% faster than LIF for same dt=0.001
# perhaps the plasticity calculations overpower
# gave overflow error in synapses.py for dt = 0.01
neuronType = None # use a Node() instead of Ensemble()
# OOPS! doesn't work as the PES rule only works with neurons
# in any case, non-linear proof only works with large number of neurons
###
### choose dynamics evolution matrix ###
###
#init_vec_idx = -1
init_vec_idx = 0 # first / largest response vector
#evolve = 'EI' # eigenvalue evolution
#evolve = 'Q' # Hennequin et al 2014
evolve = 'fixedW' # fixed W: Schaub et al 2015 / 2D oscillator
#evolve = None # no recurrent connections, W=zeros
evolve_dirn = 'arb' # arbitrary normalized initial direction
#evolve_dirn = '' # along a0, i.e. eigvec of response energy matrix Q
#evolve_dirn = 'eigW' # along eigvec of W
#evolve_dirn = 'schurW' # along schur mode of W
# choose between one of the input types for learning (or testing if testLearned)
#inputType = 'inputOsc'
#inputType = 'rampLeave'
#inputType = 'rampLeaveDirnVary'
#inputType = 'rampLeaveDirnVaryNonLin'
#inputType = 'rampLeaveHeights'
#inputType = 'rampLeaveRampHeights'
#inputType = 'rampStep'
#inputType = 'kickStart'
#inputType = 'persistent'
#inputType = 'persconst'
#inputType = 'amplVary'
#inputType = 'amplVaryHeights'
inputType = 'amplVaryHeightsScaled'
#inputType = 'vanderPolfreqVary'
#inputType = 'amplVaryRampHeightsAlt'
#inputType = 'amplVaryMultiTime'
#inputType = 'amplDurnVary'
#inputType = 'nostim'
###
### Load dynamics evolution matrix and stimulus 'direction'
###
M,W,Winit,lambdas,a0s,desc_str = loadW(evolve)
v,w,dir_str = get_relevant_modes(evolve_dirn,W,lambdas,a0s)
y0,y01,y02 = get_stim_dirn(evolve_dirn,v,w,init_vec_idx,W)
#print "Normality check for real W. Frobenius norm of (W^T*W - W*W^T) =",\
# norm(dot(transpose(W),W)-dot(W,transpose(W)))
N = len(v)
B = 2 * (y0-dot(W,y0)) # (I-W)y0
#W = np.zeros(shape=(N,N)) # dy/dt = (W-I).y/tau = -I.y/tau
#W = np.eye(N) # dy/dt = (W-I).y/tau = (I-I).y/tau = 0
rampT = 0.25#0.1 # second
dt = 0.001 # second
###
### recurrent and feedforward connection matrices ###
###
if errorLearning: # PES plasticity on
Tmax = 1000. # second
continueTmax = 10000. # if continueLearning or testLearned,
# then start with weights from continueTmax
# and run the learning/testing for Tmax
reprRadius = 1. # neurons represent (-reprRadius,+reprRadius)
if recurrentLearning: # L2 recurrent learning
#PES_learning_rate = 9e-1 # learning rate with excPES_integralTau = Tperiod
# # as deltaW actually becomes very small integrated over a cycle!
if testLearned:
PES_learning_rate_FF = 1e-15 # effectively no learning
PES_learning_rate_rec = 1e-15 # effectively no learning
else:
PES_learning_rate_FF = 1e-4
PES_learning_rate_rec = 1e-4
if learnFunction:
if funcType == 'vanderPol':
# van der Pol oscillator (relaxation oscillator for mu>>1)
Nexc = 3000 # number of excitatory neurons
mu,scale,taudyn = 2.,1.,0.125#0.25
Wdesired = lambda x: np.array([x[1],mu*(1-scale*x[0]**2)*x[1]-x[0]])\
/taudyn*tau + x
# scaled van der pol equation, using y=dx/dt form
# note the extra tau and 'identity' matrix
reprRadius = 5. # neurons represent (-reprRadius,+reprRadius)
reprRadiusIn = 0.2 # ratorIn is lower than ratorOut since ratorOut integrates the input
Tperiod = 4. # second
rampT = 0.1 # second (shorter ramp for van der Pol)
B /= 3. # reduce input, else longer sharp "down-spike" in reference cannot be reproduced
inputreduction = 50.0 # input reduction factor
elif funcType == 'Lorenz':
# Lorenz attractor (chaos in continuous dynamics needs at least 3-dim and non-linearity)
Nexc = 5000 # number of excitatory neurons
# 200*N was not good enough for Lorenz attractor
N = 3 # number of dimensions of dynamical system
taudyn = 1. # second
reprRadius = 30. # neurons represent (-reprRadius,+reprRadius)
reprRadiusIn = reprRadius/5. # ratorIn is lower than ratorOut since ratorOut integrates the input
#Wdesired = lambda x: np.array((10*(x[1]-x[0]),x[0]*(28-x[2])-x[1],x[0]*x[1]-8./3.*x[2]))*tau + x
# https://pythonhosted.org/nengo/examples/lorenz_attractor.html
# in the above nengo example, they've transformed x[2]'=x[2]-28 to get a zero baseline for x[2]
Wdesired = lambda x: np.array( (10.*(x[1]-x[0]), -x[0]*x[2]-x[1], x[0]*x[1]-8./3.*(x[2]+28.) ) )/taudyn*tau + x
# note the extra tau and 'identity matrix'
#inputType = 'nostim' # no need of an input function i.e. no stimulus
# Lorenz/van der pol attractor is self-sustaining
B = append(B,0.)
if testLearned:
Tperiod = 100. # second per ramp-release-evolve cycle, NA for Lorenz
else:
Tperiod = 20. # second per ramp-release-evolve cycle, NA for Lorenz
# Also Tnolearning = 5*Tperiod
inputreduction = 10.0 # input reduction factor
elif funcType == 'LinOsc':
reprRadius = 1.0
reprRadiusIn = reprRadius/5. # ratorIn is lower than ratorOut since ratorOut integrates the input
# linear oscillator put in as a function
Nexc = 2000 # number of excitatory neurons
taudyn = 0.1 # second
decay_fac = -0.2 # effective decay_tau = taudyn/decay_fac
Wdesired = lambda x: np.array( [decay_fac*x[0]-x[1], x[0]+decay_fac*x[1]] )/taudyn*tau + x
# note the extra tau and 'identity matrix'
Tperiod = 2. # second per ramp-release-evolve cycle
rampT = 0.1 # second (shorter ramp for lin osc)
inputreduction = 8.0 # input reduction factor
elif funcType == 'robot1':
reprRadius = 6.0
reprRadiusIn = reprRadius/5. # ratorIn is lower than ratorOut since ratorOut integrates the input
# one-link robot arm with angle blocked at +-0.6
N = 2
Nexc = 2000 # number of excitatory neurons
#Wdesired = lambda x: np.array( [ x[1], -1e-2/dt*(x[1] if (np.abs(x[0])>0.6 and x[0]*x[1]>0) else 0.) ] )*tau + x
#Wdesired = lambda x: np.array( [ x[1], 0. ] )*tau + x
#Wdesired = lambda x: np.array( [ x[1] - x[0]/1., -x[1]/1. ] )*tau + x
#Wdesired = lambda x: np.array( [ x[1] - x[0]/1., -x[1]/5. - 2e-3/dt*(x[1] if (np.abs(x[0])>0.3 and x[0]*x[1]>0) else 0.) ] )*tau + x
# angular velocity decays to 0
# if angle out of limit and, angle and velocity have same sign
# note the extra tau and 'identity matrix'
Wdesired = lambda x: np.array( [ x[1] - np.sin(x[0])/1., -x[1]/5. - ((x[0]-0.6)/0.5)**2*(x[0]>0.6) + ((x[0]+0.6)/0.5)**2*(x[0]<-0.6)/0.1 ] )*tau + x
# gravity style restoring force on angle --> 0
# drag force on angular velocity
# increasing negative torque acting on angular velocity when angle reaches limit with play of 0.1
#Wdesired = lambda x: np.array( [ x[1] - np.sin(x[0])/1., -x[1]/5. if np.abs(x[0])<0.6 else 0. ] )*tau + x
# hard limit with gravity and velocity drag as above
# NOPES, doesn't work as external torque should also be set to zero, which comes in anyway via inpfn
# use general system rather than ff+rec or put an exponential increase in resisting torque at the limit
#Wdesired = lambda x: np.array( [ x[1] - np.sin(x[0])/1., -x[1]/5. - np.exp((x[0]-2.)/0.1)*(x[0]>0.6) + np.exp((-2.-x[0])/0.1)*(x[0]<-0.6)/0.1 ] )*tau + x
# gravity style restoring force on angle --> 0
# drag force on angular velocity
# exponentially increasing negative torque acting on angular velocity when angle reaches limit with play of 0.1
Tperiod = 1. # second per ramp-release-evolve cycle
rampT = 0.25 # second (shorter ramp for lin osc)
inputreduction = 1.0 # input reduction factor
elif funcType == 'rob1SL':
reprRadius = 6.0
# one-link robot arm which is reset to zero after each trial, so that cyclic angle discontinuities or angle limits don't come into play
N = 2
Nexc = 2000 # number of excitatory neurons
Wdesired = lambda x: np.array( [ x[1] - np.sin(x[0])/1., -x[1]/5. ] )*tau + x
# gravity style restoring force on angle --> 0
# drag force on angular velocity
Tperiod = 1. # second per ramp-release-evolve cycle
rampT = 0.25 # second (shorter ramp for lin osc)
inputreduction = 1.0 # input reduction factor
else:
print("Specify a function type")
sys.exit(1)
else:
Nexc = 1000 # number of excitatory neurons
Wdesired = W
Tperiod = 2. # second per ramp-release-evolve cycle
Wdyn1 = np.zeros(shape=(N,N))
if plastDecoders: # only decoders are plastic
Wdyn2 = np.zeros(shape=(N,N))
else: # weights are plastic, connection is now between neurons
if randomInitWeights:
Wdyn2 = np.random.normal(size=(Nexc,Nexc))*randomWeightSD
else:
Wdyn2 = np.zeros(shape=(Nexc,Nexc))
#Wdyn2 = W
#Wdyn2 = W+np.random.randn(N,N)*np.max(W)/5.
W = Wdesired # used by matevolve2 below
Wtransfer = np.eye(N)
else: # L1 to L2 FF learning
Nexc = 1000 # number of excitatory neurons
PES_learning_rate = 1e-4# works for FF learning
#PES_learning_rate = 1e-10# effectively no learning
Wdyn1 = W
Wdyn2 = np.zeros(shape=(N,N))
if plastDecoders: # only decoders are plastic
Wtransfer = np.zeros(shape=(N,N))
else: # weights are plastic, connection is now between neurons
if randomInitWeights:
Wtransfer = np.random.normal(size=(1000,Nexc))*randomWeightSD
else:
Wtransfer = np.zeros(shape=(1000,Nexc))
if learnFunction: Wdesired = lambda x: (-2*x[0]**2.,10*x[0]**3-2*x[1])
# IMPORTANT! extra -ve sign embedded in tuple
# for -function(pre) going to error ensemble
else: Wdesired = np.eye(N)
Tperiod = 2. # second per ramp-release-evolve cycle
else: # no plasticity, dyn W in layer 1, transfer to L2
Tmax = 30. # second
Nexc = 1000 # number of excitatory neurons
if learnFunction:
if not recurrentLearning:
# simple non-linear feed-forward transform
reprRadius = 1. # neurons represent (-reprRadius,+reprRadius)
Wtransfer = lambda x: (2*x[0]**2.,-10*x[0]**3+2*x[1])
Wdyn2 = np.zeros(shape=(N,N))
else:
if funcType == 'vanderPol':
# van der pol oscillator (relaxation oscillator for mu>>1)
mu,scale,taudyn = 2.,1.,0.25
reprRadius = 5. # neurons represent (-reprRadius,+reprRadius)
Wdyn2 = lambda x: np.array([x[1],mu*(1-scale*x[0]**2)*x[1]-x[0]])\
/taudyn*tau + x
# scaled van der pol equation, using y=dx/dt form
# note the extra tau and 'identity' matrix
W = Wdyn2 # used by matevolve2 below
Wtransfer = lambda x: x # Identity transfer function
Tperiod = 4. # second
elif funcType == 'Lorenz':
# Lorenz attractor (chaos in continuous dynamics needs at least 3-dim and non-linearity)
N = 3
reprRadius = 30. # neurons represent (-reprRadius,+reprRadius)
#Wdyn2 = lambda x: np.array((10*(x[1]-x[0]),x[0]*(28-x[2])-x[1],x[0]*x[1]-8./3.*x[2]))*tau + x
# https://pythonhosted.org/nengo/examples/lorenz_attractor.html
# in the above nengo example, they've transformed x[2]'=x[2]-28 to get a zero baseline for x[2]
Wdyn2 = lambda x: np.array( (10.*(x[1]-x[0]), -x[0]*x[2]-x[1], x[0]*x[1]-8./3.*(x[2]+28.) ) )*tau + x
# note the extra tau and 'identity matrix'
#inputType = 'nostim' # no need of an input function i.e. no stimulus
# Lorenz attractor is self-sustaining
W = Wdyn2 # used by matevolve2 below
Wtransfer = lambda x: x # Identity transfer function
B = append(B,0.)
Tperiod = 20. # second per ramp-release-evolve cycle, NA for Lorenz
# Also Tnolearning = 2*Tperiod
else:
print("Specify a function type")
sys.exit(1)
else:
reprRadius = 1. # neurons represent (-reprRadius,+reprRadius)
Wdyn2 = W
Wtransfer = np.eye(N)
#Wtransfer = np.array([[-1,0],[0,0.5]])
Tperiod = 2. # second per ramp-release-evolve cycle
Wdyn1 = np.zeros(shape=(N,N))
reprRadiusIn = reprRadius/5. # ratorIn is lower than ratorOut since ratorOut integrates the input
Nerror = 200*N # number of error calculating neurons
reprRadiusErr = 0.2 # with error feedback, error is quite small
###
### time params ###
###
weightdt = Tmax/20. # how often to probe/sample weights
Tclamp = 0.5 # time to clamp the ref, learner and inputs after each trial (Tperiod)
Tnolearning = 4*Tperiod
# in last Tnolearning s, turn off learning & weight decay
if Nexc > 10000: assert sys.version_info >= (3,0) # require python3 for large number of neurons
saveWeightsEvolution = False
###
### Generate inputs for L1 ###
###
zerosN = np.zeros(N)
if inputType == 'rampLeave':
## ramp input along y0
inpfn = lambda t: B/rampT*reprRadius if (t%Tperiod) < rampT else zerosN
elif inputType == 'rampLeaveDirnVary':
## ramp input along random directions
# generate unit random vectors on the surface of a sphere i.e. random directions
# http://codereview.stackexchange.com/questions/77927/generate-random-unit-vectors-around-circle
# incorrect to select uniformly from theta,phi: http://mathworld.wolfram.com/SpherePointPicking.html
# CAUTION: I am multiplying by tau below. With ff + rec learning, I possibly don't need it. Change reprRadius of ratorIn then?
if 'rob' in funcType:
Bt = np.zeros(shape=(N,int(Tmax/Tperiod)+1))
Bt[N//2:,:] = np.random.normal(size=(N//2,int(Tmax/Tperiod)+1))
# randomly varying vectors for each Tperiod
#inpfn = lambda t: tau*Bt[:,int(t/Tperiod)]/rampT if (t%Tperiod) < rampT else zerosN
inpfns = [ interp1d(linspace(0,Tmax,int(Tmax/Tperiod)+1),tau*Bt[i,:]/rampT,axis=0,kind='cubic',\
bounds_error=False,fill_value=0.) for i in range(N) ]
inpfn = lambda t: np.array([ inpfns[i](t) for i in range(N) ])
# torque should not depend on reprRadius, unlike for other funcType-s.
else:
Bt = np.random.normal(size=(N,int(Tmax/Tperiod)+1)) # randomly varying vectors for each Tperiod
# multi-dimensional Gaussian distribution goes as exp(-r^2)
# so normalizing by r gets rid of r dependence, uniform in theta, phi
Bt = Bt/np.linalg.norm(Bt,axis=0)/inputreduction # normalize direction vector for each Tperiod
# for arithmetic, here division, arrays are completed / broadcast starting from last dimension
# http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
# can also use np.newaxis: http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
inpfn = lambda t: Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*reprRadius if (t%Tperiod) < rampT else zerosN
elif inputType == 'rampLeaveDirnVaryNonLin':
# ramp input along random directions
Bt = np.random.uniform(-1,1,size=(N,int(Tmax/Tperiod)+1))
# randomly varying vectors for each Tperiod
Bt /= inputreduction
rampT *= 10 # Longer ramp, so that ff transform can be learned.
inpfn = lambda t: Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*0.1*reprRadius if (t%Tperiod) < rampT else zerosN
# proper ramp, not just a square
elif inputType == 'rampLeaveHeights':
## ramp input along random directions with a constant height added
# generate unit random vectors on the surface of a sphere i.e. random directions
# http://codereview.stackexchange.com/questions/77927/generate-random-unit-vectors-around-circle
# incorrect to select uniformly from theta,phi: http://mathworld.wolfram.com/SpherePointPicking.html
Bt = np.random.normal(size=(N,int(Tmax/Tperiod)+1)) # randomly varying vectors for each Tperiod
# multi-dimensional Gaussian distribution goes as exp(-r^2)
# so normalizing by r gets rid of r dependence, uniform in theta, phi
Bt = Bt/np.linalg.norm(Bt,axis=0)/inputreduction # normalize direction vector for each Tperiod
# for arithmetic, here division, arrays are completed / broadcast starting from last dimension
# http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
# can also use np.newaxis: http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
if funcType == 'vanderPol': Bt /= 2.0
heights = np.random.normal(size=(N,int(Tmax/Tperiod)+1))
heights = heights/np.linalg.norm(heights,axis=0)/inputreduction/2.0
if funcType == 'vanderPol': heights /= 2.0
if trialClamp:
inpfn = lambda t: Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*reprRadius*((t%Tperiod)<rampT) + \
heights[:,int(t/Tperiod)]*reprRadius*((t%Tperiod)<(Tperiod-Tclamp))
else:
inpfn = lambda t: Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*reprRadius*((t%Tperiod)<rampT) + \
heights[:,int(t/Tperiod)]*reprRadius
elif inputType == 'rampLeaveRampHeights':
## ramp input along random directions with a constant height added
# generate unit random vectors on the surface of a sphere i.e. random directions
# http://codereview.stackexchange.com/questions/77927/generate-random-unit-vectors-around-circle
# incorrect to select uniformly from theta,phi: http://mathworld.wolfram.com/SpherePointPicking.html
Bt = np.random.normal(size=(N,int(Tmax/Tperiod)+1)) # randomly varying vectors for each Tperiod
# multi-dimensional Gaussian distribution goes as exp(-r^2)
# so normalizing by r gets rid of r dependence, uniform in theta, phi
Bt = Bt/np.linalg.norm(Bt,axis=0)*reprRadiusIn/2. # normalize direction vector for each Tperiod
# for arithmetic, here division, arrays are completed / broadcast starting from last dimension
# http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
# can also use np.newaxis: http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
heights = np.random.uniform(-reprRadiusIn/2.,reprRadiusIn/2.,size=(N,int(Tmax/Tperiod)+1))
if funcType == 'vanderPol': heights[0,:]/=3.
if '_nonlin' in fffType:
Bt /= 3.
heights /= 2.
if trialClamp:
inpfn = lambda t: heights[:,int(t/Tperiod)]*((t%Tperiod)<(Tperiod-Tclamp)) \
+ Bt[:,int(t/Tperiod)]*((t%Tperiod)<rampT)
else:
#heightsfunc = interp1d(linspace(0,Tmax,int(Tmax/Tperiod)+1),heights,axis=1,kind='nearest')
inpfn = lambda t: Bt[:,int(t/Tperiod)]*((t%Tperiod)<rampT) + \
heights[:,int(t/Tperiod)]
#heightsfunc(t)
elif inputType == 'rampStep':
## ramp input along random directions with a step jump at the end
# generate unit random vectors on the surface of a sphere i.e. random directions
# http://codereview.stackexchange.com/questions/77927/generate-random-unit-vectors-around-circle
# incorrect to select uniformly from theta,phi: http://mathworld.wolfram.com/SpherePointPicking.html
Bt = np.random.normal(size=N) # randomly varying vectors for each Tperiod
# multi-dimensional Gaussian distribution goes as exp(-r^2)
# so normalizing by r gets rid of r dependence, uniform in theta, phi
Bt = Bt/np.linalg.norm(Bt,axis=0)*reprRadiusIn/2. # normalize direction vector for each Tperiod
# for arithmetic, here division, arrays are completed / broadcast starting from last dimension
# http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
# can also use np.newaxis: http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
Bt = np.array([0.075,0.05])
heights = np.random.uniform(-reprRadiusIn/2.,reprRadiusIn/2.,size=N)
heights = np.array([0.025,-0.025])
if funcType == 'vanderPol': heights[0,:]/=3.
#if fffType == '_nonlin':
# Bt /= 3.
# heights /= 2.
inpfn = lambda t: Bt*(t<=Tperiod)*t/Tperiod + heights*(t>Tperiod)
elif inputType == 'kickStart':
## ramp input along y0 only once initially, for self sustaining func-s
inpfn = lambda t: B/inputreduction*t/rampT*reprRadius if t < rampT else zerosN
elif inputType == 'persistent':
## decaying ramp input along y0,
inpfn = lambda t: exp(-(t%Tperiod)/Tperiod)*B/rampT*reprRadius \
if (t%(Tperiod/5.)) < rampT else zerosN
# Repeat a ramp 5 times within Tperiod
# with a decaying envelope of time const Tperiod
# This whole sequence is periodic with Tperiod
elif inputType == 'persconst':
## ramp input along y0 with a constant offset at other times
constN = np.ones(N)*3
inpfn = lambda t: B/rampT*reprRadius if (t%Tperiod) < rampT else constN
elif inputType == 'amplVary':
## random uniform 'white-noise' with 10 ms steps interpolated by cubic
## 50ms is longer than spiking-network response-time, and assumed shorter than tau-s of the dynamical system.
noisedt = 50e-3
# cubic interpolation for long sim time takes up ~64GB RAM and then hangs, so linear or nearest interpolation.
noiseN = np.random.uniform(-reprRadiusIn/2.,reprRadiusIn/2.,size=(N,int(Tmax/noisedt)+1))
noisefunc = interp1d(np.linspace(0,Tmax,int(Tmax/noisedt)+1),noiseN,kind='nearest',\
bounds_error=False,fill_value=0.,axis=1)
del noiseN
if trialClamp:
inpfn = lambda t: noisefunc(t) * ((t%Tperiod)<(Tperiod-Tclamp))
else:
inpfn = noisefunc
elif inputType == 'amplVaryHeights':
# Gaussian in 2D with normalization ensures unit vector length but arbitrary directions.
heights = np.random.normal(size=(N,int(Tmax/Tperiod)+1))
heights = heights/np.linalg.norm(heights,axis=0)/inputreduction/2.0
if funcType == 'vanderPol': heights /= 4.0
## random uniform 'white-noise' with 10 ms steps interpolated by cubic
## 50ms is longer than spiking-network response-time, and assumed shorter than tau-s of the dynamical system.
noisedt = 50e-3
# cubic interpolation for long sim time takes up ~64GB RAM and then hangs, so linear or nearest interpolation.
noiseN = np.random.uniform(-reprRadiusIn/2.,reprRadiusIn/2.,size=(N,int(Tmax/noisedt)+1))
if funcType == 'LinOsc': noiseN /= 3.
noisefunc = interp1d(np.linspace(0,Tmax,int(Tmax/noisedt)+1),noiseN,kind='nearest',\
bounds_error=False,fill_value=0.,axis=1)
del noiseN
if trialClamp:
inpfn = lambda t: (noisefunc(t) + heights[:,int(t/Tperiod)]*reprRadius) * ((t%Tperiod)<(Tperiod-Tclamp))
else:
inpfn = lambda t: noisefunc(t) + heights[:,int(t/Tperiod)]*reprRadius
elif inputType == 'amplVaryHeightsScaled':
heights = np.random.normal(size=(N,int(Tmax/Tperiod)+1))
heights = heights/np.linalg.norm(heights,axis=0)
if funcType == 'vanderPol': heights[0,:]/=3.
## random uniform 'white-noise' with 10 ms steps interpolated by cubic
## 50ms is longer than spiking-network response-time, and assumed shorter than tau-s of the dynamical system.
noisedt = 50e-3
# cubic interpolation for long sim time takes up ~64GB RAM and then hangs, so linear or nearest interpolation.
noiseN = np.random.uniform(-reprRadiusIn,reprRadiusIn,size=(N,int(Tmax/noisedt)+1))
if funcType == 'vanderPol': noiseN[0,:]/=3.
noisefunc = interp1d(np.linspace(0,Tmax,int(Tmax/noisedt)+1),noiseN,kind='nearest',\
bounds_error=False,fill_value=0.,axis=1)
del noiseN
if trialClamp:
inpfn = lambda t: noisefunc(t)/2. * np.float((t%Tperiod)<(Tperiod-Tclamp)) + \
( heights[:,int(t/Tperiod)]*reprRadiusIn )/2.
else:
inpfn = lambda t: ( noisefunc(t) + heights[:,int(t/Tperiod)]*reprRadiusIn )/2.
elif inputType == 'vanderPolfreqVary':
heights = np.zeros(shape=(N,int(Tmax/Tperiod)+1))
heights[0,:] = np.linspace(-1./3.,0,int(Tmax/Tperiod)+1)
Bt = np.array([0.707,0.707])*reprRadiusIn
if trialClamp:
inpfn = lambda t: ( heights[:,int(t/Tperiod)]*reprRadiusIn )/2. \
* np.float((t%Tperiod)<(Tperiod-Tclamp)) \
+ Bt*np.float((t%Tperiod)<rampT)
else:
inpfn = lambda t: ( heights[:,int(t/Tperiod)]*reprRadiusIn )/2. \
+ Bt*np.float((t%Tperiod)<rampT)
elif inputType == 'amplVaryRampHeightsAlt':
## ramp input along random directions with a constant height added
# generate unit random vectors on the surface of a sphere i.e. random directions
# http://codereview.stackexchange.com/questions/77927/generate-random-unit-vectors-around-circle
# incorrect to select uniformly from theta,phi: http://mathworld.wolfram.com/SpherePointPicking.html
Bt = np.random.normal(size=(N,int(Tmax/Tperiod)+1)) # randomly varying vectors for each Tperiod
# multi-dimensional Gaussian distribution goes as exp(-r^2)
# so normalizing by r gets rid of r dependence, uniform in theta, phi
Bt = Bt/np.linalg.norm(Bt,axis=0)/inputreduction/2.0# normalize direction vector for each Tperiod
# for arithmetic, here division, arrays are completed / broadcast starting from last dimension
# http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
# can also use np.newaxis: http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
heights = np.random.normal(size=(N,int(Tmax/Tperiod)+1))
heights = heights/np.linalg.norm(heights,axis=0)/inputreduction/2.0
if funcType == 'vanderPol': heights /= 2.0
## random uniform 'white-noise' with 10 ms steps interpolated by cubic
## 50ms is longer than spiking-network response-time, and assumed shorter than tau-s of the dynamical system.
noisedt = 50e-3
# cubic interpolation for long sim time takes up ~64GB RAM and then hangs, so linear or nearest interpolation.
noiseN = np.random.uniform(-reprRadiusIn/2.,reprRadiusIn/2.,size=(N,int(Tmax/noisedt)+1))
noisefunc = interp1d(np.linspace(0,Tmax,int(Tmax/noisedt)+1),noiseN,kind='nearest',\
bounds_error=False,fill_value=0.,axis=1)
del noiseN
if trialClamp:
inpfn = lambda t: ( noisefunc(t)*(int(t/Tperiod)%2==0) + \
heights[:,int(t/Tperiod)]*reprRadius ) * ((t%Tperiod)<(Tperiod-Tclamp)) + \
Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*reprRadius * ((t%Tperiod)<rampT)*(int(t/Tperiod)%2!=0)
else:
inpfn = lambda t: noisefunc(t)*(int(t/Tperiod)%2==0) + \
heights[:,int(t/Tperiod)]*reprRadius + \
Bt[:,int(t/Tperiod)]*(t%Tperiod)/rampT*reprRadius * ((t%Tperiod)<rampT)*(int(t/Tperiod)%2!=0)
elif inputType == 'amplVaryMultiTime':
NTimeScales = 5
amplPerScale = reprRadiusIn/2./np.float(NTimeScales)
# 50ms is longer than spiking-network response-time, and assumed shorter than tau-s of the dynamical system.
baseNoisedts = linspace(100e-3,Tperiod,NTimeScales)
noiseFuncs = []
NTimeList = range(NTimeScales)
for i in NTimeList:
noiseN = np.random.uniform(-amplPerScale,amplPerScale,size=(N,int(Tmax/baseNoisedts[i])+1))
# cubic interpolation for long sim time takes up ~64GB RAM and then hangs, so linear or nearest interpolation.
noiseFuncs.append( interp1d(np.linspace(0,Tmax,int(Tmax/baseNoisedts[i])+1),noiseN,kind='linear',\
bounds_error=False,fill_value=0.,axis=1) )
del noiseN
if trialClamp:
inpfn = lambda t: np.float((t%Tperiod)<(Tperiod-Tclamp)) \
* np.sum([noiseFuncs[i](t) for i in NTimeList],axis=0)
else:
inpfn = lambda t: np.sum([noiseFuncs[i](t) for i in NTimeList],axis=0)
elif inputType == 'amplDurnVary':
## random uniform 'white-noise', with duration of each value also random
noiseN = np.random.uniform(-2*reprRadius,2*reprRadius,size=int(1200./rampT))
durationN = np.random.uniform(rampT,Tperiod,size=int(1200./rampT))
cumDurationN = np.cumsum(durationN)
# searchsorted returns the index where t should be placed in sort order
inpfn = lambda t: (noiseN[np.searchsorted(cumDurationN,t)]*B*reprRadius/rampT) \
if t<(Tmax-Tnolearning) else \
(B/rampT*reprRadius if (t%Tperiod) < rampT else zerosN)
elif inputType == 'inputOsc':
## oscillatory input in all input dimensions
omegas = 2*np.pi*np.random.uniform(1,3,size=N) # 1 to 3 Hz
phis = 2*np.pi*np.random.uniform(size=N)
inpfn = lambda t: np.cos(omegas*t+phis)
else:
inpfn = lambda t: 0.0*np.ones(N)*reprRadius # constant input, currently zero
#inpfn = None # zero input
# nengo_ocl and odeint generate some warnings, so we reverse the 'warnings to errors' from rate_evolve.py
warnings.filterwarnings('ignore')
###
### Reference evolution used when copycat layer is not used for reference ###
###
if Wdesired.__class__.__name__=='function':
if 'robot' in funcType:
def matevolve2(y,t):
return ((Wdesired(y)-y)/tau + inpfn(t)/tau)
else:
def matevolve2(y,t):
if fffType == '_nonlin':
inpfnVal = 5.*2.*((inpfn(t)/0.1/reprRadius)**3 - inpfn(t)/0.1/reprRadius)
# 5*2*( (u/0.1)**3 - u/0.1 ) input transform
# fixed points at u=0,0.1,0.1
# 2* to scale between 0 and 1; 5* to get x to cover reprRadius of 0.25
#inpfnVal = 5.*inpfn(t)/0.1/reprRadius # without the non-linearity to compare
elif fffType == '_nonlin2':
inpfnVal = 5.*2.*((inpfn(t)/0.1/reprRadius)**3 - inpfn(t)/0.4/reprRadius)
else: inpfnVal = inpfn(t)/tau
if trialClamp: return ( ((Wdesired(y)-y)/tau + inpfnVal) if (t%Tperiod)<(Tperiod-Tclamp) else -y/dt )
else: return ((Wdesired(y)-y)/tau + inpfnVal)
# on integration, 'rampLeave' becomes B*t/rampT*reprRadius
# for Tclamp at the end of the trial, clamp the output to zero
else:
eyeN = np.eye(N)
def matevolve2(y,t):
return dot((Wdesired-eyeN)/tau,y) + inpfn(t)/tau
# on integration, 'rampLeave' becomes B*t/rampT*reprRadius
trange = arange(0.0,Tmax,dt)
y = odeint(matevolve2,0.001*np.ones(N),trange,hmax=dt) # set hmax=dt, to avoid adaptive step size
# some systems (van der pol) have origin as a fixed pt
# hence start just off-origin
rateEvolveFn = interp1d(trange,y,axis=0,kind='linear',\
bounds_error=False,fill_value=0.)
# used for the error signal below
#plt.figure()
#inpfnt = np.array([inpfn(t) for t in trange])
#plt.plot(trange,inpfnt,'r')
#plt.plot(trange, 5.*2.*((inpfnt/0.1/reprRadius)**3 - inpfnt/0.4/reprRadius),'b')
#plt.figure()
#plt.plot(trange,y)
#plt.show()
#sys.exit()
del y # free some memory
if errorLearning:
if not weightRegularize:
excPES_weightsDecayRate = 0. # no decay of PES plastic weights
else:
excPES_weightsDecayRate = 1./1e1#1./1e4 # 1/tau of synaptic weight decay for PES plasticity
#if excPES_weightsDecayRate != 0.: PES_learning_rate /= excPES_weightsDecayRate
# no need to correct PES_learning_rate,
# it's fine in ElementwiseInc in builders/operator.py
#excPES_integralTau = 1. # tau of integration of deltaW for PES plasticity
excPES_integralTau = None # don't integrate deltaW for PES plasticity (default)
# for generating the expected response signal for error computation
errorAverage = False # whether to average error over the Tperiod scale
# Nopes, this won't make it learn the intricate dynamics
#tauErrInt = tau*5 # longer integration tau for err -- obsolete (commented below)
errorFeedbackGain = 10. # Feedback gain
#errorFeedbackGain = 10000. # Feedback gain - for larger initial weights
#errorFeedbackGain = 100. # Feedback gain - for larger initial weights
# below a gain of ~5, exc rates go to max, weights become large
weightErrorTau = 1*tau # filter the error to the PES weight update rule
errorFeedbackTau = 1*tau # synaptic tau for the error signal into layer2.ratorOut
# even if copycatLayer is True, you need this filtering, else too noisy spiking and cannot learn
errorGainDecay = False # whether errorFeedbackGain should decay exponentially to zero
# decaying gain gives large weights increase below some critical gain ~3
errorGainDecayRate = 1./200. # 1/tau for decay of errorFeedbackGain if errorGainDecay is True
errorGainProportion = False # scale gain proportionally to a long-time averaged |error|
errorGainProportionTau = Tperiod # time scale to average error for calculating feedback gain
weightsFileName = "../data/gilra/tmp/learnedWeights.shelve"
if errorLearning and recurrentLearning:
inhVSG_weightsDecayRate = 1./40.
else:
inhVSG_weightsDecayRate = 1./2. # 1/tau of synaptic weight decay for VSG plasticity
#inhVSG_weightsDecayRate = 0. # no decay of inh VSG plastic weights
#pathprefix = '/lcncluster/gilra/tmp/'
pathprefix = '../data/'
inputStr = ('_trials' if trialClamp else '') + \
('_seed'+str(seedRin)+'by'+str(inputreduction)+inputType if inputType != 'rampLeave' else '')
baseFileName = pathprefix+'outlearn'+('_ocl' if OCL else '')+'_Nexc'+str(Nexc) + \
'_seeds'+str(seedR0)+str(seedR1)+str(seedR2)+str(seedR4) + \
fffType + \
('_inhibition' if inhibition else '') + \
('_zeroLowWeights' if zeroLowWeights else '') + \
'_weightErrorCutoff'+str(weightErrorCutoff) + \
('_randomInitWeights'+str(randomWeightSD) if randomInitWeights else '') + \
('_weightRegularize'+str(excPES_weightsDecayRate) if weightRegularize else '') + \
'_nodeerr' + ('_plastDecoders' if plastDecoders else '') + \
( ( '_learn' + \
('_rec' if recurrentLearning else '_ff') + \
('' if errorFeedback else '_noErrFB') \
) if errorLearning else '_algo' ) + \
('_initLearned' if initLearned else '') + \
(randomDecodersType + (str(randomDecodersAmount)+'_'+str(randomDecodersFactor)\
if 'plusrandom' in randomDecodersType else '')) + \
('_sparsity'+str(sparsityFake) if sparseWeights else '') + \
('_learnIfNoInput' if learnIfNoInput else '') + \
(('_precopy' if copycatPreLearned else '') if copycatLayer else '_nocopycat') + \
('_func_'+funcType if learnFunction else '') + \
(testLearnedOn if (testLearned or continueLearning) else inputStr)
# filename to save simulation data
dataFileName = baseFileName + \
('_continueFrom'+str(continueTmax)+inputStr if continueLearning else '') + \
('_testFrom'+str(continueTmax)+inputStr if testLearned else '') + \
('_sflrec' if shuffled_rec else '') + \
('_sflff' if shuffled_ff else '') + \
'_'+str(Tmax)+'s'
print('data will be saved to', dataFileName, '_<start|end|currentweights>.shelve')
if continueLearning or testLearned:
weightsSaveFileName = baseFileName + '_'+str(continueTmax+Tmax)+'s_endweights.shelve'
weightsLoadFileName = baseFileName + '_'+str(continueTmax)+'s_endweights.shelve'
else:
weightsSaveFileName = baseFileName + '_'+str(Tmax)+'s_endweights.shelve'
weightsLoadFileName = baseFileName + '_'+str(Tmax)+'s_endweights.shelve'
if __name__ == "__main__":
#########################
### Create Nengo network
#########################
print('building model')
mainModel = nengo.Network(label="Single layer network", seed=seedR0)
with mainModel:
nodeIn = nengo.Node( size_in=N, output = lambda timeval,currval: inpfn(timeval) )
# input layer from which feedforward weights to ratorOut are computed
ratorIn = nengo.Ensemble( Nexc, dimensions=N, radius=reprRadiusIn,
neuron_type=nengo.neurons.LIF(), max_rates=nengo.dists.Uniform(200, 400), seed=seedR1, label='ratorIn' )
nengo.Connection(nodeIn, ratorIn, synapse=None) # No filtering here as no filtering/delay in the plant/arm
# another layer with learning incorporated
ratorOut = nengo.Ensemble( Nexc, dimensions=N, radius=reprRadius,
neuron_type=nengo.neurons.LIF(), max_rates=nengo.dists.Uniform(200, 400), seed=seedR2, label='ratorOut' )
if trialClamp:
# clamp ratorIn and ratorOut at the end of each trial (Tperiod) for 100ms.
# Error clamped below during end of the trial for 100ms.
clampValsZeros = np.zeros(Nexc)
clampValsNegs = -100.*np.ones(Nexc)
endTrialClamp = nengo.Node(lambda t: clampValsZeros if (t%Tperiod)<(Tperiod-Tclamp) else clampValsNegs)
nengo.Connection(endTrialClamp,ratorIn.neurons,synapse=1e-3)
nengo.Connection(endTrialClamp,ratorOut.neurons,synapse=1e-3)
# fast synapse for fast-reacting clamp
if inhibition and not plastDecoders: # excClipType='clip<0' only works with weights
Ninh = Nexc/4
IreprRadius = 1.0
inhibrator = nengo.Ensemble( Ninh, dimensions=1,\
intercepts=np.random.uniform(-0.1*IreprRadius,IreprRadius,size=Ninh),\
encoders=np.ones(shape=(Ninh,1))*IreprRadius,radius=IreprRadius,\
neuron_type=nengo.neurons.LIF(),seed=seedR2)
# only represents biasing function f(x) hence dimension = 1
# some neurons have negative intercept # i.e. baseline firing,
# encoders from f(x) to neurons are all 1 (a la Parisien et al 2008)
excClipType = 'clip<0'
phi_f = 1.0/(Nexc*400.0) / 1.5 # a positive constant to scale
# biasing function f(ExcActivityVec) between 0 and 1
# max firing of Nexc neurons is 400Hz, /1.5 adhoc,
# this ensures f between 0 and 1
EtoI = nengo.Connection(ratorOut.neurons, inhibrator,\
transform = phi_f*np.ones(shape=(1,Nexc)),\
synapse=tau_AMPA)
ItoE = nengo.Connection(inhibrator.neurons, ratorOut.neurons,
transform = np.zeros(shape=(Nexc,Ninh)),\
synapse=tau) # need neurons->neurons for InhSVG
ItoE.learning_rule_type = nengo.InhVSG(
learning_rate=2e-8,pre_tau=tau,
theta=3.0, clipType='clip>0',
decay_rate_x_dt=inhVSG_weightsDecayRate*dt)
# clip away inhibitory weights > 0
# no synaptic weights decay
else: excClipType = None
if plastDecoders:
# don't use the same seeds across the connections,
# else they seem to be all evaluated at the same values of low-dim variables
# causing seed-dependent convergence issues possibly due to similar frozen noise across connections
if initLearned:
# default transform is unity
InEtoE = nengo.Connection(ratorIn, ratorOut, synapse=tau)
else:
InEtoE = nengo.Connection(ratorIn, ratorOut, transform=Wdyn2, synapse=tau)
#np.random.seed(1)
#InEtoE = nengo.Connection(nodeIn, ratorOut, synapse=None,
# transform=np.random.uniform(-20,20,size=(N,N))+np.eye(N))
EtoE = nengo.Connection(ratorOut, ratorOut,
transform=Wdyn2, synapse=tau) # synapse is tau_syn for filtering
else:
# If initLearned==True, these weights will be reset below using InEtoEfake and EtoEfake
if copycatLayer and not copycatPreLearned: # if copycatLayer from Wdesired, we don't learn the FF transform,
# else we cannot compare to copycatweights, since a constant is arbitrary between ff and rec.
InEtoE = nengo.Connection(ratorIn.neurons, ratorOut.neurons, synapse=tau)
# the system didn't learn in this case
# possibly the problem is neurons to neurons here and ensemble to ensemble for InEtoEexpect
else:
InEtoE = nengo.Connection(ratorIn.neurons, ratorOut.neurons,
transform=Wdyn2/20., synapse=tau)
# Wdyn2 same as for EtoE, but mean(InEtoE) = mean(EtoE)/20
EtoE = nengo.Connection(ratorOut.neurons, ratorOut.neurons,
transform=Wdyn2, synapse=tau) # synapse is tau_syn for filtering
# initLearned
if initLearned and not inhibition: # initLearned=True will set bidirectional weights
# thus only useful if inhibition=False
InEtoEfake = nengo.Connection(ratorIn, ratorOut, synapse=tau)
EtoEfake = nengo.Connection(ratorOut, ratorOut,
function=Wdesired, synapse=tau) # synapse is tau_syn for filtering
# probes
nodeIn_probe = nengo.Probe(nodeIn, synapse=None)
ratorIn_probe = nengo.Probe(ratorIn, synapse=tau)
# don't probe what is encoded in ratorIn, rather what is sent to ratorOut
# 'output' reads out the output of the connection InEtoE in nengo 2.2.1.dev0
# but in older nengo ~2.0, the full variable encoded in ratorOut (the post-ensemble of InEtoE)
# NOTE: InEtoE is from neurons to neurons, so 'output' is Nexc-dim not N-dim!
#ratorIn_probe = nengo.Probe(InEtoE, 'output')
#ratorIn_probe = nengo.Probe(InEtoE, 'input', synapse=tau)
# don't probe ratorOut here as this calls build_decoders() separately for this;
# just call build_decoders() once for ratorOut2error, and probe 'output' of that connection below
#ratorOut_probe = nengo.Probe(ratorOut, synapse=tau)
# synapse is tau for filtering
# Default is no filtering
if testLearned and saveSpikes:
ratorOut_EspikesOut = nengo.Probe(ratorOut.neurons, 'output')
# this becomes too big for shelve (ndarray.dump())
# for my Lorenz _end simulation of 100s
# gives SystemError: error return without exception set
# use python3.3+ or break into smaller sizes
# even with python3.4, TypeError: gdbm mappings have byte or string elements only
############################
### Learn ratorOut EtoE connection
############################
with mainModel:
if errorLearning:
###
### copycat layer only for recurrent learning ###
###
# another layer that produces the expected signal for above layer to learn
# force the encoders, maxrates and intercepts to be same as ratorOut
# so that the weights are directly comparable between netExpect (copycat) and net2
# if Wdesired is a function, then this has to be LIF layer
if recurrentLearning and copycatLayer:
expectOut = nengo.Ensemble( Nexc, dimensions=N, radius=reprRadius, neuron_type=nengo.neurons.LIF(), seed=seedR4 )
# a node does not do the leaky integration / low-pass filtering that an ensemble does,
# so node won't work, unless I use the original W and not the one with tau and I, also input should not be *tau
# even with those above, it still gave some overflow error (dunno why)
#expectOut = nengo.Node(size_in=N, size_out=N, output = lambda timeval,x: x)
if copycatPreLearned:
InEtoEexpect = nengo.Connection(ratorIn.neurons, expectOut.neurons,
transform=Wdyn2, synapse=tau)
EtoEexpect = nengo.Connection(expectOut.neurons, expectOut.neurons,
transform=Wdyn2, synapse=tau) # synapse is tau_syn for filtering
else:
## the system didn't learn in this case
## possibly the problem is ensemble to ensemble here but neurons to neurons for InEtoE & EtoE?
InEtoEexpect = nengo.Connection(ratorIn, expectOut, synapse=tau)
# ACHTUNG! the ff transform if not unity must be set here...
EtoEexpect = nengo.Connection(expectOut, expectOut,
function=Wdesired, synapse=tau) # synapse is tau_syn for filtering
if trialClamp:
nengo.Connection(endTrialClamp,expectOut.neurons,synapse=1e-3)
# clamp expectOut like ratorIn and ratorOut above
# fast synapse for fast-reacting clamp
expectOut_probe = nengo.Probe(expectOut, synapse=tau)
if testLearned and saveSpikes:
expectOut_spikesOut = nengo.Probe(expectOut.neurons, 'output')
###
### error ensemble, could be with error averaging, gets post connection ###
###
if spikingNeurons:
error = nengo.Ensemble(Nerror, dimensions=N, radius=reprRadiusErr, label='error')
else:
error = nengo.Node( size_in=N, output = lambda timeval,err: err)
if trialClamp:
errorOff = nengo.Node( size_in=N, output = lambda timeval,err: \
err if (Tperiod<timeval<(Tmax-Tnolearning) and (timeval%Tperiod)<Tperiod-Tclamp) \
else np.zeros(N), label='errorOff' )
else:
errorOff = nengo.Node( size_in=N, output = lambda timeval,err: \
err if (Tperiod<timeval<(Tmax-Tnolearning)) else np.zeros(N), label='errorOff' )
error2errorOff = nengo.Connection(error,errorOff,synapse=None)
if errorAverage: # average the error over Tperiod time scale
errorT = np.eye(N)*(1-tau/Tperiod*dt/Tperiod)# neuralT' = tau*dynT + I
# dynT=-1/Tperiod*dt/Tperiod
# *dt/Tperiod converts integral to mean
nengo.Connection(errorOff,errorOff,transform=errorT,synapse=tau)
# Error = post - pre * desired_transform
if continueLearning: # load pre-learned rec and ff weights, but readout weights start from zero
ratorOut2error = nengo.Connection(ratorOut,error,transform=np.zeros((N,N)),synapse=tau)
else: # for testLearned load pre-learned rec and ff weights, and correct readout weights
ratorOut2error = nengo.Connection(ratorOut,error,synapse=tau)
# post input to error ensemble (pre below)
# tau-filtered output (here) is matched to the unfiltered reference (pre below)
# important to probe only ratorOut2error as output, and not directly ratorOut, to accommodate randomDecodersType != ''
# 'output' reads out the output of the connection in nengo 2.2 on
ratorOut_probe = nengo.Probe(ratorOut2error, 'output')
if trialClamp:
###
### clamp error neurons to zero firing during end probe, for ff and rec learning ###
###
# no need to clamp error during ramp, as expected signal is accurate during ramp too
# clamp error neurons to zero for the last 2 Tperiods to check generation
# by injecting strong negative input to error neurons
if spikingNeurons:
clampValsZeros = np.zeros(Nerror)
clampValsNegs = -100.*np.ones(Nerror)
rampClamp = lambda t: clampValsZeros if t<(Tmax-Tnolearning) else clampValsNegs
errorClamp = nengo.Node(rampClamp)
nengo.Connection(errorClamp,error.neurons,synapse=1e-3)
# fast synapse for fast-reacting clamp
###
### Add the relevant pre signal to the error ensemble ###
###
if recurrentLearning: # L2 rec learning
if copycatLayer:
# Error = post - desired_output
rateEvolve2error = nengo.Connection(expectOut,error,synapse=tau,transform=-np.eye(N))
# - desired output here (post above)
# tau-filtered expectOut must be compared to tau-filtered ratorOut (post above)
else:
rateEvolve = nengo.Node(rateEvolveFn)
# Error = post - desired_output
rateEvolve2error = nengo.Connection(rateEvolve,error,synapse=tau,transform=-np.eye(N))
#rateEvolve2error = nengo.Connection(rateEvolve,error,synapse=None,transform=-np.eye(N))
# - desired output here (post above)
# unfiltered non-spiking reference is compared to tau-filtered spiking ratorOut (post above)
plasticConnEE = ratorOut2error # NOTE: learn output connection instead of recurrent!
rateEvolve_probe = nengo.Probe(rateEvolve2error, 'output')
# save the filtered/unfiltered reference as this is the 'actual' reference
###
### Add the exc learning rules to the connection, and the error ensemble to the learning rule ###
###
EtoERulesDict = { 'PES' : nengo.PES(learning_rate=PES_learning_rate_rec,
pre_tau=tau) }#,
#clipType=excClipType,
#decay_rate_x_dt=excPES_weightsDecayRate*dt,
#integral_tau=excPES_integralTau) }
plasticConnEE.learning_rule_type = EtoERulesDict
#plasticConnEE.learning_rule['PES'].learning_rate=0
# learning_rate has no effect
# set to zero, yet works fine!
# It works only if you set it
# in the constructor PES() above
if learnIfNoInput: # obsolete, no support for trialClamp
errorWt = nengo.Node( size_in=N, output = lambda timeval,errWt: \
zerosN if (timeval%Tperiod) < rampT else errWt*(np.abs(errWt)>weightErrorCutoff) )
# only learn when there is no input,
# using the local (input+err) current