code: def witnessError(sp, inputVectors, activeColumnsCurrentEpoch):
    
    connectionMatrix = getConnectedSyns(sp)
    batchSize        = inputVectors.shape[0]
  
    # 1st sum... over each input in batch
    Err = 0.
    for i in range(batchSize):
      activeColumns    = np.where(activeColumnsCurrentEpoch[i] > 0.)[0]
      numActiveColumns = activeColumns.shape[0]
      # 2nd sum... over each active colum
      err = 0.
      for j in activeColumns:
        # Compute hamming distance and accumulate
        err += np.sum(np.absolute(connectionMatrix[j] - inputVectors[i]))
  
      Err += err/numActiveColumns
  
    return Err/batchSize
docs: Computes a variation of a reconstruction error. It measures the average 
    hamming distance of an active column's connected synapses vector and its witnesses. 
    An input vector is called witness for a column, iff the column is among 
    the active columns for the input computed by the spatial pooler. 
    The error is given by
    \[     
        Witness Error = (1/batchSize) * \sum_{x \in InputBatch} 
                            (1/numActiveColumns) * \sum_{i active column of sdr(x)} \| x -  syn(i) \|_1.
    \]
    Note: Turns out that in our setting (x and syn(i) binary vectors) we have 
    \[
        Witness Error = Reconstruction Error.
    \]
    It can be shown that the error is optimized by the Hebbian-like update rule 
    of the spatial pooler.