<commit_before>/*
 * HyPerConn.cpp
 *
 *  Created on: Oct 21, 2008
 *      Author: <NAME>
 */

#include "HyPerConn.hpp"
#include "../layers/LIF.hpp"
#include "../layers/PVLayer.h"
#include "../include/default_params.h"
#include "../io/ConnectionProbe.hpp"
#include "../io/io.h"
#include "../io/fileio.hpp"
#include "../utils/conversions.h"
#include "../utils/pv_random.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "../weightinit/InitWeights.hpp"
#include "../weightinit/InitCocircWeights.hpp"
#include "../weightinit/InitSmartWeights.hpp"
#include "../weightinit/InitUniformRandomWeights.hpp"
#include "../weightinit/InitGaussianRandomWeights.hpp"

namespace PV {

// default values

//PVConnParams defaultConnParams =
//{
//   /*delay*/ 0
//   // Commenting out the same parameters that are commented out in setParams()
//   // , /*fixDelay*/ 0, /*varDelayMin*/ 0, /*varDelayMax*/ 0, /*numDelay*/ 1,
//   // /*isGraded*/ 0, /*vel*/ 45.248, /*rmin*/ 0.0, /*rmax*/ 4.0
//};

HyPerConn::HyPerConn()
{
   initialize_base();
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, NULL, NULL);
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, InitWeights *weightInit)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, NULL, weightInit);
}

// provide filename or set to NULL
HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, filename, NULL);
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename, InitWeights *weightInit)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, filename, weightInit);
}


HyPerConn::~HyPerConn()
{
   if (parent->columnId() == 0) {
      printf("%32s: total time in %6s %10s: ", name, "conn", "update ");
      update_timer->elapsed_time();
      fflush(stdout);
   }
   delete update_timer;

   free(name);

#ifdef PV_USE_OPENCL
   delete krRecvSyn;

   if (clWeights != NULL) {
      for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
         delete clWeights[arbor];
      }
      free(clWeights);
      clWeights = NULL;
   }
#endif // PV_USE_OPENCL

   // assert(params != NULL); // freeing a null pointer is not an error
   //free(params);

   deleteWeights();

   // free the task information

//   for (int l = 0; l < numAxonalArborLists; l++) {
//      if ( axonalArborList[l] ) {
//         free(axonalArbor(0, l)->data);
//         // axonalArbor(0,l) frees all data patches for arbor l because all
//         // axonalArbor patches for that l were created in a single calloc().
//         free(axonalArborList[l]);
//      }
//   }
   free(*gSynPatchStart); // All gSynPatchStart[k]'s were allocated together in a single malloc call.
   free(gSynPatchStart);
   free(*aPostOffset); // All aPostOffset[k]'s were allocated together in a single malloc call.
   free(aPostOffset);

   // delete weightInitializer; // weightInitializer should be deleted by whoever called the HyPerConn constructor

}

//!
/*!
 *
 *
 *
 */
int HyPerConn::initialize_base()
{
   this->name = strdup("Unknown");
   this->nxp = 1;
   this->nyp = 1;
   this->nfp = 1;
   this->sxp = 1;
   this->syp = 1;
   this->sfp = 1;
   this->parent = NULL;
   this->connId = 0;
   this->pre = NULL;
   this->post = NULL;
   this->numAxonalArborLists = 1;
   this->channel = CHANNEL_EXC;
   this->ioAppend = false;

   this->weightInitializer = NULL;

   this->probes = NULL;
   this->numProbes = 0;

   this->update_timer = new Timer();

   this->wMin = 0.0;
   this->wMax = 1.0;
   this->wPostTime = -1.0;
   this->wPostPatches = NULL;
   this->writeCompressedWeights = true;
   this->fileType = PVP_WGT_FILE_TYPE; // Subclass's initialize_base() gets called after HyPerConn's initialize_base(), so this can be changed in subclasses.

   wPatches=NULL;
   // axonalArborList=NULL;
   pIncr = NULL;
   aPostOffset = NULL;

   this->normalize_flag = true; // default value, overridden by params file parameter "normalize" in initNormalize()
   this->plasticityFlag = false;
   this->shrinkPatches_flag = false; // default value, overridden by params file parameter "normalize" in initNormalize()
   this->normalize_arbors_individually = true;
   this->normalize_max = false;
   this->normalize_max = false;
   this->normalize_zero_offset = false;
   this->normalize_cutoff = 0.0f;

   return PV_SUCCESS;
}

int HyPerConn::createArbors() {
   wPatches = (PVPatch***) calloc(numAxonalArborLists, sizeof(PVPatch**));
   if( wPatches == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   gSynPatchStart = (pvdata_t ***) calloc( numAxonalArborLists, sizeof(pvdata_t **) );
   if( gSynPatchStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   pvdata_t ** gSynPatchStartBuffer = (pvdata_t **) calloc(
         (this->shrinkPatches_flag ? numAxonalArborLists : 1)
               * preSynapticLayer()->getNumExtended(), sizeof(pvdata_t *));
   if (gSynPatchStartBuffer == NULL) {
      createArborsOutOfMemory();
      assert(false);
   }
   for (int k = 0; k < numAxonalArborLists; k++) {
      gSynPatchStart[k] = gSynPatchStartBuffer
            + this->shrinkPatches_flag * k * preSynapticLayer()->getNumExtended();
   }

   aPostOffset = (size_t **) calloc(numAxonalArborLists, sizeof(size_t *));
   if( aPostOffset == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   size_t * aPostOffsetBuffer = (size_t *) calloc(
         (this->shrinkPatches_flag ? numAxonalArborLists : 1)
               * preSynapticLayer()->getNumExtended(), sizeof(size_t));
   if( aPostOffsetBuffer == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   for( int k=0; k<numAxonalArborLists; k++ ) {
      aPostOffset[k] = aPostOffsetBuffer
            + this->shrinkPatches_flag * k * preSynapticLayer()->getNumExtended();
   }
   delays = (int *) calloc(numAxonalArborLists, sizeof(int));
   if( delays == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   patchDataStart = (pvdata_t **) calloc(numAxonalArborLists, sizeof(pvdata_t *));
   if( patchDataStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   pIncrDataStart = (pvdata_t **) calloc(numAxonalArborLists, sizeof(pvdata_t *));
   if( pIncrDataStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   return PV_SUCCESS;
}

void HyPerConn::createArborsOutOfMemory() {
   connOutOfMemory("HyPerConn::createArbors()");
}


//!
/*!
 * REMARKS:
 *      - Each neuron in the pre-synaptic layer can project "up"
 *      a number of arbors. Each arbor connects to a patch in the post-synaptic
 *      layer.
 *      - writeTime and writeStep are used to write post-synaptic patches.These
 *      patches are written every writeStep.
 *      .
 */
int HyPerConn::constructWeights(const char * filename)
{
   int status = PV_SUCCESS;

   //allocate the arbor arrays:
   createArbors();


   //const int arbor = 0;
   //numAxonalArborLists = 1;

   setPatchSize(filename);
   setPatchStrides();

   //allocate weightpatches and axonal arbors for each arbor
   for (int arborId=0;arborId<numAxonalArborLists;arborId++) {
      patchDataStart[arborId] = createWeights(wPatches, arborId);
      assert(wPatches[arborId] != NULL);
      setWPatches(wPatches[arborId], arborId);
      //wPatches[arborId] = createWeights(wPatches[arborId]); //allocates memory for weights

      // Create list of axonal arbors containing pointers to {phi,w,P,M} patches.
      //  weight patches may shrink
      status |= createAxonalArbors(arborId);
      assert(weights(arborId) != NULL);
   }  // arborId

   //initialize weights for patches:
   assert( initializeWeights(wPatches, numWeightPatches(), filename) != NULL);
   status |= initPlasticityPatches();
   assert(status == 0);
   initShrinkPatches(); // Sets shrinkPatches; derived-class methods that override initShrinkPatches must also set shrinkPatches
   if (shrinkPatches_flag) {

      for (int arborId=0;arborId<numAxonalArborLists;arborId++) {
         shrinkPatches(arborId);
      }
   }
   return status;
}

int HyPerConn::shrinkPatches(int arborId) {
   int numPatches = numWeightPatches();
   for (int kex = 0; kex < numPatches; kex++) {
      shrinkPatch(kex, arborId /* arbor */ );
   } // loop over pre-synaptic neurons

   return 0;
}

int HyPerConn::shrinkPatch(int kExt, int arborId /* PVAxonalArbor * arbor */) {
   //int kl, offset, nxPatch, nyPatch, dx, dy;
   //calcPatchSize(arborId, kex, &kl, &offset, &nxPatch, &nyPatch, &dx, &dy);

   // PVAxonalArbor * arbor = axonalArbor(kExt, arborId);

   PVPatch *weights = getWeights(kExt,arborId);
   pvdata_t * w = weights->data;

   int nxp = weights->nx;
   int nyp = weights->ny;
   //int nfp = weights->nf;

   //int sxp = weights->sx;
   //int syp = weights->sy;
   //int sfp = weights->sf;

   int maxnx = -999999;
   int minnx = 999999;
   int maxny = -999999;
   int minny = 999999;

   bool nonZeroWeightFound = false;
   // loop over all post-synaptic cells in patch
   for (int y = 0; y < nyp; y++) {
      for (int x = 0; x < nxp; x++) {
         for (int f = 0; f < nfp; f++) {
            if(w[x * sxp + y * syp + f * sfp] != 0) {
               nonZeroWeightFound=true;
               //pvdata_t weight = w[x * sxp + y * syp + f * sfp];
               maxnx = maxnx < x ? x : maxnx;
               minnx = minnx > x ? x : minnx;
               maxny = maxny < y ? y : maxny;
               minny = minny > y ? y : minny;
            }
         }
      }
   }

   if(nonZeroWeightFound) {
      int nxNew = maxnx - minnx;
      int nyNew = maxny - minny;
      int dxNew = minnx;
      int dyNew = minny;

      // adjust patch size (shrink) to fit within interior of post-synaptic layer
      //
      pvpatch_adjust(weights, sxp, syp, nxNew, nyNew, dxNew, dyNew);

      // adjust patch size (shrink) for the data to fit within interior of post-synaptic layer
      //
      // pvpatch_adjust(arbor->data, nxNew, nyNew, dxNew, dyNew);
      gSynPatchStart[arborId][kExt] += dxNew*getPostNonextStrides()->sx + dyNew*getPostNonextStrides()->sy;
      aPostOffset[arborId][kExt] += dxNew*getPostExtStrides()->sx + dyNew*getPostExtStrides()->sy; // Someone who uses these routines, please check that this is correct.
   }
   return 0;
}


int HyPerConn::initShrinkPatches() {
   PVParams * params = parent->parameters();
   shrinkPatches_flag = params->value(name, "shrinkPatches", shrinkPatches_flag);
   return PV_SUCCESS;
}

#ifdef OBSOLETE // marked Obsolete Oct 1, 2011.  We can use a default argument for weightInit instead of defining an extra form of the method
int HyPerConn::initialize(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename) {
   return initialize(name, hc, pre, post, channel, filename, NULL);
}
#endif // OBSOLETE

int HyPerConn::initialize(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename, InitWeights *weightInit)
{
   int status = PV_SUCCESS;

   int postnumchannels = post->getNumChannels();
   if(postnumchannels <= 0) {
      fprintf(stderr, "Connection \"%s\": layer \"%s\" has no channels and cannot be a post-synaptic layer.  Exiting.\n",
              name, post->getName());
      exit(EXIT_FAILURE);
   }
   if( channel < 0 || channel >= postnumchannels ) {
      fprintf(stderr, "Connection \"%s\": given channel is %d but channels for post-synaptic layer \"%s\" are 0 through %d. Exiting.\n",
              name, channel, post->getName(), post->getNumChannels()-1);
      exit(EXIT_FAILURE);
   }
   this->parent = hc;
   this->pre = pre;
   this->post = post;
   this->channel = channel;

   free(this->name);  // name will already have been set in initialize_base()
   this->name = strdup(name);
   assert(this->name != NULL);

   PVParams * inputParams = parent->parameters();
   //if a weight initializer hasn't been created already, use the default--> either 2D Gauss or read from file
   if(weightInit==NULL) {
      this->weightInitializer = handleMissingInitWeights(inputParams);
   }
   else {
      this->weightInitializer = weightInit;
   }
   assert(this->weightInitializer != NULL);

   status = setParams(inputParams /*, &defaultConnParams*/);
   defaultDelay = (int) inputParams->value(name, "delay", 0);

//   stochasticReleaseFlag = inputParams->value(name, "stochasticReleaseFlag", 0, true) != 0;
   accumulateFunctionPointer = stochasticReleaseFlag ? &pvpatch_accumulate_stochastic : &pvpatch_accumulate;

   this->connId = parent->addConnection(this);

   writeTime = parent->simulationTime();
   writeStep = parent->parameters()->value(name, "writeStep", parent->getDeltaTime());

   constructWeights(filename);

   // Find maximum delay over all the arbors and send it to the presynaptic layer
   int maxdelay = 0;
   for( int arborId=0; arborId<numberOfAxonalArborLists(); arborId++ ) {
      int curdelay = this->getDelay(arborId);
      if( maxdelay < curdelay ) maxdelay = curdelay;
   }
   int allowedDelay = pre->increaseDelayLevels(maxdelay);
   if( allowedDelay < maxdelay ) {
      if( parent->icCommunicator()->commRank() == 0 ) {
         fflush(stdout);
         fprintf(stderr, "Connection \"%s\": attempt to set delay to %d, but the maximum allowed delay is %d.  Exiting\n", name, maxdelay, allowedDelay);
      }
      exit(EXIT_FAILURE);
   }

#ifdef PV_USE_OPENCL
   initializeThreadBuffers("HyPerLayer_recv_synaptic_input");
   initializeThreadKernels("HyPerLayer_recv_synaptic_input");
#endif // PV_USE_OPENCL

   return status;
}

int HyPerConn::initPlasticityPatches()
{
   if (!plasticityFlag) return PV_SUCCESS;

   const int numAxons = numberOfAxonalArborLists();

   pIncr = (PVPatch***) calloc(numAxons, sizeof(PVPatch**));
   assert(pIncr != NULL);

   int numArbors = numWeightPatches();
   for (int arborId = 0; arborId < numAxons; arborId++) {

      this->setPIncrDataStart(arborId, createWeights(pIncr, numWeightPatches(), nxp, nyp, nfp, arborId));
      assert(pIncr[arborId] != NULL);
      // PVPatch** dWPatch = createWeights(NULL, numWeightPatches(), nxp, nyp, nfp, 0);
      // assert(dWPatch != NULL);


      // kex is in extended frame
      for (int kex = 0; kex < numArbors; kex++) {
         int kl, offset, nxPatch, nyPatch, dx, dy;

         calcPatchSize(arborId, kex, &kl, &offset, &nxPatch, &nyPatch, &dx, &dy);

         // adjust patch size (shrink) to fit within interior of post-synaptic layer
         //
         //arbor->plasticIncr = pIncr[n][kex];
         //arbor->plasticIncr = pIncr[arborId][kex];
         pvpatch_adjust(pIncr[arborId][kex], sxp, syp, nxPatch, nyPatch, dx, dy);

      } // loop over pre-synaptic neurons
      setdWPatches(pIncr[arborId], arborId);

   } // loop over arbors

   return PV_SUCCESS;
}

// set member variables specified by user
int HyPerConn::setParams(PVParams * inputParams /*, PVConnParams * p*/)
{
   const char * name = getName();

   numAxonalArborLists=(int) inputParams->value(name, "numAxonalArbors", 1, true);
   plasticityFlag = inputParams->value(name, "plasticityFlag", plasticityFlag, true);
   stochasticReleaseFlag = inputParams->value(name, "stochasticReleaseFlag", false, true) != 0;

   writeCompressedWeights = inputParams->value(name, "writeCompressedWeights", true);


   return 0;
}

// returns handle to initialized weight patches
PVPatch *** HyPerConn::initializeWeights(PVPatch *** arbors, int numPatches, const char * filename)
{
   weightInitializer->initializeWeights(arbors, numPatches, filename, this);
   // for(int arborId=0; arborId<numberOfAxonalArborLists(); arborId++) {
   //    weightInitializer->initializeWeights(arbors[arborId], arborId, numPatches, filename, this);
   // }
   initNormalize(); // Sets normalize_flag; derived-class methods that override initNormalize must also set normalize_flag
   if (normalize_flag) {
      for(int arborId=0; arborId<numberOfAxonalArborLists(); arborId++) {
         int status = normalizeWeights(arbors[arborId], numPatches, arborId);
         if (status == PV_BREAK) break;
      } // arborId
   } // normalize_flag
   return arbors;
}

InitWeights * HyPerConn::handleMissingInitWeights(PVParams * params) {
   int rank = parent->icCommunicator()->commRank();
   bool randomFlag = params->value(name, "randomFlag", 0.0f, false) != 0;
   bool smartWeights = params->value(name, "smartWeights",0.0f, false) != 0;
   bool cocircWeights = params->value(name, "cocircWeights",0.0f, false) != 0;

   if( rank == 0 ) {
      bool using_legacy_flags = randomFlag || smartWeights || cocircWeights;
      if( using_legacy_flags ) {
         fprintf(stderr, "Connection \"%s\": This method of initializing weights has been deprecated.\n"
                         "  Please pass an InitWeights object to the constructor.\n"
                         "  In buildandrun(), use the string parameter \"weightInitType\" to set the InitWeights object.\n", name);
      }
      else {
         fprintf(stderr, "Connection \"%s\": Please pass an InitWeights object to the constructor.\n"
                         "  In buildandrun(), use the string parameter \"weightInitType\" to set the InitWeights object.\n", name);

      }
   }

   if( randomFlag ) {
      if( rank==0 && (smartWeights || cocircWeights) ) {
         fprintf(stderr, "Connection \"%s\": Conflict in specifying initial weights.  randomFlag will be used\n", name);
      }
      bool uniform_weights = params->value(name, "uniformWeights", 1.0f, false) != 0;
      bool gaussian_weights = params->value(name, "gaussianWeights", 0.0f, false) != 0;
      if( uniform_weights ) {
         if( gaussian_weights ) {
            if( rank==0 ) {
               fprintf(stderr, "Connection \"%s\": Conflict in specifying distribution for random weights.  uniformWeights will be used\n", name);
            }
         }
         return new InitUniformRandomWeights();
      }
      else if( gaussian_weights ) {
         return new InitGaussianRandomWeights();
      }
      else {
         if( rank==0 ) {
            fprintf(stderr, "Connection \"%s\": No distribution for random weights specified.  gaussianWeights will be used.\n", name);
         }
         return new InitGaussianRandomWeights();
      }
   }
   else if (smartWeights) {
      if( rank==0 && (cocircWeights) ) {
         fprintf(stderr, "Connection \"%s\": Conflict in specifying initial weights.  smartWeights will be used\n", name);
      }
      return new InitSmartWeights();
   }
   else if (cocircWeights) {
      return new InitCocircWeights();
   }
   else {
      return new InitWeights();
   }
   return NULL;
}

#ifdef PV_USE_OPENCL
/**
 * Initialize OpenCL buffers.  This must be called after weights have
 * been allocated.
 */
int HyPerConn::initializeThreadBuffers(const char * kernel_name)
{
   int status = CL_SUCCESS;

   const size_t size = numWeightPatches() * nxp*nyp*nfp * sizeof(pvdata_t);

   CLDevice * device = parent->getCLDevice();

   clWeights = NULL;
   if (numAxonalArborLists > 0) {
      clWeights = (CLBuffer **) malloc(numAxonalArborLists*sizeof(CLBuffer *));
      assert(clWeights != NULL);
   }

   // create device buffers for weights
   //
   for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
      pvdata_t * wBuf = getWeights(0, arbor)->data;
      clWeights[arbor] = device->createBuffer(CL_MEM_COPY_HOST_PTR, size, wBuf);
   }

   return status;
}

int HyPerConn::initializeThreadKernels(const char * kernel_name)
{
   char kernelPath[PV_PATH_MAX+128];
   char kernelFlags[PV_PATH_MAX+128];

   int status = CL_SUCCESS;
   CLDevice * device = parent->getCLDevice();

   const char * pvRelPath = "../PetaVision";
   sprintf(kernelPath, "%s/%s/src/kernels/%s.cl", parent->getPath(), pvRelPath, kernel_name);
   sprintf(kernelFlags, "-D PV_USE_OPENCL -cl-fast-relaxed-math -I %s/%s/src/kernels/", parent->getPath(), pvRelPath);

   // create kernels
   //

   krRecvSyn = device->createKernel(kernelPath, kernel_name, kernelFlags);

   const PVLayerLoc * preLoc  = pre-> getLayerLoc();
   const PVLayerLoc * postLoc = post->getLayerLoc();

   int argid = 0;

   status |= krRecvSyn->setKernelArg(argid++, preLoc->nx);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->ny);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->nf);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->nb);

   status |= krRecvSyn->setKernelArg(argid++, nxp);
   status |= krRecvSyn->setKernelArg(argid++, nyp);
   status |= krRecvSyn->setKernelArg(argid++, nfp);

   float xScale = (float)postLoc->nx/(float)preLoc->nx;
   float yScale = (float)postLoc->ny/(float)preLoc->ny;
   status |= krRecvSyn->setKernelArg(argid++, xScale);
   status |= krRecvSyn->setKernelArg(argid++, yScale);

   clArgIdOffset = argid;  // offset into activity buffer (with delay)
   status |= krRecvSyn->setKernelArg(argid++, pre->getLayerDataStoreOffset());
   // activity buffer from DataStore
   status |= krRecvSyn->setKernelArg(argid++, pre->getLayerDataStoreCLBuffer());
   clArgIdWeights = argid; // weights
   status |= krRecvSyn->setKernelArg(argid++, clWeights[0]);
   // update variable, GSyn
   status |= krRecvSyn->setKernelArg(argid++, post->getChannelCLBuffer(getChannel()));

   return status;
}
#endif // PV_USE_OPENCL

int HyPerConn::checkPVPFileHeader(Communicator * comm, const PVLayerLoc * loc, int params[], int numParams)
{
   // use default header checker
   //
   return pvp_check_file_header(comm, loc, params, numParams);
}

int HyPerConn::checkWeightsHeader(const char * filename, int * wgtParams)
{
   // extra weight parameters
   //
   const int nxpFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NXP];
   const int nypFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NYP];
   const int nfpFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NFP];

   if (nxp != nxpFile) {
      fprintf(stderr,
              "ignoring nxp = %i in HyPerConn %s, using nxp = %i in binary file %s\n",
              nxp, name, nxpFile, filename);
      nxp = nxpFile;
   }
   if (nyp != nypFile) {
      fprintf(stderr,
              "ignoring nyp = %i in HyPerConn %s, using nyp = %i in binary file %s\n",
              nyp, name, nypFile, filename);
      nyp = nypFile;
   }
   if (nfp != nfpFile) {
      fprintf(stderr,
              "ignoring nfp = %i in HyPerConn %s, using nfp = %i in binary file %s\n",
              nfp, name, nfpFile, filename);
      nfp = nfpFile;
   }
   return 0;
}
int HyPerConn::correctPIndex(int patchIndex) {
   return patchIndex;
}

int HyPerConn::writeWeights(float time, bool last)
{
   const int numPatches = numWeightPatches();
   return writeWeights(wPatches, numPatches, NULL, time, last);
}

int HyPerConn::writeWeights(const char * filename) {
   return writeWeights(wPatches, numWeightPatches(), filename, parent->simulationTime(), true);
}

#ifdef OBSOLETE_NBANDSFORARBORS
int HyPerConn::writeWeights(float time, bool last)
{
   //const int arbor = 0;
   const int numPatches = numWeightPatches();
   for(int arborId=0;arborId<numberOfAxonalArborLists();arborId++) {
      if(writeWeights(wPatches[arborId], numPatches, NULL, time, last, arborId))
         return 1;
   }
   return 0;
}
#endif OBSOLETE_NBANDSFORARBORS

int HyPerConn::writeWeights(PVPatch *** patches, int numPatches, const char * filename, float timef, bool last) {
   int status = PV_SUCCESS;
   char path[PV_PATH_MAX];

   if (patches == NULL) return PV_SUCCESS;

   float minVal = FLT_MAX;
   float maxVal = -FLT_MAX;
   for(int arbor=0; arbor<this->numberOfAxonalArborLists(); arbor++) {
      float minVal1 = minWeight(arbor);
      if( minVal1 < minVal ) minVal = minVal1;
      float maxVal1 = maxWeight(arbor);
      if( maxVal1 > maxVal ) maxVal = maxVal1;
   }

   const PVLayerLoc * loc = pre->getLayerLoc();

   if (filename == NULL) {
      if (last) {
         snprintf(path, PV_PATH_MAX-1, "%s/w%d_last.pvp", parent->getOutputPath(), getConnectionId());
      }
      else {
         snprintf(path, PV_PATH_MAX - 1, "%s/w%d.pvp", parent->getOutputPath(), getConnectionId());
      }
   }
   else {
      snprintf(path, PV_PATH_MAX-1, "%s", filename);
   }

   Communicator * comm = parent->icCommunicator();

   bool append = last ? false : ioAppend;

   status = PV::writeWeights(path, comm, (double) timef, append,
                             loc, nxp, nyp, nfp, minVal, maxVal,
                             patches, numPatches, numberOfAxonalArborLists(), writeCompressedWeights, fileType);
   assert(status == 0);

   return status;
}

#ifdef OBSOLETE_NBANDSFORARBORS
int HyPerConn::writeWeights(PVPatch ** patches, int numPatches,
                            const char * filename, float time, bool last, int arborId)
{
   int status = 0;
   char path[PV_PATH_MAX];

   if (patches == NULL) return 0;

   const float minVal = minWeight(arborId);
   const float maxVal = maxWeight(arborId);

   const PVLayerLoc * loc = pre->getLayerLoc();

   if (filename == NULL) {
      if (last) {
         if(numberOfAxonalArborLists()>1)
            snprintf(path, PV_PATH_MAX-1, "%s/w%d_a%d_last.pvp", parent->getOutputPath(), getConnectionId(), arborId);
         else
            snprintf(path, PV_PATH_MAX-1, "%s/w%d_last.pvp", parent->getOutputPath(), getConnectionId());
      }
      else {
         if(numberOfAxonalArborLists()>1)
            snprintf(path, PV_PATH_MAX - 1, "%s/w%d_a%d.pvp", parent->getOutputPath(), getConnectionId(), arborId);
         else
            snprintf(path, PV_PATH_MAX - 1, "%s/w%d.pvp", parent->getOutputPath(), getConnectionId());
      }
   }
   else {
      snprintf(path, PV_PATH_MAX-1, "%s", filename);
   }

   Communicator * comm = parent->icCommunicator();

   bool append = (last) ? false : ioAppend;

   status = PV::writeWeights(path, comm, (double) time, append,
                             loc, nxp, nyp, nfp, minVal, maxVal,
                             patches, numPatches, writeCompressedWeights, fileType);
   assert(status == 0);

#ifdef DEBUG_WEIGHTS
   char outfile[PV_PATH_MAX];

   // only write first weight patch

   sprintf(outfile, "%s/w%d.tif", parent->getOutputPath(), getConnectionId());
   FILE * fd = fopen(outfile, "wb");
   if (fd == NULL) {
      fprintf(stderr, "writeWeights: ERROR opening file %s\n", outfile);
      return 1;
   }
   int arbor = 0;
   pv_tiff_write_patch(fd, patches);
   fclose(fd);
#endif // DEBUG_WEIGHTS

   return status;
}
#endif // OBSOLETE_NBANDSFORARBORS

int HyPerConn::writeTextWeights(const char * filename, int k)
{
   FILE * fd = stdout;
   char outfile[PV_PATH_MAX];

   if (filename != NULL) {
      snprintf(outfile, PV_PATH_MAX-1, "%s/%s", parent->getOutputPath(), filename);
      fd = fopen(outfile, "w");
      if (fd == NULL) {
         fprintf(stderr, "writeWeights: ERROR opening file %s\n", filename);
         return 1;
      }
   }

   fprintf(fd, "Weights for connection \"%s\", neuron %d\n", name, k);
   fprintf(fd, "   (kxPre,kyPre,kfPre)   = (%i,%i,%i)\n",
           kxPos(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf),
           kyPos(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf),
           featureIndex(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf) );
   fprintf(fd, "   (nxp,nyp,nfp)   = (%i,%i,%i)\n", (int) nxp, (int) nyp, (int) nfp);
   fprintf(fd, "   pre  (nx,ny,nf) = (%i,%i,%i)\n",
           pre->getLayerLoc()->nx, pre->getLayerLoc()->ny, pre->getLayerLoc()->nf);
   fprintf(fd, "   post (nx,ny,nf) = (%i,%i,%i)\n",
           post->getLayerLoc()->nx, post->getLayerLoc()->ny, post->getLayerLoc()->nf);
   fprintf(fd, "\n");


   //int arbor = 0;
   for(int arbor = 0; arbor<numberOfAxonalArborLists(); arbor++) {
      fprintf(fd, "displaying arbor %1.1d\n", arbor);
      // give a chance for derived classes to add extra information
      //
      writeTextWeightsExtra(fd, k, arbor);
      pv_text_write_patch(fd, wPatches[arbor][k], nfp, sxp, syp, sfp);
      fprintf(fd, "----------------------------\n");
   }

   if (fd != stdout) {
      fclose(fd);
   }

   return 0;
}

void HyPerConn::setDelay(int arborId, int delay) {
   assert(arborId>=0 && arborId<numAxonalArborLists);
   delays[arborId] = delay;
//   int numPatches = numWeightPatches();
//    for(int pID=0;pID<numPatches; pID++) {
//       axonalArbor(pID, arborId)->delay = delay;
//    }
}

// NOTE: this should be temporary until delivery interface is straightened out
//
#ifdef PV_USE_OPENCL
int HyPerConn::deliverOpenCL(Publisher * pub)
{
   int status = PV_SUCCESS;

   const PVLayerLoc * preLoc = pre->getLayerLoc();
   const size_t nxex = preLoc->nx + 2*preLoc->nb;
   const size_t nyex = preLoc->ny + 2*preLoc->nb;
   const size_t nxl = 16;
   const size_t nyl = 8;

   // for all numextended in pre

   for (int arbor = 0; arbor < numberOfAxonalArborLists(); arbor++) {
      size_t activityOffset = pre->getLayerDataStoreOffset(arbor);
      status |= krRecvSyn->setKernelArg(clArgIdOffset, activityOffset);
      status |= krRecvSyn->setKernelArg(clArgIdWeights, clWeights[arbor]);
      status |= krRecvSyn->run(nxex, nyex, nxl, nyl, 0, NULL, &evRecvSyn);
   }

   // TODO - use events properly
   status |= clWaitForEvents(1, &evRecvSyn);
   clReleaseEvent(evRecvSyn);

   return status;
}
#endif // PV_USE_OPENCL

int HyPerConn::deliver(Publisher * pub, const PVLayerCube * cube, int neighbor)
{
#ifdef DEBUG_OUTPUT
   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   printf("[%d]: HyPerConn::deliver: neighbor=%d cube=%p post=%p this=%p\n", rank, neighbor, cube, post, this);
   fflush(stdout);
#endif // DEBUG_OUTPUT

   for(int arborId=0;arborId<numberOfAxonalArborLists();arborId++) {
      int delay = getDelay(arborId);
      pub->readData(delay);
      int status = post->recvSynapticInput(this, cube, arborId);
      if (status == PV_BREAK) break;
      assert(status == PV_SUCCESS);
   }

#ifdef DEBUG_OUTPUT
   printf("[%d]: HyPerConn::delivered: \n", rank);
   fflush(stdout);
#endif // DEBUG_OUTPUT
   return 0;
}

int HyPerConn::checkpointRead(float * timef) {
   char * filename = checkpointFilename();
   InitWeights * weightsInitObject = new InitWeights();
   weightsInitObject->initializeWeights(wPatches, numDataPatches(), filename, this, timef);
   free(filename);
   return PV_SUCCESS;
}

int HyPerConn::checkpointWrite() {
   char * filename = checkpointFilename();
   int status = writeWeights(wPatches, numWeightPatches(), filename, parent->simulationTime(), true);
   free(filename);
   return status;
}

char * HyPerConn::checkpointFilename() {
   char * filename = (char *) malloc( (strlen(name)+12)*sizeof(char) );
   // routine that calls checkpointFilename should free filename when done
   if( filename != NULL ) {
      sprintf(filename, "%s_W.pvp", name);
   }
   else {
         fprintf(stderr, "Connection \"%s\", Rank %d process: unable to allocate memory for checkpointFilename.  Exiting.\n", name, parent->icCommunicator()->commRank());
         abort();
   }
   return filename;
}

int HyPerConn::insertProbe(BaseConnectionProbe * p)
{
   BaseConnectionProbe ** tmp;
   tmp = (BaseConnectionProbe **) malloc((numProbes + 1) * sizeof(BaseConnectionProbe *));
   assert(tmp != NULL);

   for (int i = 0; i < numProbes; i++) {
      tmp[i] = probes[i];
   }
   delete probes;

   probes = tmp;
   probes[numProbes] = p;

   return ++numProbes;
}

int HyPerConn::outputState(float time, bool last)
{
   int status = 0;

   if( !last ) {
      for (int i = 0; i < numProbes; i++) {
         probes[i]->outputState(time, this);
      }
   }

   if (last) {
      status = writeWeights(time, last);
      assert(status == 0);
   }
   else if ( (time >= writeTime) && (writeStep >= 0) ) {
      writeTime += writeStep;

      status = writeWeights(time, last);
      assert(status == 0);

      // append to output file after original open
      ioAppend = true;
   }

   return status;
}

int HyPerConn::updateState(float time, float dt)
{
   update_timer->start();

   int status;
   //const int axonId = 0;       // assume only one for now
   for(int axonId=0;axonId<numberOfAxonalArborLists();axonId++) {
      status = calc_dW(axonId);        // Calculate changes in weights
      // TODO error handling
      status = updateWeights(axonId);  // Apply changes in weights
   }
   update_timer->stop();
   return status;
}

int HyPerConn::calc_dW(int axonId) {
   return PV_SUCCESS;
}

//
/* M (m or pDecr->data) is an extended post-layer variable
 *
 */
int HyPerConn::updateWeights(int axonId)
{
   return 0;
}

int HyPerConn::numDataPatches()
{
   return numWeightPatches();
}

/**
 * returns the number of weight patches for the given neighbor
 * @param neighbor the id of the neighbor (0 for interior/self)
 */
int HyPerConn::numWeightPatches()
{
   // for now there is just one axonal arbor
   // extending to all neurons in extended layer
   return pre->getNumExtended();
}

PVPatch * HyPerConn::getWeights(int k, int arbor)
{
   // a separate arbor/patch of weights for every neuron
   return wPatches[arbor][k];
}

pvdata_t * HyPerConn::createWeights(PVPatch *** patches, int nPatches, int nxPatch,
      int nyPatch, int nfPatch, int axonId)
{
   // could create only a single patch with following call
   //   return createPatches(numAxonalArborLists, nxp, nyp, nfp);

   //assert(numAxonalArborLists == 1);
   assert(patches[axonId] == NULL);

   //patches = (PVPatch**) calloc(sizeof(PVPatch*), nPatches);
   patches[axonId] = (PVPatch**) calloc(nPatches, sizeof(PVPatch*));
   assert(patches[axonId] != NULL);

   // TODO - allocate space for all weights at once (inplace), return pointer to beginning of weight array
   pvdata_t * data_patches = allocWeights(patches, nPatches, nxPatch, nyPatch, nfPatch, axonId);
   return data_patches;
}

/**
 * Create a separate patch of weights for every neuron
 */
pvdata_t * HyPerConn::createWeights(PVPatch *** patches, int axonId)
{
   //const int arbor = 0;
   int nPatches = numWeightPatches();
   int nxPatch = nxp;
   int nyPatch = nyp;
   int nfPatch = nfp;

   pvdata_t * data_patches = createWeights(patches, nPatches, nxPatch, nyPatch, nfPatch, axonId);
   return data_patches;
}

int HyPerConn::deleteWeights()
{
   // to be used if createPatches is used above
   // HyPerConn::deletePatches(numAxonalArborLists, wPatches);

   for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
      int numPatches = numWeightPatches();
      if (wPatches[arbor] != NULL) {
         for (int k = 0; k < numPatches; k++) {
            pvpatch_inplace_delete(wPatches[arbor][k]);
         }
         free(wPatches[arbor]);
         wPatches[arbor] = NULL;
      }
   }

   if (wPostPatches != NULL) {
      for(int axonID=0;axonID<numberOfAxonalArborLists();axonID++) {
         const int numPostNeurons = post->getNumNeurons();
         for (int k = 0; k < numPostNeurons; k++) {
            pvpatch_inplace_delete(wPostPatches[axonID][k]);
         }
         free(wPostPatches[axonID]);
      }
      free(wPostPatches);
   }

   return 0;
}

//!
/*!
 *
 *      - Each neuron in the pre-synaptic layer projects a number of axonal
 *      arbors to the post-synaptic layer (Can they be projected accross columns too?).
 *      - numAxons is the number of axonal arbors projected by each neuron.<commit_msg>fixed deleteWeights to delete both PVPathcs and the weights themselves<commit_after>/*
 * HyPerConn.cpp
 *
 *  Created on: Oct 21, 2008
 *      Author: <NAME>
 */

#include "HyPerConn.hpp"
#include "../layers/LIF.hpp"
#include "../layers/PVLayer.h"
#include "../include/default_params.h"
#include "../io/ConnectionProbe.hpp"
#include "../io/io.h"
#include "../io/fileio.hpp"
#include "../utils/conversions.h"
#include "../utils/pv_random.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "../weightinit/InitWeights.hpp"
#include "../weightinit/InitCocircWeights.hpp"
#include "../weightinit/InitSmartWeights.hpp"
#include "../weightinit/InitUniformRandomWeights.hpp"
#include "../weightinit/InitGaussianRandomWeights.hpp"

namespace PV {

// default values

//PVConnParams defaultConnParams =
//{
//   /*delay*/ 0
//   // Commenting out the same parameters that are commented out in setParams()
//   // , /*fixDelay*/ 0, /*varDelayMin*/ 0, /*varDelayMax*/ 0, /*numDelay*/ 1,
//   // /*isGraded*/ 0, /*vel*/ 45.248, /*rmin*/ 0.0, /*rmax*/ 4.0
//};

HyPerConn::HyPerConn()
{
   initialize_base();
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, NULL, NULL);
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, InitWeights *weightInit)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, NULL, weightInit);
}

// provide filename or set to NULL
HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, filename, NULL);
}

HyPerConn::HyPerConn(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename, InitWeights *weightInit)
{
   initialize_base();
   initialize(name, hc, pre, post, channel, filename, weightInit);
}


HyPerConn::~HyPerConn()
{
   if (parent->columnId() == 0) {
      printf("%32s: total time in %6s %10s: ", name, "conn", "update ");
      update_timer->elapsed_time();
      fflush(stdout);
   }
   delete update_timer;

   free(name);

#ifdef PV_USE_OPENCL
   delete krRecvSyn;

   if (clWeights != NULL) {
      for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
         delete clWeights[arbor];
      }
      free(clWeights);
      clWeights = NULL;
   }
#endif // PV_USE_OPENCL

   // assert(params != NULL); // freeing a null pointer is not an error
   //free(params);

   deleteWeights();

   // free the task information

//   for (int l = 0; l < numAxonalArborLists; l++) {
//      if ( axonalArborList[l] ) {
//         free(axonalArbor(0, l)->data);
//         // axonalArbor(0,l) frees all data patches for arbor l because all
//         // axonalArbor patches for that l were created in a single calloc().
//         free(axonalArborList[l]);
//      }
//   }
   free(*gSynPatchStart); // All gSynPatchStart[k]'s were allocated together in a single malloc call.
   free(gSynPatchStart);
   free(*aPostOffset); // All aPostOffset[k]'s were allocated together in a single malloc call.
   free(aPostOffset);

   // delete weightInitializer; // weightInitializer should be deleted by whoever called the HyPerConn constructor

}

//!
/*!
 *
 *
 *
 */
int HyPerConn::initialize_base()
{
   this->name = strdup("Unknown");
   this->nxp = 1;
   this->nyp = 1;
   this->nfp = 1;
   this->sxp = 1;
   this->syp = 1;
   this->sfp = 1;
   this->parent = NULL;
   this->connId = 0;
   this->pre = NULL;
   this->post = NULL;
   this->numAxonalArborLists = 1;
   this->channel = CHANNEL_EXC;
   this->ioAppend = false;

   this->weightInitializer = NULL;

   this->probes = NULL;
   this->numProbes = 0;

   this->update_timer = new Timer();

   this->wMin = 0.0;
   this->wMax = 1.0;
   this->wPostTime = -1.0;
   this->wPostPatches = NULL;
   this->writeCompressedWeights = true;
   this->fileType = PVP_WGT_FILE_TYPE; // Subclass's initialize_base() gets called after HyPerConn's initialize_base(), so this can be changed in subclasses.

   wPatches=NULL;
   // axonalArborList=NULL;
   pIncr = NULL;
   aPostOffset = NULL;

   this->normalize_flag = true; // default value, overridden by params file parameter "normalize" in initNormalize()
   this->plasticityFlag = false;
   this->shrinkPatches_flag = false; // default value, overridden by params file parameter "normalize" in initNormalize()
   this->normalize_arbors_individually = true;
   this->normalize_max = false;
   this->normalize_max = false;
   this->normalize_zero_offset = false;
   this->normalize_cutoff = 0.0f;

   return PV_SUCCESS;
}

int HyPerConn::createArbors() {
   wPatches = (PVPatch***) calloc(numAxonalArborLists, sizeof(PVPatch**));
   if( wPatches == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   gSynPatchStart = (pvdata_t ***) calloc( numAxonalArborLists, sizeof(pvdata_t **) );
   if( gSynPatchStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   pvdata_t ** gSynPatchStartBuffer = (pvdata_t **) calloc(
         (this->shrinkPatches_flag ? numAxonalArborLists : 1)
               * preSynapticLayer()->getNumExtended(), sizeof(pvdata_t *));
   if (gSynPatchStartBuffer == NULL) {
      createArborsOutOfMemory();
      assert(false);
   }
   for (int k = 0; k < numAxonalArborLists; k++) {
      gSynPatchStart[k] = gSynPatchStartBuffer
            + this->shrinkPatches_flag * k * preSynapticLayer()->getNumExtended();
   }

   aPostOffset = (size_t **) calloc(numAxonalArborLists, sizeof(size_t *));
   if( aPostOffset == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   size_t * aPostOffsetBuffer = (size_t *) calloc(
         (this->shrinkPatches_flag ? numAxonalArborLists : 1)
               * preSynapticLayer()->getNumExtended(), sizeof(size_t));
   if( aPostOffsetBuffer == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   for( int k=0; k<numAxonalArborLists; k++ ) {
      aPostOffset[k] = aPostOffsetBuffer
            + this->shrinkPatches_flag * k * preSynapticLayer()->getNumExtended();
   }
   delays = (int *) calloc(numAxonalArborLists, sizeof(int));
   if( delays == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   patchDataStart = (pvdata_t **) calloc(numAxonalArborLists, sizeof(pvdata_t *));
   if( patchDataStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   pIncrDataStart = (pvdata_t **) calloc(numAxonalArborLists, sizeof(pvdata_t *));
   if( pIncrDataStart == NULL ) {
      createArborsOutOfMemory();
      assert(false);
   }
   return PV_SUCCESS;
}

void HyPerConn::createArborsOutOfMemory() {
   connOutOfMemory("HyPerConn::createArbors()");
}


//!
/*!
 * REMARKS:
 *      - Each neuron in the pre-synaptic layer can project "up"
 *      a number of arbors. Each arbor connects to a patch in the post-synaptic
 *      layer.
 *      - writeTime and writeStep are used to write post-synaptic patches.These
 *      patches are written every writeStep.
 *      .
 */
int HyPerConn::constructWeights(const char * filename)
{
   int status = PV_SUCCESS;

   //allocate the arbor arrays:
   createArbors();


   //const int arbor = 0;
   //numAxonalArborLists = 1;

   setPatchSize(filename);
   setPatchStrides();

   //allocate weightpatches and axonal arbors for each arbor
   for (int arborId=0;arborId<numAxonalArborLists;arborId++) {
      patchDataStart[arborId] = createWeights(wPatches, arborId);
      assert(wPatches[arborId] != NULL);
      setWPatches(wPatches[arborId], arborId);
      //wPatches[arborId] = createWeights(wPatches[arborId]); //allocates memory for weights

      // Create list of axonal arbors containing pointers to {phi,w,P,M} patches.
      //  weight patches may shrink
      status |= createAxonalArbors(arborId);
      assert(weights(arborId) != NULL);
   }  // arborId

   //initialize weights for patches:
   assert( initializeWeights(wPatches, numWeightPatches(), filename) != NULL);
   status |= initPlasticityPatches();
   assert(status == 0);
   initShrinkPatches(); // Sets shrinkPatches; derived-class methods that override initShrinkPatches must also set shrinkPatches
   if (shrinkPatches_flag) {

      for (int arborId=0;arborId<numAxonalArborLists;arborId++) {
         shrinkPatches(arborId);
      }
   }
   return status;
}

int HyPerConn::shrinkPatches(int arborId) {
   int numPatches = numWeightPatches();
   for (int kex = 0; kex < numPatches; kex++) {
      shrinkPatch(kex, arborId /* arbor */ );
   } // loop over pre-synaptic neurons

   return 0;
}

int HyPerConn::shrinkPatch(int kExt, int arborId /* PVAxonalArbor * arbor */) {
   //int kl, offset, nxPatch, nyPatch, dx, dy;
   //calcPatchSize(arborId, kex, &kl, &offset, &nxPatch, &nyPatch, &dx, &dy);

   // PVAxonalArbor * arbor = axonalArbor(kExt, arborId);

   PVPatch *weights = getWeights(kExt,arborId);
   pvdata_t * w = weights->data;

   int nxp = weights->nx;
   int nyp = weights->ny;
   //int nfp = weights->nf;

   //int sxp = weights->sx;
   //int syp = weights->sy;
   //int sfp = weights->sf;

   int maxnx = -999999;
   int minnx = 999999;
   int maxny = -999999;
   int minny = 999999;

   bool nonZeroWeightFound = false;
   // loop over all post-synaptic cells in patch
   for (int y = 0; y < nyp; y++) {
      for (int x = 0; x < nxp; x++) {
         for (int f = 0; f < nfp; f++) {
            if(w[x * sxp + y * syp + f * sfp] != 0) {
               nonZeroWeightFound=true;
               //pvdata_t weight = w[x * sxp + y * syp + f * sfp];
               maxnx = maxnx < x ? x : maxnx;
               minnx = minnx > x ? x : minnx;
               maxny = maxny < y ? y : maxny;
               minny = minny > y ? y : minny;
            }
         }
      }
   }

   if(nonZeroWeightFound) {
      int nxNew = maxnx - minnx;
      int nyNew = maxny - minny;
      int dxNew = minnx;
      int dyNew = minny;

      // adjust patch size (shrink) to fit within interior of post-synaptic layer
      //
      pvpatch_adjust(weights, sxp, syp, nxNew, nyNew, dxNew, dyNew);

      // adjust patch size (shrink) for the data to fit within interior of post-synaptic layer
      //
      // pvpatch_adjust(arbor->data, nxNew, nyNew, dxNew, dyNew);
      gSynPatchStart[arborId][kExt] += dxNew*getPostNonextStrides()->sx + dyNew*getPostNonextStrides()->sy;
      aPostOffset[arborId][kExt] += dxNew*getPostExtStrides()->sx + dyNew*getPostExtStrides()->sy; // Someone who uses these routines, please check that this is correct.
   }
   return 0;
}


int HyPerConn::initShrinkPatches() {
   PVParams * params = parent->parameters();
   shrinkPatches_flag = params->value(name, "shrinkPatches", shrinkPatches_flag);
   return PV_SUCCESS;
}

#ifdef OBSOLETE // marked Obsolete Oct 1, 2011.  We can use a default argument for weightInit instead of defining an extra form of the method
int HyPerConn::initialize(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename) {
   return initialize(name, hc, pre, post, channel, filename, NULL);
}
#endif // OBSOLETE

int HyPerConn::initialize(const char * name, HyPerCol * hc, HyPerLayer * pre,
      HyPerLayer * post, ChannelType channel, const char * filename, InitWeights *weightInit)
{
   int status = PV_SUCCESS;

   int postnumchannels = post->getNumChannels();
   if(postnumchannels <= 0) {
      fprintf(stderr, "Connection \"%s\": layer \"%s\" has no channels and cannot be a post-synaptic layer.  Exiting.\n",
              name, post->getName());
      exit(EXIT_FAILURE);
   }
   if( channel < 0 || channel >= postnumchannels ) {
      fprintf(stderr, "Connection \"%s\": given channel is %d but channels for post-synaptic layer \"%s\" are 0 through %d. Exiting.\n",
              name, channel, post->getName(), post->getNumChannels()-1);
      exit(EXIT_FAILURE);
   }
   this->parent = hc;
   this->pre = pre;
   this->post = post;
   this->channel = channel;

   free(this->name);  // name will already have been set in initialize_base()
   this->name = strdup(name);
   assert(this->name != NULL);

   PVParams * inputParams = parent->parameters();
   //if a weight initializer hasn't been created already, use the default--> either 2D Gauss or read from file
   if(weightInit==NULL) {
      this->weightInitializer = handleMissingInitWeights(inputParams);
   }
   else {
      this->weightInitializer = weightInit;
   }
   assert(this->weightInitializer != NULL);

   status = setParams(inputParams /*, &defaultConnParams*/);
   defaultDelay = (int) inputParams->value(name, "delay", 0);

//   stochasticReleaseFlag = inputParams->value(name, "stochasticReleaseFlag", 0, true) != 0;
   accumulateFunctionPointer = stochasticReleaseFlag ? &pvpatch_accumulate_stochastic : &pvpatch_accumulate;

   this->connId = parent->addConnection(this);

   writeTime = parent->simulationTime();
   writeStep = parent->parameters()->value(name, "writeStep", parent->getDeltaTime());

   constructWeights(filename);

   // Find maximum delay over all the arbors and send it to the presynaptic layer
   int maxdelay = 0;
   for( int arborId=0; arborId<numberOfAxonalArborLists(); arborId++ ) {
      int curdelay = this->getDelay(arborId);
      if( maxdelay < curdelay ) maxdelay = curdelay;
   }
   int allowedDelay = pre->increaseDelayLevels(maxdelay);
   if( allowedDelay < maxdelay ) {
      if( parent->icCommunicator()->commRank() == 0 ) {
         fflush(stdout);
         fprintf(stderr, "Connection \"%s\": attempt to set delay to %d, but the maximum allowed delay is %d.  Exiting\n", name, maxdelay, allowedDelay);
      }
      exit(EXIT_FAILURE);
   }

#ifdef PV_USE_OPENCL
   initializeThreadBuffers("HyPerLayer_recv_synaptic_input");
   initializeThreadKernels("HyPerLayer_recv_synaptic_input");
#endif // PV_USE_OPENCL

   return status;
}

int HyPerConn::initPlasticityPatches()
{
   if (!plasticityFlag) return PV_SUCCESS;

   const int numAxons = numberOfAxonalArborLists();

   pIncr = (PVPatch***) calloc(numAxons, sizeof(PVPatch**));
   assert(pIncr != NULL);

   int numArbors = numWeightPatches();
   for (int arborId = 0; arborId < numAxons; arborId++) {

      this->setPIncrDataStart(arborId, createWeights(pIncr, numWeightPatches(), nxp, nyp, nfp, arborId));
      assert(pIncr[arborId] != NULL);
      // PVPatch** dWPatch = createWeights(NULL, numWeightPatches(), nxp, nyp, nfp, 0);
      // assert(dWPatch != NULL);


      // kex is in extended frame
      for (int kex = 0; kex < numArbors; kex++) {
         int kl, offset, nxPatch, nyPatch, dx, dy;

         calcPatchSize(arborId, kex, &kl, &offset, &nxPatch, &nyPatch, &dx, &dy);

         // adjust patch size (shrink) to fit within interior of post-synaptic layer
         //
         //arbor->plasticIncr = pIncr[n][kex];
         //arbor->plasticIncr = pIncr[arborId][kex];
         pvpatch_adjust(pIncr[arborId][kex], sxp, syp, nxPatch, nyPatch, dx, dy);

      } // loop over pre-synaptic neurons
      setdWPatches(pIncr[arborId], arborId);

   } // loop over arbors

   return PV_SUCCESS;
}

// set member variables specified by user
int HyPerConn::setParams(PVParams * inputParams /*, PVConnParams * p*/)
{
   const char * name = getName();

   numAxonalArborLists=(int) inputParams->value(name, "numAxonalArbors", 1, true);
   plasticityFlag = inputParams->value(name, "plasticityFlag", plasticityFlag, true);
   stochasticReleaseFlag = inputParams->value(name, "stochasticReleaseFlag", false, true) != 0;

   writeCompressedWeights = inputParams->value(name, "writeCompressedWeights", true);


   return 0;
}

// returns handle to initialized weight patches
PVPatch *** HyPerConn::initializeWeights(PVPatch *** arbors, int numPatches, const char * filename)
{
   weightInitializer->initializeWeights(arbors, numPatches, filename, this);
   // for(int arborId=0; arborId<numberOfAxonalArborLists(); arborId++) {
   //    weightInitializer->initializeWeights(arbors[arborId], arborId, numPatches, filename, this);
   // }
   initNormalize(); // Sets normalize_flag; derived-class methods that override initNormalize must also set normalize_flag
   if (normalize_flag) {
      for(int arborId=0; arborId<numberOfAxonalArborLists(); arborId++) {
         int status = normalizeWeights(arbors[arborId], numPatches, arborId);
         if (status == PV_BREAK) break;
      } // arborId
   } // normalize_flag
   return arbors;
}

InitWeights * HyPerConn::handleMissingInitWeights(PVParams * params) {
   int rank = parent->icCommunicator()->commRank();
   bool randomFlag = params->value(name, "randomFlag", 0.0f, false) != 0;
   bool smartWeights = params->value(name, "smartWeights",0.0f, false) != 0;
   bool cocircWeights = params->value(name, "cocircWeights",0.0f, false) != 0;

   if( rank == 0 ) {
      bool using_legacy_flags = randomFlag || smartWeights || cocircWeights;
      if( using_legacy_flags ) {
         fprintf(stderr, "Connection \"%s\": This method of initializing weights has been deprecated.\n"
                         "  Please pass an InitWeights object to the constructor.\n"
                         "  In buildandrun(), use the string parameter \"weightInitType\" to set the InitWeights object.\n", name);
      }
      else {
         fprintf(stderr, "Connection \"%s\": Please pass an InitWeights object to the constructor.\n"
                         "  In buildandrun(), use the string parameter \"weightInitType\" to set the InitWeights object.\n", name);

      }
   }

   if( randomFlag ) {
      if( rank==0 && (smartWeights || cocircWeights) ) {
         fprintf(stderr, "Connection \"%s\": Conflict in specifying initial weights.  randomFlag will be used\n", name);
      }
      bool uniform_weights = params->value(name, "uniformWeights", 1.0f, false) != 0;
      bool gaussian_weights = params->value(name, "gaussianWeights", 0.0f, false) != 0;
      if( uniform_weights ) {
         if( gaussian_weights ) {
            if( rank==0 ) {
               fprintf(stderr, "Connection \"%s\": Conflict in specifying distribution for random weights.  uniformWeights will be used\n", name);
            }
         }
         return new InitUniformRandomWeights();
      }
      else if( gaussian_weights ) {
         return new InitGaussianRandomWeights();
      }
      else {
         if( rank==0 ) {
            fprintf(stderr, "Connection \"%s\": No distribution for random weights specified.  gaussianWeights will be used.\n", name);
         }
         return new InitGaussianRandomWeights();
      }
   }
   else if (smartWeights) {
      if( rank==0 && (cocircWeights) ) {
         fprintf(stderr, "Connection \"%s\": Conflict in specifying initial weights.  smartWeights will be used\n", name);
      }
      return new InitSmartWeights();
   }
   else if (cocircWeights) {
      return new InitCocircWeights();
   }
   else {
      return new InitWeights();
   }
   return NULL;
}

#ifdef PV_USE_OPENCL
/**
 * Initialize OpenCL buffers.  This must be called after weights have
 * been allocated.
 */
int HyPerConn::initializeThreadBuffers(const char * kernel_name)
{
   int status = CL_SUCCESS;

   const size_t size = numWeightPatches() * nxp*nyp*nfp * sizeof(pvdata_t);

   CLDevice * device = parent->getCLDevice();

   clWeights = NULL;
   if (numAxonalArborLists > 0) {
      clWeights = (CLBuffer **) malloc(numAxonalArborLists*sizeof(CLBuffer *));
      assert(clWeights != NULL);
   }

   // create device buffers for weights
   //
   for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
      pvdata_t * wBuf = getWeights(0, arbor)->data;
      clWeights[arbor] = device->createBuffer(CL_MEM_COPY_HOST_PTR, size, wBuf);
   }

   return status;
}

int HyPerConn::initializeThreadKernels(const char * kernel_name)
{
   char kernelPath[PV_PATH_MAX+128];
   char kernelFlags[PV_PATH_MAX+128];

   int status = CL_SUCCESS;
   CLDevice * device = parent->getCLDevice();

   const char * pvRelPath = "../PetaVision";
   sprintf(kernelPath, "%s/%s/src/kernels/%s.cl", parent->getPath(), pvRelPath, kernel_name);
   sprintf(kernelFlags, "-D PV_USE_OPENCL -cl-fast-relaxed-math -I %s/%s/src/kernels/", parent->getPath(), pvRelPath);

   // create kernels
   //

   krRecvSyn = device->createKernel(kernelPath, kernel_name, kernelFlags);

   const PVLayerLoc * preLoc  = pre-> getLayerLoc();
   const PVLayerLoc * postLoc = post->getLayerLoc();

   int argid = 0;

   status |= krRecvSyn->setKernelArg(argid++, preLoc->nx);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->ny);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->nf);
   status |= krRecvSyn->setKernelArg(argid++, preLoc->nb);

   status |= krRecvSyn->setKernelArg(argid++, nxp);
   status |= krRecvSyn->setKernelArg(argid++, nyp);
   status |= krRecvSyn->setKernelArg(argid++, nfp);

   float xScale = (float)postLoc->nx/(float)preLoc->nx;
   float yScale = (float)postLoc->ny/(float)preLoc->ny;
   status |= krRecvSyn->setKernelArg(argid++, xScale);
   status |= krRecvSyn->setKernelArg(argid++, yScale);

   clArgIdOffset = argid;  // offset into activity buffer (with delay)
   status |= krRecvSyn->setKernelArg(argid++, pre->getLayerDataStoreOffset());
   // activity buffer from DataStore
   status |= krRecvSyn->setKernelArg(argid++, pre->getLayerDataStoreCLBuffer());
   clArgIdWeights = argid; // weights
   status |= krRecvSyn->setKernelArg(argid++, clWeights[0]);
   // update variable, GSyn
   status |= krRecvSyn->setKernelArg(argid++, post->getChannelCLBuffer(getChannel()));

   return status;
}
#endif // PV_USE_OPENCL

int HyPerConn::checkPVPFileHeader(Communicator * comm, const PVLayerLoc * loc, int params[], int numParams)
{
   // use default header checker
   //
   return pvp_check_file_header(comm, loc, params, numParams);
}

int HyPerConn::checkWeightsHeader(const char * filename, int * wgtParams)
{
   // extra weight parameters
   //
   const int nxpFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NXP];
   const int nypFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NYP];
   const int nfpFile = wgtParams[NUM_BIN_PARAMS + INDEX_WGT_NFP];

   if (nxp != nxpFile) {
      fprintf(stderr,
              "ignoring nxp = %i in HyPerConn %s, using nxp = %i in binary file %s\n",
              nxp, name, nxpFile, filename);
      nxp = nxpFile;
   }
   if (nyp != nypFile) {
      fprintf(stderr,
              "ignoring nyp = %i in HyPerConn %s, using nyp = %i in binary file %s\n",
              nyp, name, nypFile, filename);
      nyp = nypFile;
   }
   if (nfp != nfpFile) {
      fprintf(stderr,
              "ignoring nfp = %i in HyPerConn %s, using nfp = %i in binary file %s\n",
              nfp, name, nfpFile, filename);
      nfp = nfpFile;
   }
   return 0;
}
int HyPerConn::correctPIndex(int patchIndex) {
   return patchIndex;
}

int HyPerConn::writeWeights(float time, bool last)
{
   const int numPatches = numWeightPatches();
   return writeWeights(wPatches, numPatches, NULL, time, last);
}

int HyPerConn::writeWeights(const char * filename) {
   return writeWeights(wPatches, numWeightPatches(), filename, parent->simulationTime(), true);
}

#ifdef OBSOLETE_NBANDSFORARBORS
int HyPerConn::writeWeights(float time, bool last)
{
   //const int arbor = 0;
   const int numPatches = numWeightPatches();
   for(int arborId=0;arborId<numberOfAxonalArborLists();arborId++) {
      if(writeWeights(wPatches[arborId], numPatches, NULL, time, last, arborId))
         return 1;
   }
   return 0;
}
#endif OBSOLETE_NBANDSFORARBORS

int HyPerConn::writeWeights(PVPatch *** patches, int numPatches, const char * filename, float timef, bool last) {
   int status = PV_SUCCESS;
   char path[PV_PATH_MAX];

   if (patches == NULL) return PV_SUCCESS;

   float minVal = FLT_MAX;
   float maxVal = -FLT_MAX;
   for(int arbor=0; arbor<this->numberOfAxonalArborLists(); arbor++) {
      float minVal1 = minWeight(arbor);
      if( minVal1 < minVal ) minVal = minVal1;
      float maxVal1 = maxWeight(arbor);
      if( maxVal1 > maxVal ) maxVal = maxVal1;
   }

   const PVLayerLoc * loc = pre->getLayerLoc();

   if (filename == NULL) {
      if (last) {
         snprintf(path, PV_PATH_MAX-1, "%s/w%d_last.pvp", parent->getOutputPath(), getConnectionId());
      }
      else {
         snprintf(path, PV_PATH_MAX - 1, "%s/w%d.pvp", parent->getOutputPath(), getConnectionId());
      }
   }
   else {
      snprintf(path, PV_PATH_MAX-1, "%s", filename);
   }

   Communicator * comm = parent->icCommunicator();

   bool append = last ? false : ioAppend;

   status = PV::writeWeights(path, comm, (double) timef, append,
                             loc, nxp, nyp, nfp, minVal, maxVal,
                             patches, numPatches, numberOfAxonalArborLists(), writeCompressedWeights, fileType);
   assert(status == 0);

   return status;
}

#ifdef OBSOLETE_NBANDSFORARBORS
int HyPerConn::writeWeights(PVPatch ** patches, int numPatches,
                            const char * filename, float time, bool last, int arborId)
{
   int status = 0;
   char path[PV_PATH_MAX];

   if (patches == NULL) return 0;

   const float minVal = minWeight(arborId);
   const float maxVal = maxWeight(arborId);

   const PVLayerLoc * loc = pre->getLayerLoc();

   if (filename == NULL) {
      if (last) {
         if(numberOfAxonalArborLists()>1)
            snprintf(path, PV_PATH_MAX-1, "%s/w%d_a%d_last.pvp", parent->getOutputPath(), getConnectionId(), arborId);
         else
            snprintf(path, PV_PATH_MAX-1, "%s/w%d_last.pvp", parent->getOutputPath(), getConnectionId());
      }
      else {
         if(numberOfAxonalArborLists()>1)
            snprintf(path, PV_PATH_MAX - 1, "%s/w%d_a%d.pvp", parent->getOutputPath(), getConnectionId(), arborId);
         else
            snprintf(path, PV_PATH_MAX - 1, "%s/w%d.pvp", parent->getOutputPath(), getConnectionId());
      }
   }
   else {
      snprintf(path, PV_PATH_MAX-1, "%s", filename);
   }

   Communicator * comm = parent->icCommunicator();

   bool append = (last) ? false : ioAppend;

   status = PV::writeWeights(path, comm, (double) time, append,
                             loc, nxp, nyp, nfp, minVal, maxVal,
                             patches, numPatches, writeCompressedWeights, fileType);
   assert(status == 0);

#ifdef DEBUG_WEIGHTS
   char outfile[PV_PATH_MAX];

   // only write first weight patch

   sprintf(outfile, "%s/w%d.tif", parent->getOutputPath(), getConnectionId());
   FILE * fd = fopen(outfile, "wb");
   if (fd == NULL) {
      fprintf(stderr, "writeWeights: ERROR opening file %s\n", outfile);
      return 1;
   }
   int arbor = 0;
   pv_tiff_write_patch(fd, patches);
   fclose(fd);
#endif // DEBUG_WEIGHTS

   return status;
}
#endif // OBSOLETE_NBANDSFORARBORS

int HyPerConn::writeTextWeights(const char * filename, int k)
{
   FILE * fd = stdout;
   char outfile[PV_PATH_MAX];

   if (filename != NULL) {
      snprintf(outfile, PV_PATH_MAX-1, "%s/%s", parent->getOutputPath(), filename);
      fd = fopen(outfile, "w");
      if (fd == NULL) {
         fprintf(stderr, "writeWeights: ERROR opening file %s\n", filename);
         return 1;
      }
   }

   fprintf(fd, "Weights for connection \"%s\", neuron %d\n", name, k);
   fprintf(fd, "   (kxPre,kyPre,kfPre)   = (%i,%i,%i)\n",
           kxPos(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf),
           kyPos(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf),
           featureIndex(k,pre->getLayerLoc()->nx + 2*pre->getLayerLoc()->nb,
                 pre->getLayerLoc()->ny + 2*pre->getLayerLoc()->nb, pre->getLayerLoc()->nf) );
   fprintf(fd, "   (nxp,nyp,nfp)   = (%i,%i,%i)\n", (int) nxp, (int) nyp, (int) nfp);
   fprintf(fd, "   pre  (nx,ny,nf) = (%i,%i,%i)\n",
           pre->getLayerLoc()->nx, pre->getLayerLoc()->ny, pre->getLayerLoc()->nf);
   fprintf(fd, "   post (nx,ny,nf) = (%i,%i,%i)\n",
           post->getLayerLoc()->nx, post->getLayerLoc()->ny, post->getLayerLoc()->nf);
   fprintf(fd, "\n");


   //int arbor = 0;
   for(int arbor = 0; arbor<numberOfAxonalArborLists(); arbor++) {
      fprintf(fd, "displaying arbor %1.1d\n", arbor);
      // give a chance for derived classes to add extra information
      //
      writeTextWeightsExtra(fd, k, arbor);
      pv_text_write_patch(fd, wPatches[arbor][k], nfp, sxp, syp, sfp);
      fprintf(fd, "----------------------------\n");
   }

   if (fd != stdout) {
      fclose(fd);
   }

   return 0;
}

void HyPerConn::setDelay(int arborId, int delay) {
   assert(arborId>=0 && arborId<numAxonalArborLists);
   delays[arborId] = delay;
//   int numPatches = numWeightPatches();
//    for(int pID=0;pID<numPatches; pID++) {
//       axonalArbor(pID, arborId)->delay = delay;
//    }
}

// NOTE: this should be temporary until delivery interface is straightened out
//
#ifdef PV_USE_OPENCL
int HyPerConn::deliverOpenCL(Publisher * pub)
{
   int status = PV_SUCCESS;

   const PVLayerLoc * preLoc = pre->getLayerLoc();
   const size_t nxex = preLoc->nx + 2*preLoc->nb;
   const size_t nyex = preLoc->ny + 2*preLoc->nb;
   const size_t nxl = 16;
   const size_t nyl = 8;

   // for all numextended in pre

   for (int arbor = 0; arbor < numberOfAxonalArborLists(); arbor++) {
      size_t activityOffset = pre->getLayerDataStoreOffset(arbor);
      status |= krRecvSyn->setKernelArg(clArgIdOffset, activityOffset);
      status |= krRecvSyn->setKernelArg(clArgIdWeights, clWeights[arbor]);
      status |= krRecvSyn->run(nxex, nyex, nxl, nyl, 0, NULL, &evRecvSyn);
   }

   // TODO - use events properly
   status |= clWaitForEvents(1, &evRecvSyn);
   clReleaseEvent(evRecvSyn);

   return status;
}
#endif // PV_USE_OPENCL

int HyPerConn::deliver(Publisher * pub, const PVLayerCube * cube, int neighbor)
{
#ifdef DEBUG_OUTPUT
   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   printf("[%d]: HyPerConn::deliver: neighbor=%d cube=%p post=%p this=%p\n", rank, neighbor, cube, post, this);
   fflush(stdout);
#endif // DEBUG_OUTPUT

   for(int arborId=0;arborId<numberOfAxonalArborLists();arborId++) {
      int delay = getDelay(arborId);
      pub->readData(delay);
      int status = post->recvSynapticInput(this, cube, arborId);
      if (status == PV_BREAK) break;
      assert(status == PV_SUCCESS);
   }

#ifdef DEBUG_OUTPUT
   printf("[%d]: HyPerConn::delivered: \n", rank);
   fflush(stdout);
#endif // DEBUG_OUTPUT
   return 0;
}

int HyPerConn::checkpointRead(float * timef) {
   char * filename = checkpointFilename();
   InitWeights * weightsInitObject = new InitWeights();
   weightsInitObject->initializeWeights(wPatches, numDataPatches(), filename, this, timef);
   free(filename);
   return PV_SUCCESS;
}

int HyPerConn::checkpointWrite() {
   char * filename = checkpointFilename();
   int status = writeWeights(wPatches, numWeightPatches(), filename, parent->simulationTime(), true);
   free(filename);
   return status;
}

char * HyPerConn::checkpointFilename() {
   char * filename = (char *) malloc( (strlen(name)+12)*sizeof(char) );
   // routine that calls checkpointFilename should free filename when done
   if( filename != NULL ) {
      sprintf(filename, "%s_W.pvp", name);
   }
   else {
         fprintf(stderr, "Connection \"%s\", Rank %d process: unable to allocate memory for checkpointFilename.  Exiting.\n", name, parent->icCommunicator()->commRank());
         abort();
   }
   return filename;
}

int HyPerConn::insertProbe(BaseConnectionProbe * p)
{
   BaseConnectionProbe ** tmp;
   tmp = (BaseConnectionProbe **) malloc((numProbes + 1) * sizeof(BaseConnectionProbe *));
   assert(tmp != NULL);

   for (int i = 0; i < numProbes; i++) {
      tmp[i] = probes[i];
   }
   delete probes;

   probes = tmp;
   probes[numProbes] = p;

   return ++numProbes;
}

int HyPerConn::outputState(float time, bool last)
{
   int status = 0;

   if( !last ) {
      for (int i = 0; i < numProbes; i++) {
         probes[i]->outputState(time, this);
      }
   }

   if (last) {
      status = writeWeights(time, last);
      assert(status == 0);
   }
   else if ( (time >= writeTime) && (writeStep >= 0) ) {
      writeTime += writeStep;

      status = writeWeights(time, last);
      assert(status == 0);

      // append to output file after original open
      ioAppend = true;
   }

   return status;
}

int HyPerConn::updateState(float time, float dt)
{
   update_timer->start();

   int status;
   //const int axonId = 0;       // assume only one for now
   for(int axonId=0;axonId<numberOfAxonalArborLists();axonId++) {
      status = calc_dW(axonId);        // Calculate changes in weights
      // TODO error handling
      status = updateWeights(axonId);  // Apply changes in weights
   }
   update_timer->stop();
   return status;
}

int HyPerConn::calc_dW(int axonId) {
   return PV_SUCCESS;
}

//
/* M (m or pDecr->data) is an extended post-layer variable
 *
 */
int HyPerConn::updateWeights(int axonId)
{
   return 0;
}

int HyPerConn::numDataPatches()
{
   return numWeightPatches();
}

/**
 * returns the number of weight patches for the given neighbor
 * @param neighbor the id of the neighbor (0 for interior/self)
 */
int HyPerConn::numWeightPatches()
{
   // for now there is just one axonal arbor
   // extending to all neurons in extended layer
   return pre->getNumExtended();
}

PVPatch * HyPerConn::getWeights(int k, int arbor)
{
   // a separate arbor/patch of weights for every neuron
   return wPatches[arbor][k];
}

pvdata_t * HyPerConn::createWeights(PVPatch *** patches, int nPatches, int nxPatch,
      int nyPatch, int nfPatch, int axonId)
{
   // could create only a single patch with following call
   //   return createPatches(numAxonalArborLists, nxp, nyp, nfp);

   //assert(numAxonalArborLists == 1);
   assert(patches[axonId] == NULL);

   //patches = (PVPatch**) calloc(sizeof(PVPatch*), nPatches);
   patches[axonId] = (PVPatch**) calloc(nPatches, sizeof(PVPatch*));
   assert(patches[axonId] != NULL);

   // TODO - allocate space for all weights at once (inplace), return pointer to beginning of weight array
   pvdata_t * data_patches = allocWeights(patches, nPatches, nxPatch, nyPatch, nfPatch, axonId);
   return data_patches;
}

/**
 * Create a separate patch of weights for every neuron
 */
pvdata_t * HyPerConn::createWeights(PVPatch *** patches, int axonId)
{
   //const int arbor = 0;
   int nPatches = numWeightPatches();
   int nxPatch = nxp;
   int nyPatch = nyp;
   int nfPatch = nfp;

   pvdata_t * data_patches = createWeights(patches, nPatches, nxPatch, nyPatch, nfPatch, axonId);
   return data_patches;
}

int HyPerConn::deleteWeights()
{
   // to be used if createPatches is used above
   // HyPerConn::deletePatches(numAxonalArborLists, wPatches);

   for (int arbor = 0; arbor < numAxonalArborLists; arbor++) {
      int numPatches = numWeightPatches();
      if (wPatches[arbor] != NULL) {
         for (int k = 0; k < numPatches; k++) {
            pvpatch_inplace_delete(wPatches[arbor][k]);
         }
         free(wPatches[arbor]);
         wPatches[arbor] = NULL;
      }
      free(this->patchDataStart[arbor]);
      this->patchDataStart[arbor] = NULL;
      if (pIncr[arbor] != NULL) {
         for (int k = 0; k < numPatches; k++) {
            pvpatch_inplace_delete(pIncr[arbor][k]);
         }
         free(pIncr[arbor]);
         pIncr[arbor] = NULL;
      }
      free(this->pIncrDataStart[arbor]);
      this->pIncrDataStart[arbor] = NULL;
  }

   if (wPostPatches != NULL) {
      for(int axonID=0;axonID<numberOfAxonalArborLists();axonID++) {
         const int numPostNeurons = post->getNumNeurons();
         for (int k = 0; k < numPostNeurons; k++) {
            pvpatch_inplace_delete(wPostPatches[axonID][k]);
         }
         free(wPostPatches[axonID]);
      }
      free(wPostPatches);
   }

   return 0;
}

//!
/*!
 *
 *      - Each neuron in the pre-synaptic layer projects a number of axonal
 *      arbors to the post-synaptic layer (Can they be projected accross columns too?).
 *      - numAxons is the number of axonal arbors projected by each neuron.
{'fraction_non_alphanumeric': 0.13749671275727, 'fraction_numerical': 0.0059931625351215935, 'mean_word_length': 3.1954590325765055, 'pattern_counts': {'":': 18, '<': 88, '<?xml version=': 0, '>': 461, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3371205', 'n_tokens_mistral': 25283, 'n_tokens_neox': 23989, 'n_words': 5842}