PetaVision  Alpha
ANNLayer.cpp
1 /*
2  * ANNLayer.cpp
3  *
4  * Created on: Dec 21, 2010
5  * Author: pschultz
6  */
7 
8 #include "ANNLayer.hpp"
9 #include "layers/updateStateFunctions.h"
10 #include <limits>
11 
12 void ANNLayer_vertices_update_state(
13  const int nbatch,
14  const int numNeurons,
15  const int nx,
16  const int ny,
17  const int nf,
18  const int lt,
19  const int rt,
20  const int dn,
21  const int up,
22 
23  float *V,
24  int numVertices,
25  float *verticesV,
26  float *verticesA,
27  float *slopes,
28  int num_channels,
29  float *GSynHead,
30  float *activity);
31 
32 void ANNLayer_threshminmax_update_state(
33  const int nbatch,
34  const int numNeurons,
35  const int nx,
36  const int ny,
37  const int nf,
38  const int lt,
39  const int rt,
40  const int dn,
41  const int up,
42 
43  float *V,
44  float VThresh,
45  float AMin,
46  float AMax,
47  float AShift,
48  float VWidth,
49  int num_channels,
50  float *GSynHead,
51  float *activity);
52 
53 namespace PV {
54 
55 ANNLayer::ANNLayer() { initialize_base(); }
56 
57 ANNLayer::ANNLayer(const char *name, HyPerCol *hc) {
58  initialize_base();
59  initialize(name, hc);
60 }
61 
62 ANNLayer::~ANNLayer() {
63  free(verticesV);
64  free(verticesA);
65  free(slopes);
66 }
67 
68 int ANNLayer::initialize_base() {
69  // Data members were initialized in the class member-declarations
70  return PV_SUCCESS;
71 }
72 
73 int ANNLayer::initialize(const char *name, HyPerCol *hc) {
74  int status = HyPerLayer::initialize(name, hc);
75  if (!this->layerListsVerticesInParams()) {
76  if (status == PV_SUCCESS) {
77  status = setVertices();
78  }
79  }
80  if (status == PV_SUCCESS) {
81  status = checkVertices();
82  }
83  if (status == PV_SUCCESS) {
84  setSlopes();
85  }
86  return status;
87 }
88 
89 int ANNLayer::ioParamsFillGroup(enum ParamsIOFlag ioFlag) {
90  int status = HyPerLayer::ioParamsFillGroup(ioFlag);
91 
92  if (parent->parameters()->arrayPresent(name, "verticesV")) {
93  verticesListInParams = true;
94  ioParam_verticesV(ioFlag);
95  ioParam_verticesA(ioFlag);
96  ioParam_slopeNegInf(ioFlag);
97  ioParam_slopePosInf(ioFlag);
98  }
99  else {
100  verticesListInParams = false;
101  ioParam_VThresh(ioFlag);
102  ioParam_AMin(ioFlag);
103  ioParam_AMax(ioFlag);
104  ioParam_AShift(ioFlag);
105  ioParam_VWidth(ioFlag);
106  }
107 
108  return status;
109 }
110 
111 void ANNLayer::ioParam_verticesV(enum ParamsIOFlag ioFlag) {
112  pvAssert(verticesListInParams);
113  int numVerticesTmp = numVertices;
114  this->parent->parameters()->ioParamArray(
115  ioFlag, this->getName(), "verticesV", &verticesV, &numVerticesTmp);
116  if (ioFlag == PARAMS_IO_READ) {
117  if (numVerticesTmp == 0) {
118  if (this->parent->columnId() == 0) {
119  ErrorLog().printf("%s: verticesV cannot be empty\n", getDescription_c());
120  }
121  MPI_Barrier(this->parent->getCommunicator()->communicator());
122  exit(EXIT_FAILURE);
123  }
124  if (numVertices != 0 && numVerticesTmp != numVertices) {
125  if (this->parent->columnId() == 0) {
126  ErrorLog().printf(
127  "%s: verticesV (%d elements) and verticesA (%d elements) must have the same "
128  "lengths.\n",
129  getDescription_c(),
130  numVerticesTmp,
131  numVertices);
132  }
133  MPI_Barrier(this->parent->getCommunicator()->communicator());
134  exit(EXIT_FAILURE);
135  }
136  assert(numVertices == 0 || numVertices == numVerticesTmp);
137  numVertices = numVerticesTmp;
138  }
139 }
140 
141 void ANNLayer::ioParam_verticesA(enum ParamsIOFlag ioFlag) {
142  pvAssert(verticesListInParams);
143  int numVerticesA = numVertices;
144  this->parent->parameters()->ioParamArray(
145  ioFlag, this->getName(), "verticesA", &verticesA, &numVerticesA);
146  if (ioFlag == PARAMS_IO_READ) {
147  if (numVerticesA == 0) {
148  if (this->parent->columnId() == 0) {
149  ErrorLog().printf("%s: verticesA cannot be empty\n", getDescription_c());
150  }
151  MPI_Barrier(this->parent->getCommunicator()->communicator());
152  exit(EXIT_FAILURE);
153  }
154  if (numVertices != 0 && numVerticesA != numVertices) {
155  if (this->parent->columnId() == 0) {
156  ErrorLog().printf(
157  "%s: verticesV (%d elements) and verticesA (%d elements) must have the same "
158  "lengths.\n",
159  getDescription_c(),
160  numVertices,
161  numVerticesA);
162  }
163  MPI_Barrier(this->parent->getCommunicator()->communicator());
164  exit(EXIT_FAILURE);
165  }
166  assert(numVertices == 0 || numVertices == numVerticesA);
167  numVertices = numVerticesA;
168  }
169 }
170 
171 void ANNLayer::ioParam_slopeNegInf(enum ParamsIOFlag ioFlag) {
172  pvAssert(verticesListInParams);
173  parent->parameters()->ioParamValue(
174  ioFlag, name, "slopeNegInf", &slopeNegInf, slopeNegInf /*default*/, true /*warnIfAbsent*/);
175 }
176 
177 void ANNLayer::ioParam_slopePosInf(enum ParamsIOFlag ioFlag) {
178  pvAssert(verticesListInParams);
179  parent->parameters()->ioParamValue(
180  ioFlag, name, "slopePosInf", &slopePosInf, slopePosInf /*default*/, true /*warnIfAbsent*/);
181 }
182 
183 void ANNLayer::ioParam_VThresh(enum ParamsIOFlag ioFlag) {
184  pvAssert(!verticesListInParams);
185  parent->parameters()->ioParamValue(ioFlag, name, "VThresh", &VThresh, VThresh);
186 }
187 
188 void ANNLayer::ioParam_AMin(enum ParamsIOFlag ioFlag) {
189  pvAssert(!verticesListInParams);
190  pvAssert(!parent->parameters()->presentAndNotBeenRead(name, "VThresh"));
191  parent->parameters()->ioParamValue(
192  ioFlag,
193  name,
194  "AMin",
195  &AMin,
196  VThresh); // defaults to the value of VThresh, which was read earlier.
197 }
198 
199 void ANNLayer::ioParam_AMax(enum ParamsIOFlag ioFlag) {
200  pvAssert(!verticesListInParams);
201  parent->parameters()->ioParamValue(ioFlag, name, "AMax", &AMax, AMax);
202 }
203 
204 void ANNLayer::ioParam_AShift(enum ParamsIOFlag ioFlag) {
205  pvAssert(!verticesListInParams);
206  parent->parameters()->ioParamValue(ioFlag, name, "AShift", &AShift, AShift);
207 }
208 
209 void ANNLayer::ioParam_VWidth(enum ParamsIOFlag ioFlag) {
210  pvAssert(!verticesListInParams);
211  parent->parameters()->ioParamValue(ioFlag, name, "VWidth", &VWidth, VWidth);
212 }
213 
215  pvAssert(!layerListsVerticesInParams());
216  if (VWidth < 0) {
217  VThresh += VWidth;
218  VWidth = -VWidth;
219  if (parent->columnId() == 0) {
220  WarnLog().printf(
221  "%s: interpreting negative VWidth as setting VThresh=%f and VWidth=%f\n",
222  getDescription_c(),
223  (double)VThresh,
224  (double)VWidth);
225  }
226  }
227 
228  float limfromright = VThresh + VWidth - AShift;
229  if (AMax < limfromright)
230  limfromright = AMax;
231 
232  if (AMin > limfromright) {
233  if (parent->columnId() == 0) {
234  if (VWidth == 0) {
235  WarnLog().printf(
236  "%s: nonmonotonic transfer function, jumping from %f to %f at Vthresh=%f\n",
237  getDescription_c(),
238  (double)AMin,
239  (double)limfromright,
240  (double)VThresh);
241  }
242  else {
243  WarnLog().printf(
244  "%s: nonmonotonic transfer function, changing from %f to %f as V goes from "
245  "VThresh=%f to VThresh+VWidth=%f\n",
246  getDescription_c(),
247  (double)AMin,
248  (double)limfromright,
249  (double)VThresh,
250  (double)(VThresh + VWidth));
251  }
252  }
253  }
254 
255  // Initialize slopes to NaN so that we can tell whether they've been initialized.
256  slopeNegInf = std::numeric_limits<double>::quiet_NaN();
257  slopePosInf = std::numeric_limits<double>::quiet_NaN();
258  std::vector<float> vectorV;
259  std::vector<float> vectorA;
260 
261  slopePosInf = 1.0f;
262  if (VThresh <= -(float)0.999 * FLT_MAX) {
263  numVertices = 1;
264  vectorV.push_back((float)0);
265  vectorA.push_back(-AShift);
266  slopeNegInf = 1.0f;
267  }
268  else {
269  assert(VWidth >= (float)0);
270  if (VWidth == (float)0
271  && (float)VThresh - AShift == AMin) { // Should there be a tolerance instead of strict ==?
272  numVertices = 1;
273  vectorV.push_back(VThresh);
274  vectorA.push_back(AMin);
275  }
276  else {
277  numVertices = 2;
278  vectorV.push_back(VThresh);
279  vectorV.push_back(VThresh + VWidth);
280  vectorA.push_back(AMin);
281  vectorA.push_back(VThresh + VWidth - AShift);
282  }
283  slopeNegInf = 0.0f;
284  }
285  if (AMax < (float)0.999 * FLT_MAX) {
286  assert(slopePosInf == 1.0f);
287  if (vectorA[numVertices - 1] < AMax) {
288  float interval = AMax - vectorA[numVertices - 1];
289  vectorV.push_back(vectorV[numVertices - 1] + (float)interval);
290  vectorA.push_back(AMax);
291  numVertices++;
292  }
293  else {
294  // find the last vertex where A < AMax.
295  bool found = false;
296  int v;
297  for (v = numVertices - 1; v >= 0; v--) {
298  if (vectorA[v] < AMax) {
299  found = true;
300  break;
301  }
302  }
303  if (found) {
304  assert(v + 1 < numVertices && vectorA[v] < AMax && vectorA[v + 1] >= AMax);
305  float interval = AMax - vectorA[v];
306  numVertices = v + 1;
307  vectorA.resize(numVertices);
308  vectorV.resize(numVertices);
309  vectorV.push_back(vectorV[v] + (float)interval);
310  vectorA.push_back(AMax);
311  // In principle, there could be a case where a vertex n has A[n]>AMax but A[n-1] and
312  // A[n+1] are both < AMax.
313  // But with the current ANNLayer parameters, that won't happen.
314  }
315  else {
316  // All vertices have A>=AMax.
317  // If slopeNegInf is positive, transfer function should increase from -infinity to AMax,
318  // and then stays constant.
319  // If slopeNegInf is negative or zero,
320  numVertices = 1;
321  vectorA.resize(numVertices);
322  vectorV.resize(numVertices);
323  if (slopeNegInf > 0) {
324  float intervalA = vectorA[0] - AMax;
325  float intervalV = (float)(intervalA / slopeNegInf);
326  vectorV[0] = vectorV[0] - intervalV;
327  vectorA[0] = AMax;
328  }
329  else {
330  // Everything everywhere is above AMax, so make the transfer function a constant
331  // A=AMax.
332  vectorA.resize(1);
333  vectorV.resize(1);
334  vectorV[0] = (float)0;
335  vectorA[0] = AMax;
336  numVertices = 1;
337  slopeNegInf = 0;
338  }
339  }
340  }
341  slopePosInf = 0.0f;
342  }
343  // Check for NaN
344  assert(slopeNegInf == slopeNegInf && slopePosInf == slopePosInf && numVertices > 0);
345  assert(vectorA.size() == numVertices && vectorV.size() == numVertices);
346  verticesV = (float *)malloc((size_t)numVertices * sizeof(*verticesV));
347  verticesA = (float *)malloc((size_t)numVertices * sizeof(*verticesA));
348  if (verticesV == NULL || verticesA == NULL) {
349  ErrorLog().printf(
350  "%s: unable to allocate memory for vertices:%s\n", getDescription_c(), strerror(errno));
351  exit(EXIT_FAILURE);
352  }
353  memcpy(verticesV, &vectorV[0], numVertices * sizeof(*verticesV));
354  memcpy(verticesA, &vectorA[0], numVertices * sizeof(*verticesA));
355 
356  return PV_SUCCESS;
357 }
358 
360  pvAssert(numVertices > 0);
361  pvAssert(verticesA != nullptr);
362  pvAssert(verticesV != nullptr);
363  slopes = (float *)pvMallocError(
364  (size_t)(numVertices + 1) * sizeof(*slopes),
365  "%s: unable to allocate memory for transfer function slopes: %s\n",
366  getDescription_c(),
367  strerror(errno));
368  slopes[0] = slopeNegInf;
369  for (int k = 1; k < numVertices; k++) {
370  float V1 = verticesV[k - 1];
371  float V2 = verticesV[k];
372  if (V1 != V2) {
373  slopes[k] = (verticesA[k] - verticesA[k - 1]) / (V2 - V1);
374  }
375  else {
376  slopes[k] = verticesA[k] > verticesA[k - 1]
377  ? std::numeric_limits<float>::infinity()
378  : verticesA[k] < verticesA[k - 1]
379  ? -std::numeric_limits<float>::infinity()
380  : std::numeric_limits<float>::quiet_NaN();
381  }
382  }
383  slopes[numVertices] = slopePosInf;
384 }
385 
387  int status = PV_SUCCESS;
388  for (int v = 1; v < numVertices; v++) {
389  if (verticesV[v] < verticesV[v - 1]) {
390  status = PV_FAILURE;
391  if (this->parent->columnId() == 0) {
392  ErrorLog().printf(
393  "%s: vertices %d and %d: V-coordinates decrease from %f to %f.\n",
394  getDescription_c(),
395  v,
396  v + 1,
397  (double)verticesV[v - 1],
398  (double)verticesV[v]);
399  }
400  }
401  if (verticesA[v] < verticesA[v - 1]) {
402  if (this->parent->columnId() == 0) {
403  WarnLog().printf(
404  "%s: vertices %d and %d: A-coordinates decrease from %f to %f.\n",
405  getDescription_c(),
406  v,
407  v + 1,
408  (double)verticesA[v - 1],
409  (double)verticesA[v]);
410  }
411  }
412  }
413  return status;
414 }
415 
416 int ANNLayer::resetGSynBuffers(double timef, double dt) {
417  return HyPerLayer::resetGSynBuffers(timef, dt);
418 }
419 
420 Response::Status ANNLayer::updateState(double time, double dt) {
421  const PVLayerLoc *loc = getLayerLoc();
422  float *A = clayer->activity->data;
423  float *V = getV();
424  int num_channels = getNumChannels();
425  float *gSynHead = GSyn == NULL ? NULL : GSyn[0];
426  int nx = loc->nx;
427  int ny = loc->ny;
428  int nf = loc->nf;
429  int num_neurons = nx * ny * nf;
430  int nbatch = loc->nbatch;
432  ANNLayer_vertices_update_state(
433  nbatch,
434  num_neurons,
435  nx,
436  ny,
437  nf,
438  loc->halo.lt,
439  loc->halo.rt,
440  loc->halo.dn,
441  loc->halo.up,
442  V,
443  numVertices,
444  verticesV,
445  verticesA,
446  slopes,
447  num_channels,
448  gSynHead,
449  A);
450  }
451  else {
452  ANNLayer_threshminmax_update_state(
453  nbatch,
454  num_neurons,
455  nx,
456  ny,
457  nf,
458  loc->halo.lt,
459  loc->halo.rt,
460  loc->halo.dn,
461  loc->halo.up,
462  V,
463  VThresh,
464  AMin,
465  AMax,
466  AShift,
467  VWidth,
468  num_channels,
469  gSynHead,
470  A);
471  }
472  return Response::SUCCESS;
473 }
474 
475 int ANNLayer::setActivity() {
476  const PVLayerLoc *loc = getLayerLoc();
477  int nx = loc->nx;
478  int ny = loc->ny;
479  int nf = loc->nf;
480  PVHalo const *halo = &loc->halo;
481  int num_neurons = nx * ny * nf;
482  int nbatch = loc->nbatch;
483  int status;
484  status = setActivity_PtwiseLinearTransferLayer(
485  nbatch,
486  num_neurons,
487  getCLayer()->activity->data,
488  getV(),
489  nx,
490  ny,
491  nf,
492  halo->lt,
493  halo->rt,
494  halo->dn,
495  halo->up,
496  numVertices,
497  verticesV,
498  verticesA,
499  slopes);
500  return status;
501 }
502 
503 } // end namespace PV
504 
506 //
507 // implementation of ANNLayer kernels
508 //
509 
510 void ANNLayer_vertices_update_state(
511  const int nbatch,
512  const int numNeurons,
513  const int nx,
514  const int ny,
515  const int nf,
516  const int lt,
517  const int rt,
518  const int dn,
519  const int up,
520 
521  float *V,
522  int numVertices,
523  float *verticesV,
524  float *verticesA,
525  float *slopes,
526  int num_channels,
527  float *GSynHead,
528  float *activity) {
529  updateV_ANNLayer_vertices(
530  nbatch,
531  numNeurons,
532  V,
533  num_channels,
534  GSynHead,
535  activity,
536  numVertices,
537  verticesV,
538  verticesA,
539  slopes,
540  nx,
541  ny,
542  nf,
543  lt,
544  rt,
545  dn,
546  up);
547 }
548 
549 void ANNLayer_threshminmax_update_state(
550  const int nbatch,
551  const int numNeurons,
552  const int nx,
553  const int ny,
554  const int nf,
555  const int lt,
556  const int rt,
557  const int dn,
558  const int up,
559 
560  float *V,
561  float VThresh,
562  float AMin,
563  float AMax,
564  float AShift,
565  float VWidth,
566  int num_channels,
567  float *GSynHead,
568  float *activity) {
569  updateV_ANNLayer_threshminmax(
570  nbatch,
571  numNeurons,
572  V,
573  num_channels,
574  GSynHead,
575  activity,
576  VThresh,
577  AMin,
578  AMax,
579  AShift,
580  VWidth,
581  nx,
582  ny,
583  nf,
584  lt,
585  rt,
586  dn,
587  up);
588 }
virtual void ioParam_verticesV(enum ParamsIOFlag ioFlag)
verticesV: An array of membrane potentials at points where the transfer function jumps or changes slo...
Definition: ANNLayer.cpp:111
virtual int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: HyPerLayer.cpp:571
bool layerListsVerticesInParams() const
Definition: ANNLayer.hpp:30
virtual void ioParam_AShift(enum ParamsIOFlag ioFlag)
AShift: Only read if verticesV is absent. When membrane potential V is above the threshold VThresh...
Definition: ANNLayer.cpp:204
virtual void ioParam_slopeNegInf(enum ParamsIOFlag ioFlag)
slopeNegInf: The slope of the transfer function when x is less than the first element of verticesV...
Definition: ANNLayer.cpp:171
virtual void ioParam_verticesA(enum ParamsIOFlag ioFlag)
verticesA: An array of activities of points where the transfer function jumps or changes slope...
Definition: ANNLayer.cpp:141
virtual void ioParam_AMin(enum ParamsIOFlag ioFlag)
AMin: Only read if verticesV is absent. When membrane potential V is below the threshold VThresh...
Definition: ANNLayer.cpp:188
void setSlopes()
Definition: ANNLayer.cpp:359
int initialize(const char *name, HyPerCol *hc)
Definition: HyPerLayer.cpp:129
virtual void ioParam_AMax(enum ParamsIOFlag ioFlag)
AMax: Only read if verticesV is absent. Activity that would otherwise be greater than AMax is truncat...
Definition: ANNLayer.cpp:199
virtual void ioParam_slopePosInf(enum ParamsIOFlag ioFlag)
slopePosInf: The slope of the transfer function when x is greater than the last element of verticesV...
Definition: ANNLayer.cpp:177
virtual void ioParam_VWidth(enum ParamsIOFlag ioFlag)
VWidth: Only read if verticesV is absent. When the membrane potential is between VThresh and VThresh+...
Definition: ANNLayer.cpp:209
virtual void ioParam_VThresh(enum ParamsIOFlag ioFlag)
VThresh: Only read if verticesV is absent. The threshold value for the membrane potential. Below this value, the output activity will be AMin. Above, it will obey the transfer function as specified by AMax, VWidth, and AShift. Default is -infinity.
Definition: ANNLayer.cpp:183
virtual int setVertices()
Definition: ANNLayer.cpp:214
virtual int checkVertices() const
Definition: ANNLayer.cpp:386
virtual int ioParamsFillGroup(enum ParamsIOFlag ioFlag) override
Definition: ANNLayer.cpp:89