PetaVision  Alpha
Weights.cpp
1 /*
2  * Weights.cpp
3  *
4  * Created on: Jul 21, 2017
5  * Author: Pete Schultz
6  */
7 
8 #include "Weights.hpp"
9 #include "checkpointing/CheckpointEntryWeightPvp.hpp"
10 #include "utils/PVAssert.hpp"
11 #include "utils/conversions.h"
12 #include <cstring>
13 #include <stdexcept>
14 
15 namespace PV {
16 
17 Weights::Weights(std::string const &name) { setName(name); }
18 
20  std::string const &name,
21  int patchSizeX,
22  int patchSizeY,
23  int patchSizeF,
24  PVLayerLoc const *preLoc,
25  PVLayerLoc const *postLoc,
26  int numArbors,
27  bool sharedWeights,
28  double timestamp) {
29  setName(name);
30  initialize(
31  patchSizeX, patchSizeY, patchSizeF, preLoc, postLoc, numArbors, sharedWeights, timestamp);
32 }
33 
35  std::shared_ptr<PatchGeometry> geometry,
36  int numArbors,
37  bool sharedWeights,
38  double timestamp) {
39  FatalIf(
40  mGeometry != nullptr,
41  "Weights object \"%s\" has already been initialized.\n",
42  getName().c_str());
43  mGeometry = geometry;
44  mNumArbors = numArbors;
45  mSharedFlag = sharedWeights;
46  mTimestamp = timestamp;
47 
48  initNumDataPatches();
49 
50 #ifdef PV_USE_CUDA
51  mTimestampGPU = timestamp;
52 #endif // PV_USE_CUDA
53 }
54 
55 void Weights::initialize(Weights const *baseWeights) {
56  auto geometry = baseWeights->getGeometry();
57  initialize(
58  geometry,
59  baseWeights->getNumArbors(),
60  baseWeights->getSharedFlag(),
61  baseWeights->getTimestamp());
62 }
63 
65  int patchSizeX,
66  int patchSizeY,
67  int patchSizeF,
68  PVLayerLoc const *preLoc,
69  PVLayerLoc const *postLoc,
70  int numArbors,
71  bool sharedWeights,
72  double timestamp) {
73  auto geometry = std::make_shared<PatchGeometry>(
74  mName.c_str(), patchSizeX, patchSizeY, patchSizeF, preLoc, postLoc);
75  initialize(geometry, numArbors, sharedWeights, timestamp);
76 }
77 
78 void Weights::setMargins(PVHalo const &preHalo, PVHalo const &postHalo) {
79  mGeometry->setMargins(preHalo, postHalo);
80  initNumDataPatches();
81 }
82 
84  if (!mData.empty()) {
85  return;
86  }
87  FatalIf(mGeometry == nullptr, "%s has not been initialized.\n", mName.c_str());
88  mGeometry->allocateDataStructures();
89 
90  int numDataPatches = mNumDataPatchesX * mNumDataPatchesY * mNumDataPatchesF;
91  if (numDataPatches != 0) {
92  int numItemsPerPatch = getPatchSizeOverall();
93  mData.resize(mNumArbors);
94  for (int arbor = 0; arbor < mNumArbors; arbor++) {
95  mData[arbor].resize(numDataPatches * numItemsPerPatch);
96  }
97  }
98  if (mSharedFlag and getNumDataPatches() > 0) {
99  int const numPatches = mGeometry->getNumPatches();
100  dataIndexLookupTable.resize(numPatches);
101  for (int p = 0; p < numPatches; p++) {
102  dataIndexLookupTable[p] = calcDataIndexFromPatchIndex(p);
103  }
104  }
105 #ifdef PV_USE_CUDA
106  if (mUsingGPUFlag) {
107  allocateCudaBuffers();
108  }
109 #endif // PV_USE_CUDA
110 }
111 
112 #ifdef PV_USE_CUDA
113 void Weights::allocateCudaBuffers() {
114  FatalIf(
115  mCudaDevice == nullptr,
116  "Weights::allocateCudaBuffers() called for weights \"%s\" without having set "
117  "CudaDevice.\n",
118  getName().c_str());
119  pvAssert(mDeviceData == nullptr); // Should only be called once, by allocateDataStructures();
120 #ifdef PV_USE_CUDNN
121  pvAssert(mCUDNNData == nullptr); // Should only be called once, by allocateDataStructures();
122 #endif // PV_USE_CUDNN
123  std::string description(mName);
124  int numPatches = getGeometry()->getNumPatchesX() * getGeometry()->getNumPatchesY()
125  * getGeometry()->getNumPatchesF();
126  std::size_t size;
127 
128  // Apr 10, 2018: mDevicePatches and mDeviceGSynPatchStart have been moved to
129  // PresynapticPerspectiveGPUDelivery, since they are needed for the presynaptic perspective,
130  // but not the postsynaptic perspective.
131 
132  if (getNumDataPatches() > 0) {
133  std::vector<int> hostPatchToDataLookupVector(numPatches);
134  if (mSharedFlag) {
135  for (int patchIndex = 0; patchIndex < numPatches; patchIndex++) {
136  hostPatchToDataLookupVector[patchIndex] = dataIndexLookupTable[patchIndex];
137  }
138  }
139  else {
140  for (int patchIndex = 0; patchIndex < numPatches; patchIndex++) {
141  hostPatchToDataLookupVector[patchIndex] = patchIndex;
142  }
143  }
144  size = hostPatchToDataLookupVector.size() * sizeof(hostPatchToDataLookupVector[0]);
145  mDevicePatchToDataLookup = mCudaDevice->createBuffer(size, &description);
146  // Copy PatchToDataLookup array onto CUDA device because it never changes.
147  mDevicePatchToDataLookup->copyToDevice(hostPatchToDataLookupVector.data());
148 
149  size = (std::size_t)getNumArbors() * (std::size_t)getNumDataPatches()
150  * (std::size_t)getPatchSizeOverall() * sizeof(float);
151  mDeviceData = mCudaDevice->createBuffer(size, &description);
152  pvAssert(mDeviceData);
153 #ifdef PV_USE_CUDNN
154  mCUDNNData = mCudaDevice->createBuffer(size, &description);
155 #endif
156  // no point in copying weights to device yet; they aren't set until the InitializeState stage.
157  }
158 }
159 #endif // PV_USE_CUDA
160 
161 void Weights::checkpointWeightPvp(
162  Checkpointer *checkpointer,
163  char const *objectName,
164  char const *bufferName,
165  bool compressFlag) {
166  auto checkpointEntry = std::make_shared<CheckpointEntryWeightPvp>(
167  std::string(objectName), bufferName, checkpointer->getMPIBlock(), this, compressFlag);
168  bool registerSucceeded =
169  checkpointer->registerCheckpointEntry(checkpointEntry, !mWeightsArePlastic);
170  FatalIf(
171  !registerSucceeded,
172  "%s failed to register %s for checkpointing.\n",
173  getName().c_str(),
174  bufferName);
175 }
176 
177 void Weights::initNumDataPatches() {
178  if (mSharedFlag) {
179  setNumDataPatches(
180  mGeometry->getNumKernelsX(), mGeometry->getNumKernelsY(), mGeometry->getNumKernelsF());
181  }
182  else {
183  setNumDataPatches(
184  mGeometry->getNumPatchesX(), mGeometry->getNumPatchesY(), mGeometry->getNumPatchesF());
185  }
186 }
187 
188 void Weights::setNumDataPatches(int numDataPatchesX, int numDataPatchesY, int numDataPatchesF) {
189  mNumDataPatchesX = numDataPatchesX;
190  mNumDataPatchesY = numDataPatchesY;
191  mNumDataPatchesF = numDataPatchesF;
192 }
193 
194 Patch const &Weights::getPatch(int patchIndex) const { return mGeometry->getPatch(patchIndex); }
195 
196 float *Weights::getData(int arbor) { return &mData[arbor][0]; }
197 
198 float const *Weights::getDataReadOnly(int arbor) const { return &mData[arbor][0]; }
199 
200 float *Weights::getDataFromDataIndex(int arbor, int dataIndex) {
201  int numItemsPerPatch = getPatchSizeX() * getPatchSizeY() * getPatchSizeF();
202  return &mData[arbor][dataIndex * numItemsPerPatch];
203 }
204 
205 float *Weights::getDataFromPatchIndex(int arbor, int patchIndex) {
206  int dataIndex = mSharedFlag ? dataIndexLookupTable[patchIndex] : patchIndex;
207  return getDataFromDataIndex(arbor, dataIndex);
208 }
209 
210 int Weights::calcDataIndexFromPatchIndex(int patchIndex) const {
211  if (getSharedFlag()) {
212  int numPatchesX = mGeometry->getNumPatchesX();
213  int numPatchesY = mGeometry->getNumPatchesY();
214  int numPatchesF = mGeometry->getNumPatchesF();
215  int xIndex = kxPos(patchIndex, numPatchesX, numPatchesY, numPatchesF);
216  xIndex = (xIndex - mGeometry->getPreLoc().halo.lt) % mNumDataPatchesX;
217  if (xIndex < 0) {
218  xIndex += mNumDataPatchesX;
219  }
220 
221  int yIndex = kyPos(patchIndex, numPatchesX, numPatchesY, numPatchesF);
222  yIndex = (yIndex - mGeometry->getPreLoc().halo.up) % mNumDataPatchesY;
223  if (yIndex < 0) {
224  yIndex += mNumDataPatchesY;
225  }
226 
227  int fIndex = featureIndex(patchIndex, numPatchesX, numPatchesY, numPatchesF);
228 
229  int dataIndex =
230  kIndex(xIndex, yIndex, fIndex, mNumDataPatchesX, mNumDataPatchesY, mNumDataPatchesF);
231  return dataIndex;
232  }
233  else {
234  return patchIndex;
235  }
236 }
237 
239  float minWeight = FLT_MAX;
240  for (int a = 0; a < mNumArbors; a++) {
241  float arborMin = calcMinWeight(a);
242  if (arborMin < minWeight) {
243  minWeight = arborMin;
244  }
245  }
246  return minWeight;
247 }
248 
249 float Weights::calcMinWeight(int arbor) {
250  float arborMin = FLT_MAX;
251  if (getSharedFlag()) {
252  for (auto &w : mData[arbor]) {
253  if (w < arborMin) {
254  arborMin = w;
255  }
256  }
257  }
258  else {
259  pvAssert(getNumDataPatches() == getGeometry()->getNumPatches());
260  for (int p = 0; p < getNumDataPatches(); p++) {
261  Patch const &patch = getPatch(p);
262  int const nk = patch.nx * getPatchSizeF();
263  int const sy = getPatchSizeX() * getPatchSizeF();
264  for (int y = 0; y < patch.ny; y++) {
265  for (int k = 0; k < nk; k++) {
266  float w = getDataFromDataIndex(arbor, p)[patch.offset + y * sy + k];
267  if (w < arborMin) {
268  arborMin = w;
269  }
270  }
271  }
272  }
273  }
274  return arborMin;
275 }
276 
278  float maxWeight = -FLT_MAX;
279  for (int a = 0; a < mNumArbors; a++) {
280  float arborMax = calcMaxWeight(a);
281  if (arborMax > maxWeight) {
282  maxWeight = arborMax;
283  }
284  }
285  return maxWeight;
286 }
287 
288 float Weights::calcMaxWeight(int arbor) {
289  float arborMax = -FLT_MAX;
290  if (getSharedFlag()) {
291  for (auto &w : mData[arbor]) {
292  if (w > arborMax) {
293  arborMax = w;
294  }
295  }
296  }
297  else {
298  pvAssert(getNumDataPatches() == getGeometry()->getNumPatches());
299  for (int p = 0; p < getNumDataPatches(); p++) {
300  Patch const &patch = getPatch(p);
301  int const nk = patch.nx * getPatchSizeF();
302  int const sy = getPatchSizeX() * getPatchSizeF();
303  for (int y = 0; y < patch.ny; y++) {
304  for (int k = 0; k < nk; k++) {
305  float w = getDataFromDataIndex(arbor, p)[patch.offset + y * sy + k];
306  if (w > arborMax) {
307  arborMax = w;
308  }
309  }
310  }
311  }
312  }
313  return arborMax;
314 }
315 
316 #ifdef PV_USE_CUDA
318  if (!(mUsingGPUFlag and mTimestampGPU < mTimestamp)) {
319  return;
320  }
321  pvAssert(mDeviceData);
322 
323  int const numDataPatches = mNumDataPatchesX * mNumDataPatchesY * mNumDataPatchesF;
324  std::size_t const arborSize = (std::size_t)numDataPatches * (std::size_t)getPatchSizeOverall();
325  std::size_t const numArbors = (std::size_t)mNumArbors;
326  for (std::size_t a = 0; a < numArbors; a++) {
327  mDeviceData->copyToDevice(mData[a].data(), arborSize * sizeof(mData[a][0]), a * arborSize);
328  }
329 #ifdef PV_USE_CUDNN
330  mCUDNNData->permuteWeightsPVToCudnn(
331  mDeviceData->getPointer(),
332  mNumArbors,
333  numDataPatches,
334  getPatchSizeX(),
335  getPatchSizeY(),
336  getPatchSizeF());
337 #endif // PV_USE_CUDNN
338  mTimestampGPU = mTimestamp;
339 }
340 #endif // PV_USE_CUDA
341 
342 } // end namespace PV
bool getSharedFlag() const
Definition: Weights.hpp:142
void setMargins(PVHalo const &preHalo, PVHalo const &postHalo)
Definition: Weights.cpp:78
float * getData(int arbor)
Definition: Weights.cpp:196
float calcMaxWeight()
Definition: Weights.cpp:277
int getPatchSizeX() const
Definition: Weights.hpp:219
std::string const & getName() const
Definition: Weights.hpp:145
void initialize(std::shared_ptr< PatchGeometry > geometry, int numArbors, bool sharedWeights, double timestamp)
Definition: Weights.cpp:34
int getPatchSizeOverall() const
Definition: Weights.hpp:231
int getNumDataPatches() const
Definition: Weights.hpp:174
Patch const & getPatch(int patchIndex) const
Definition: Weights.cpp:194
float * getDataFromDataIndex(int arbor, int dataIndex)
Definition: Weights.cpp:200
int getNumArbors() const
Definition: Weights.hpp:151
int getPatchSizeY() const
Definition: Weights.hpp:222
std::shared_ptr< PatchGeometry > getGeometry() const
Definition: Weights.hpp:148
float * getDataFromPatchIndex(int arbor, int patchIndex)
Definition: Weights.cpp:205
double getTimestamp() const
Definition: Weights.hpp:216
float calcMinWeight()
Definition: Weights.cpp:238
void allocateDataStructures()
Definition: Weights.cpp:83
void copyToGPU()
Definition: Weights.cpp:317
int getPatchSizeF() const
Definition: Weights.hpp:225
float const * getDataReadOnly(int arbor) const
Definition: Weights.cpp:198