CARLsim  6.1.0
CARLsim: a GPU-accelerated SNN simulator
simple_weight_tuner.cpp
Go to the documentation of this file.
1 /* * Copyright (c) 2016 Regents of the University of California. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * 3. The names of its contributors may not be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *********************************************************************************************** *
31 * CARLsim
32 * created by: (MDR) Micah Richert, (JN) Jayram M. Nageswaran
33 * maintained by:
34 * (MA) Mike Avery <averym@uci.edu>
35 * (MB) Michael Beyeler <mbeyeler@uci.edu>,
36 * (KDC) Kristofor Carlson <kdcarlso@uci.edu>
37 * (TSC) Ting-Shuo Chou <tingshuc@uci.edu>
38 * (HK) Hirak J Kashyap <kashyaph@uci.edu>
39 *
40 * CARLsim v1.0: JM, MDR
41 * CARLsim v2.0/v2.1/v2.2: JM, MDR, MA, MB, KDC
42 * CARLsim3: MB, KDC, TSC
43 * CARLsim4: TSC, HK
44 * CARLsim5: HK, JX, KC
45 * CARLsim6: LN, JX, KC, KW
46 *
47 * CARLsim available from http://socsci.uci.edu/~jkrichma/CARLsim/
48 * Ver 12/31/2016
49 */
50 #include "simple_weight_tuner.h"
51 
52 #include <carlsim.h> // CARLsim, SpikeMonitor
53 #include <math.h> // fabs
54 #include <stdio.h> // printf
55 #include <limits> // double::max
56 #include <assert.h> // assert
57 
58 // ****************************************************************************************************************** //
59 // SIMPLEWEIGHTTUNER UTILITY PRIVATE IMPLEMENTATION
60 // ****************************************************************************************************************** //
61 
70 public:
71  // +++++ PUBLIC METHODS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
72 
73  Impl(CARLsim *sim, double errorMargin, int maxIter, double stepSizeFraction) {
74  assert(sim!=NULL);
75  assert(errorMargin>0);
76  assert(maxIter>0);
77  assert(stepSizeFraction>0.0f && stepSizeFraction<=1.0f);
78 
79  sim_ = sim;
80  assert(sim_->getCARLsimState()!=RUN_STATE);
81 
82  errorMargin_ = errorMargin;
83  stepSizeFraction_ = stepSizeFraction;
84  maxIter_ = maxIter;
85 
86  connId_ = -1;
87  wtRange_ = NULL;
88  wtInit_ = -1.0;
89 
90  grpId_ = -1;
91  targetRate_ = -1.0;
92 
93  wtStepSize_ = -1.0;
94  cntIter_ = 0;
95 
96  wtShouldIncrease_ = true;
97  adjustRange_ = true;
98 
99  needToInitConnection_ = true;
100  needToInitTargetFiring_ = true;
101 
102  needToInitAlgo_ = true;
103  }
104 
105  ~Impl() {
106  if (wtRange_!=NULL)
107  delete wtRange_;
108  wtRange_=NULL;
109  }
110 
111 // user function to reset algo
112 void reset() {
113  needToInitAlgo_ = true;
114  initAlgo();
115 }
116 
117 bool done(bool printMessage) {
118  // algo not initalized: we're not done
119  if (needToInitConnection_ || needToInitTargetFiring_ || needToInitAlgo_)
120  return false;
121 
122  // success: margin reached
123  if (fabs(currentError_) < errorMargin_) {
124  if (printMessage) {
125  printf("SimpleWeightTuner successful: Error margin reached in %d iterations.\n",cntIter_);
126  }
127  return true;
128  }
129 
130  // failure: max iter reached
131  if (cntIter_ >= maxIter_) {
132  if (printMessage) {
133  printf("SimpleWeightTuner failed: Max number of iterations (%d) reached.\n",maxIter_);
134  }
135  return true;
136  }
137 
138  // else we're not done
139  return false;
140 }
141 
142 void setConnectionToTune(short int connId, double initWt, bool adjustRange) {
143  assert(connId>=0 && connId<sim_->getNumConnections());
144 
145  connId_ = connId;
146  wtInit_ = initWt;
147  adjustRange_ = adjustRange;
148 
149  needToInitConnection_ = false;
150  needToInitAlgo_ = true;
151 }
152 
153 void setTargetFiringRate(int grpId, double targetRate) {
154  grpId_ = grpId;
155  targetRate_ = targetRate;
156  currentError_ = targetRate;
157 
158  // check whether group has SpikeMonitor
159  SM_ = sim_->getSpikeMonitor(grpId);
160  if (SM_==NULL) {
161  // setSpikeMonitor has not been called yet
162  SM_ = sim_->setSpikeMonitor(grpId,"NULL");
163  }
164 
165  needToInitTargetFiring_ = false;
166  needToInitAlgo_ = true;
167 }
168 
169 void iterate(int runDurationMs, bool printStatus) {
170  assert(runDurationMs>0);
171 
172  // if we're done, don't iterate
173  if (done(printStatus)) {
174  return;
175  }
176 
177  // make sure we have initialized algo
178  assert(!needToInitConnection_);
179  assert(!needToInitTargetFiring_);
180  if (needToInitAlgo_)
181  initAlgo();
182 
183  // in case the user has already been messing with the SpikeMonitor, we need to make sure that
184  // PersistentMode is off
185  SM_->setPersistentData(false);
186 
187  // now iterate
188  SM_->startRecording();
189  sim_->runNetwork(runDurationMs/1000, runDurationMs%1000, false);
190  SM_->stopRecording();
191 
192  double thisRate = SM_->getPopMeanFiringRate();
193  if (printStatus) {
194  printf("#%d: rate=%.4fHz, target=%.4fHz, error=%.7f, errorMargin=%.7f\n", cntIter_, thisRate, targetRate_,
195  thisRate-targetRate_, errorMargin_);
196  }
197 
198  currentError_ = thisRate - targetRate_;
199  cntIter_++;
200 
201  // check if we're done now
202  if (done(printStatus)) {
203  return;
204  }
205 
206  // else update parameters
207  if (wtStepSize_>0 && thisRate>targetRate_ || wtStepSize_<0 && thisRate<targetRate_) {
208  // we stepped too far to the right or too far to the left
209  // turn around and cut step size in half
210  // note that this should work for inhibitory connections, too: they have negative weights, so adding
211  // to the weight will actually decrease it (make it less negative)
212  wtStepSize_ = -wtStepSize_/2.0;
213  }
214 
215  // find new weight
216  sim_->biasWeights(connId_, wtStepSize_, adjustRange_);
217 }
218 
219 private:
220  // +++++ PRIVATE METHODS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
221 
222 // need to call this whenever connection or target firing changes
223 // or when user calls reset
224 void initAlgo() {
225  if (!needToInitAlgo_)
226  return;
227 
228  // make sure we have all the data structures we need
229  assert(!needToInitConnection_);
230  assert(!needToInitTargetFiring_);
231 
232  // update weight ranges
233  RangeWeight wt = sim_->getWeightRange(connId_);
234  wtRange_ = new RangeWeight(wt.min, wt.init, wt.max);
235 
236  // reset algo
237  wtShouldIncrease_ = true;
238  wtStepSize_ = stepSizeFraction_ * (wtRange_->max - wtRange_->min);
239 #if defined(WIN32) || defined(WIN64)
240  currentError_ = DBL_MAX;
241 #else
242  currentError_ = std::numeric_limits<double>::max();
243 #endif
244 
245  // make sure we're in the right CARLsim state
246  if (sim_->getCARLsimState()!=RUN_STATE)
247  sim_->runNetwork(0,0,false);
248 
249  // initialize weights
250  if (wtInit_>=0) {
251  // start at some specified initWt
252  if (wt.init != wtInit_) {
253  // specified starting point is not what is specified in connect
254 
255  sim_->biasWeights(connId_, wtInit_ - wt.init, adjustRange_);
256  }
257  }
258 
259  needToInitAlgo_ = false;
260 }
261 
262 
263  // +++++ PRIVATE STATIC PROPERTIES ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
264 
265  // +++++ PRIVATE PROPERTIES +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
266 
267  // flags that manage state
268  bool needToInitConnection_;
269  bool needToInitTargetFiring_;
270  bool needToInitAlgo_;
271 
272  // CARLsim data structures
273  CARLsim *sim_;
274  SpikeMonitor *SM_;
275  int grpId_;
276  short int connId_;
277  RangeWeight* wtRange_;
278 
279  // termination condition params
280  int maxIter_;
281  double errorMargin_;
282  double targetRate_;
283 
284  // params that are updated every iteration step
285  int cntIter_;
286  double wtStepSize_;
287  bool wtShouldIncrease_;
288  double currentError_;
289 
290  // options
291  bool adjustRange_;
292  double wtInit_;
293  double stepSizeFraction_;
294 };
295 
296 
297 // ****************************************************************************************************************** //
298 // SIMPLEWEIGHTTUNER API IMPLEMENTATION
299 // ****************************************************************************************************************** //
300 
301 // create and destroy a pImpl instance
302 SimpleWeightTuner::SimpleWeightTuner(CARLsim* sim, double errorMargin, int maxIter, double stepSizeFraction) :
303  _impl( new Impl(sim, errorMargin, maxIter, stepSizeFraction) ) {}
305 
306 void SimpleWeightTuner::setConnectionToTune(short int connId, double initWt, bool adjustRange) {
307  _impl->setConnectionToTune(connId, initWt, adjustRange);
308 }
309 void SimpleWeightTuner::setTargetFiringRate(int grpId, double targetRate) {
310  _impl->setTargetFiringRate(grpId, targetRate);
311 }
312 void SimpleWeightTuner::iterate(int runDurationMs, bool printStatus) { _impl->iterate(runDurationMs, printStatus); }
313 bool SimpleWeightTuner::done(bool printMessage) { return _impl->done(printMessage); }
314 void SimpleWeightTuner::reset() { _impl->reset(); }
CARLsimState getCARLsimState()
Writes population weights from gIDpre to gIDpost to file fname in binary.
Definition: carlsim.cpp:2397
void startRecording()
Starts a new recording period.
void setTargetFiringRate(int grpId, double targetRate)
CARLsim User Interface This class provides a user interface to the public sections of CARLsimCore sou...
Definition: carlsim.h:142
float getPopMeanFiringRate()
Returns the mean firing rate of the entire neuronal population.
run state, where the model is stepped
SpikeMonitor * getSpikeMonitor(int grpId)
Returns the number of spikes per neuron for a certain group.
Definition: carlsim.cpp:2507
void setConnectionToTune(short int connId, double initWt=-1.0, bool adjustRange=true)
Sets up the connection to tune.
~SimpleWeightTuner()
Destructor.
void setConnectionToTune(short int connId, double initWt, bool adjustRange)
Class SpikeMonitor.
SimpleWeightTuner(CARLsim *sim, double errorMargin=1e-3, int maxIter=100, double stepSizeFraction=0.5)
Creates a new instance of class SimpleWeightTuner.
void stopRecording()
Ends a recording period.
void reset()
Resets the algorithm to initial conditions.
bool done(bool printMessage=false)
Determines whether a termination criterion has been met.
void iterate(int runDurationMs, bool printStatus)
void setTargetFiringRate(int grpId, double targetRate)
Sets up the target firing rate of a specific group.
void iterate(int runDurationMs=1000, bool printStatus=true)
Performs an iteration step of the tuning algorithm.
void biasWeights(short int connId, float bias, bool updateWeightRange=false)
Adds a constant bias to the weight of every synapse in the connection.
Definition: carlsim.cpp:2335
bool done(bool printMessage)
int runNetwork(int nSec, int nMsec=0, bool printRunSummary=true)
run the simulation for time=(nSec*seconds + nMsec*milliseconds)
Definition: carlsim.cpp:2288
a range struct for synaptic weight magnitudes
Private implementation of the Stopwatch Utility.
SpikeMonitor * setSpikeMonitor(int grpId, const std::string &fileName)
Sets a Spike Monitor for a groups, prints spikes to binary file.
Definition: carlsim.cpp:2371
void setPersistentData(bool persistentData)
Sets PersistentMode either on (true) or off (false)
Impl(CARLsim *sim, double errorMargin, int maxIter, double stepSizeFraction)
RangeWeight getWeightRange(short int connId)
returns the RangeWeight struct for a specific connection ID
Definition: carlsim.cpp:2510