def _encodeSynSparse(self, synIds, weights, cIdxOffset, cIdxMult, signMode, kernelIds=None, delays=None, softReset=False): """Encode synapses using SPARSE compression. :param np.ndarray synIds: Synapse indices. :param np.ndarray weights: Weight values. :param int cIdxOffset: cIdxOffset. :param int cIdxMult: cIdxMult. :param int signMode: Defines how inhibitory and excitatory weights are treated. 1: Signs are not shared. 2: Excitatory connections with shared sign. 3: Inhibitory connections with shared sign. :param np.ndarray kernelIds: Global indices of synapse weights. Only needed for plotting and reconstructing kernelIdMap to validate partition. :param np.ndarray delays: Delay values :param bool softReset: True if synapse is used in soft-reset mode. :return: List of synaptic entries. :rtype: list[SynEntry] """ kIds = None dlys = None numSyn = len(synIds) numSynEntries = int(math.ceil(numSyn / self._maxNumSynPerSynEntry)) for i in range(numSynEntries): # Compute id range of up to 60 synapses of current synEntry cIdStart = i * self._maxNumSynPerSynEntry cIdEnd = min(cIdStart + self._maxNumSynPerSynEntry, numSyn) # Get synaptic configuration idx = synIds[cIdStart:cIdEnd] if kernelIds is not None: kIds = kernelIds[cIdStart:cIdEnd] wgts = weights[cIdStart:cIdEnd] if delays is not None: dlys = delays[cIdStart:cIdEnd] numIdxBits = self._getNumIdxBits(np.max(idx)) # Generate synapses and synEntries synFmt = SynFmt(len(self._synFmts), cIdxOffset, cIdxMult, numIdxBits, 0, self.numWeightBits, Compression.SPARSE, signMode, self.numDelayBits, softReset) self._synFmts.append(synFmt) synEntry = SynEntry(0, idx, wgts, synFmt, kIds, dlys) self._synEntries.append(synEntry)
def compressSynFmts(synFmts, maxNumSynFmt): """Compress ``synFmts`` by pruning and merging. Finds the unique set of synFmts and merges the remaining synFmts until the number of different synFmts is below ``maxNumSynFmts``. :param list[SynFmt] synFmts: List of all synFmt objects. :param int maxNumSynFmt: Maximum number of synFmts supported by core. :returns: tuple(synFmts, synEntryToFmtMap) synFmts is a list of merged synFmt objects. synEntryToFmtMap is a mapping vector from global synEntry index to set of merged synFmts. """ # Extract synFmt properties into array for processing synFmtProperties = np.stack([synFmt.asArray() for synFmt in synFmts]) # Extract unique synFmts properties and mapping from synEntries to synFmts synFmtProperties, synEntryToFmtMap = np.unique(synFmtProperties, return_inverse=True, axis=0) numSynFmts = synFmtProperties.shape[0] # Compute pairwise distances between rows of synFmtProperties # (cIdx{Offset/Mult} and compression receive high weight because they are # not allowed to differ). It's conceivable to merge different compressions # but that requires re-encoding of synEntries. distances = np.infty * np.ones((numSynFmts, numSynFmts)) for i in range(numSynFmts): for j in range(i): distances[i, j] = _computeDistance(synFmtProperties[i], synFmtProperties[j]) # Merge closest rows of synFmtProperties iteratively until number of # distinct synFmts is small enough. while numSynFmts > maxNumSynFmt: if np.all(np.isinf(distances)): print(numSynFmts) break # Find index of shortest distance in distances. idx = np.unravel_index([np.argmin(distances)], distances.shape) idx0, idx1 = sorted([idx[0][0], idx[1][0]]) # Overwrite the first of the two closest rows with the merged row. # The merged row has the same cIdx{Offset/Mult} and compression but # uses their max of numIdxBits, numSkipBits and numWgtBits. synFmtProperties[idx0, 2:5] = \ np.max(synFmtProperties[[idx0, idx1], 2:5], axis=0) # Delete the second of the two closest rows in synFmtProperties and in # distances. synFmtProperties = np.delete(synFmtProperties, idx1, axis=0) distances = np.delete(np.delete(distances, idx1, axis=0), idx1, axis=1) # Recompute distances of all other rows to merged row. for j in range(idx0): distances[idx0, j] = _computeDistance(synFmtProperties[idx0], synFmtProperties[j]) for j in range(idx0 + 1, distances.shape[1]): distances[j, idx0] = _computeDistance(synFmtProperties[j], synFmtProperties[idx0]) # Remap synEntryToFmtMap from redundant row to merged row. synEntryToFmtMap[synEntryToFmtMap == idx1] = idx0 # Reduce all row indices above the redundant row by one. synEntryToFmtMap[synEntryToFmtMap > idx1] -= 1 numSynFmts -= 1 synFmts = [] for i in range(numSynFmts): args = synFmtProperties[i, :-4] # Convert compression arg from int to Enum. kwargs = { 'compression': Compression(synFmtProperties[i, -4]), 'signMode': synFmtProperties[i, -3], 'numDlyBits': synFmtProperties[i, -2], 'softReset': synFmtProperties[i, -1] } synFmts.append(SynFmt(i, *args, **kwargs)) return synFmts, synEntryToFmtMap
def _encodeSynDense2(self, synIds, weights, cIdxOffset, cIdxMult, signMode, kernelIds=None, delays=None, softReset=False): """Encode synapses using DENSE compression. This encoder does not create synaptic entries whenever the compartment index increments by more than 1. Instead it inserts dummy connections with weight 0. :param np.ndarray synIds: Synapse indices. :param np.ndarray weights: Weight values. :param int cIdxOffset: cIdxOffset. :param int cIdxMult: cIdxMult. :param int signMode: Defines how inhibitory and excitatory weights are treated. 1: Signs are not shared. 2: Excitatory connections with shared sign. 3: Inhibitory connections with shared sign. :param np.ndarray kernelIds: Global indices of synapse weights. Only needed for plotting and reconstructing kernelIdMap to validate partition. :param np.ndarray delays: Delay values :param bool softReset: True if synapse is used in soft-reset mode. :return: List of synaptic entries. :rtype: list[SynEntry] """ cIdsUnprocessed = np.copy(synIds) wgtsUnprocessed = np.copy(weights) dlysUnprocessed = None if delays is None else np.copy(delays) kIdsUnprocessed = None if kernelIds is None else np.copy(kernelIds) kIds = None dlys = None while len(cIdsUnprocessed): # Extract compartment index range of at most size maxNumSynPerEntry cIdStart = cIdsUnprocessed[0] mask = cIdsUnprocessed < (cIdStart + self._maxNumSynPerSynEntry) notMask = np.logical_not(mask) cIds = cIdsUnprocessed[mask] cIdEnd = np.max(cIds) + 1 # Get synaptic configuration idx = np.arange(cIdEnd - cIdStart) prefixOffset = cIdStart if kernelIds is not None: kIds = np.zeros(idx.shape, int) kIds[cIds - cIdStart] = kIdsUnprocessed[mask] kIdsUnprocessed = kIdsUnprocessed[notMask] wgts = np.zeros(idx.shape, int) wgts[cIds - cIdStart] = wgtsUnprocessed[mask] if delays is not None: dlys = np.zeros(idx.shape, int) dlys[cIds - cIdStart] = dlysUnprocessed[mask] dlysUnprocessed = dlysUnprocessed[notMask] numIdxBits = self._getNumIdxBits(prefixOffset) # Discard processed synaptic configuration cIdsUnprocessed = cIdsUnprocessed[notMask] wgtsUnprocessed = wgtsUnprocessed[notMask] # Generate synapses and synEntries synFmt = SynFmt(len(self._synFmts), cIdxOffset, cIdxMult, numIdxBits, 0, self.numWeightBits, Compression.DENSE, signMode, self.numDelayBits, softReset) self._synFmts.append(synFmt) synEntry = SynEntry(prefixOffset, idx, wgts, synFmt, kIds, dlys) self._synEntries.append(synEntry)
def _encodeSynDense1(self, synIds, weights, cIdxOffset, cIdxMult, signMode, kernelIds=None, delays=None, softReset=False): """Encode synapses using DENSE compression. This encoder creates a new synaptic entry whenever a compartment index increment greater than 1 is detected. That means no dummy connections with zero weight are created. :param np.ndarray synIds: Synapse indices. :param np.ndarray kernelIds: Weight indices. :param int cIdxOffset: cIdxOffset. :param int cIdxMult: cIdxMult. :param int signMode: Defines how inhibitory and excitatory weights are treated. 1: Signs are not shared. 2: Excitatory connections with shared sign. 3: Inhibitory connections with shared sign. :param np.ndarray kernelIds: Global indices of synapse weights. Only needed for plotting and reconstructing kernelIdMap to validate partition. :param np.ndarray delays: Delay values :param bool softReset: True if synapse is used in soft-reset mode. :return: List of synaptic entries. :rtype: list[SynEntry] """ kIds = None dlys = None numSyn = len(synIds) cIdStart = 0 # Find indices of gaps in the connection vector, in descending order. gapIds = list(np.flatnonzero(np.diff(synIds) > 1))[::-1] while cIdStart < numSyn: # Compute id range of up to 60 synapses of current synEntry or # until next non-1 compartment index increment cIdEnd = min(cIdStart + self._maxNumSynPerSynEntry, numSyn) if len(gapIds) and gapIds[-1] < cIdEnd: cIdEnd = gapIds.pop() + 1 # Get synaptic configuration idx = synIds[cIdStart:cIdEnd] prefixOffset = idx[0] if kernelIds is not None: kIds = kernelIds[cIdStart:cIdEnd] wgts = weights[cIdStart:cIdEnd] if delays is not None: dlys = delays[cIdStart:cIdEnd] numIdxBits = self._getNumIdxBits(prefixOffset) # Generate synapses and synEntries synFmt = SynFmt(len(self._synFmts), cIdxOffset, cIdxMult, numIdxBits, 0, self.numWeightBits, Compression.DENSE, signMode, self.numDelayBits, softReset) self._synFmts.append(synFmt) synEntry = SynEntry(prefixOffset, idx - prefixOffset, wgts, synFmt, kIds, dlys) self._synEntries.append(synEntry) cIdStart = cIdEnd
def _encodeSynRunLength(self, synIds, weights, cIdxOffset, cIdxMult, signMode, kernelIds=None, delays=None, softReset=False): """Encode synapses using RUNLENGTH compression. Creates a new ``SynEntry`` whenever it detects a gap >= 2**5 (limit on ``skipIdxBits``). .. note:: Assumes that first skip index is zero! :param np.ndarray synIds: Synapse indices. :param np.ndarray kernelIds: Weight indices. :param int cIdxOffset: cIdxOffset. :param int cIdxMult: cIdxMult. :param int signMode: Defines how inhibitory and excitatory weights are treated. 1: Signs are not shared. 2: Excitatory connections with shared sign. 3: Inhibitory connections with shared sign. :param np.ndarray kernelIds: Global indices of synapse weights. Only needed for plotting and reconstructing kernelIdMap to validate partition. :param np.ndarray delays: Delay values :param bool softReset: True if synapse is used in soft-reset mode. :return: List of synaptic entries. :rtype: list[SynEntry] """ kIds = None dlys = None numSyn = len(synIds) cIdStart = 0 # Find indices of gaps in the connection vector, in descending order. gapIds = list( np.flatnonzero(np.diff(synIds) >= 2**self._maxNumSkipBits))[::-1] while cIdStart < numSyn: # Compute id range of up to 60 synapses of current synEntry or # until next compartment index increment >= 2**5. cIdEnd = min(cIdStart + self._maxNumSynPerSynEntry, numSyn) if len(gapIds) and gapIds[-1] < cIdEnd: cIdEnd = gapIds.pop() + 1 # Get synaptic configuration idx = synIds[cIdStart:cIdEnd] skipIdx = np.insert(np.diff(idx), 0, 0) prefixOffset = idx[0] if kernelIds is not None: kIds = kernelIds[cIdStart:cIdEnd] wgts = weights[cIdStart:cIdEnd] if delays is not None: dlys = delays[cIdStart:cIdEnd] numIdxBits = self._getNumIdxBits(prefixOffset) numSkipBits = self._getNumSkipBits(np.max(skipIdx)) # Generate synapses and synEntries synFmt = SynFmt(len(self._synFmts), cIdxOffset, cIdxMult, numIdxBits, numSkipBits, self.numWeightBits, Compression.RUNLENGTH, signMode, self.numDelayBits, softReset) self._synFmts.append(synFmt) synEntry = SynEntry(prefixOffset, skipIdx, wgts, synFmt, kIds, dlys) self._synEntries.append(synEntry) cIdStart = cIdEnd
def test_mapSynapses_3(self): """Check generation of synGrpMap.""" verbose = False layer = Layer(layerId=0, layerType='', compartmentKwargs={}, connectionKwargs={"weightExponent": 2}, coreIdMap=np.array([]), multiplicityMap=np.array([]), postLayer=None) sf = SynFmt(synFmtId=0, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=8, compression=0, signMode=1) se0 = SynEntry(prefixOffset=0, idxs=np.array([0, 1, 2]), weights=np.array([6, 7, 8]), synFmt=sf) se1 = SynEntry(prefixOffset=10, idxs=np.array([0, 1]), weights=np.array([16, 17]), synFmt=sf) se2 = SynEntry(prefixOffset=20, idxs=np.array([0, 1, 2]), weights=np.array([26, 27, 28]), synFmt=sf) se3 = SynEntry(prefixOffset=30, idxs=np.array([0, 1]), weights=np.array([36, 37]), synFmt=sf) sg0 = SynapseGroup(groupId=0, synEntries=[[se0, se1, se2], [se3]]) sg1 = SynapseGroup(groupId=1, synEntries=[[se0], [se1], [se2, se3]]) p = Partition(partitionId=0, chipCounter=0, sizeInterleaved=-1, parentLayer=layer) p.addSynFmt(sf) p.addSynapseGroup(sg0) p.addSynapseGroup(sg1) layer.addPartition(p) numSyn = 10 * 2 board = N2Board(0) c = DnnMapper(board) core = board.allocateCores(1, numSyn)[0] c._mapSynapses(p, core) # pylint: disable=protected-access if verbose: print(c._synGrpMap) self.assertEqual(len(c._synGrpMap), 5) self.assertEqual(c._synGrpMap[(sg0, 0)], (0, 8)) self.assertEqual(c._synGrpMap[(sg0, 1)], (8, 2)) self.assertEqual(c._synGrpMap[(sg1, 0)], (10, 3)) self.assertEqual(c._synGrpMap[(sg1, 1)], (13, 2)) self.assertEqual(c._synGrpMap[(sg1, 2)], (15, 5))
def test_mapSynapses_2(self): """Check switching between synFmt for successive synEntries if synFmtId does not change.""" verbose = False layer = Layer(layerId=0, layerType='', compartmentKwargs={}, connectionKwargs={"weightExponent": 2}, coreIdMap=np.array([]), multiplicityMap=np.array([]), postLayer=None) sf0 = SynFmt(synFmtId=0, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=8, compression=0, signMode=1) sf1 = SynFmt(synFmtId=1, cIdxOffset=0, cIdxMult=2, numIdxBits=7, numSkipBits=0, numWgtBits=8, compression=1, signMode=1) se00 = SynEntry(prefixOffset=0, idxs=np.array([0, 1, 2]), weights=np.array([6, 7, 8]), synFmt=sf0) se01 = SynEntry(prefixOffset=10, idxs=np.array([0, 1]), weights=np.array([16, 17]), synFmt=sf0) se02 = SynEntry(prefixOffset=20, idxs=np.array([0, 1, 2]), weights=np.array([26, 27, 28]), synFmt=sf0) se10 = SynEntry(prefixOffset=30, idxs=np.array([0, 1]), weights=np.array([36, 37]), synFmt=sf1) sg = SynapseGroup(groupId=0, synEntries=[[se00, se01, se02], [se10]]) p = Partition(partitionId=0, chipCounter=0, sizeInterleaved=-1, parentLayer=layer) p.addSynFmt(sf0) p.addSynFmt(sf1) p.addSynapseGroup(sg) layer.addPartition(p) numSyn = 10 board = N2Board(0) c = DnnMapper(board) core = board.allocateCores(1, numSyn)[0] c._mapSynapses(p, core) # pylint: disable=protected-access if verbose: c.printCore(core, synapses=True) cIdx = [0, 1, 2, 10, 11, 20, 21, 22, 30, 31] wgts = [6, 7, 8, 16, 17, 26, 27, 28, 36, 37] synFmtIds = [1, 1, 1, 2, 2, 1, 1, 1, 3, 3] for i in range(numSyn): self.assertEqual(core.synapses[i].CIdx, cIdx[i]) self.assertEqual(core.synapses[i].Wgt, wgts[i]) self.assertEqual(core.synapses[i].synFmtId, synFmtIds[i])
def test_mapSynapses_1(self): """Check allocation of synFmts.""" verbose = False layer = Layer(layerId=0, layerType='', compartmentKwargs={}, connectionKwargs={"weightExponent": 2}, coreIdMap=np.array([]), multiplicityMap=np.array([]), postLayer=None) sf0 = SynFmt(synFmtId=0, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=8, compression=0, signMode=2) sf1 = SynFmt(synFmtId=1, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=6, compression=0, signMode=3) p = Partition(partitionId=0, chipCounter=0, sizeInterleaved=-1, parentLayer=layer) p.addSynFmt(sf0) p.addSynFmt(sf1) layer.addPartition(p) board = N2Board(0) c = DnnMapper(board) core = board.allocateCores(1, 0)[0] c._mapSynapses(p, core) # pylint: disable=protected-access if verbose: c.printCore(core, synFmts=True) # Validate number of synFmts self.assertEqual(len(core.synapseFmt.modified), 4) # Validate duplication of synFmts by checking unique fanoutTypes self.assertEqual(core.synapseFmt[1].fanoutType, 2) self.assertEqual(core.synapseFmt[2].fanoutType, 2) self.assertEqual(core.synapseFmt[3].fanoutType, 3) self.assertEqual(core.synapseFmt[4].fanoutType, 3) # Validate mapping of numWgtBits -> wgtBits self.assertEqual(core.synapseFmt[1].wgtBits, 7) self.assertEqual(core.synapseFmt[3].wgtBits, 6) # Validate wgtExp self.assertEqual(core.synapseFmt[1].wgtExp, 2)
def test_mapInputAxons_1(self): """Check mapping of InputAxonGroup with all input nodes having multiplicity 1.""" verbose = False layer = Layer(layerId=0, layerType='', compartmentKwargs={}, connectionKwargs={"weightExponent": 2}, coreIdMap=np.array([]), multiplicityMap=np.array([]), postLayer=None) sf = SynFmt(synFmtId=0, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=8, compression=0, signMode=1) se0 = SynEntry(prefixOffset=0, idxs=np.array([0, 1, 2]), weights=np.array([6, 7, 8]), synFmt=sf) se1 = SynEntry(prefixOffset=10, idxs=np.array([0, 1]), weights=np.array([16, 17]), synFmt=sf) se2 = SynEntry(prefixOffset=20, idxs=np.array([0, 1, 2]), weights=np.array([26, 27, 28]), synFmt=sf) se3 = SynEntry(prefixOffset=30, idxs=np.array([0, 1]), weights=np.array([36, 37]), synFmt=sf) sg = SynapseGroup(groupId=0, synEntries=[[se0, se1], [se1, se2, se3], [se3]]) p = Partition(partitionId=0, chipCounter=0, sizeInterleaved=-1, parentLayer=layer) p.addSynFmt(sf) p.addSynapseGroup(sg) iag = InputAxonGroup(srcNodeIds=np.array([10, 11, 12]), multiplicity=np.array([1, 1, 1]), synGroup=sg, cxBase=2, parentPartition=p) p.addInputAxonGroup(iag) layer.addPartition(p) numSyn = 14 board = N2Board(0) c = DnnMapper(board) core = board.allocateCores(1, numSyn)[0] c._mapSynapses(p, core) # pylint: disable=protected-access c._mapInputAxons(p, core) # pylint: disable=protected-access # Validate input axon sMap = core.synapseMap[0] self.assertEqual(sMap.synapsePtr, 0) self.assertEqual(sMap.synapseLen, [5, 7, 2]) self.assertEqual(sMap.popSize, 3) self.assertEqual(sMap.population32MapEntry.ptr, 0) self.assertEqual(sMap.population32MapEntry.length, 3) self.assertEqual(sMap.population32MapEntry.cxBase, 2) # Validate inAxMap self.assertTrue( np.array_equal(c._inAxMap[iag.id], np.array([[0, 0, 4], [0, 0, 4], [0, 0, 4]], int))) if verbose: c.printCore(core, inputAxons=True)
def test_mapInputAxons_4(self): """Check mapping of multiple InputAxonGroups.""" verbose = False layer = Layer(layerId=0, layerType='', compartmentKwargs={}, connectionKwargs={"weightExponent": 2}, coreIdMap=np.array([]), multiplicityMap=np.array([]), postLayer=None) sf = SynFmt(synFmtId=0, cIdxOffset=0, cIdxMult=2, numIdxBits=6, numSkipBits=0, numWgtBits=8, compression=0, signMode=1) se0 = SynEntry(prefixOffset=0, idxs=np.array([0, 1, 2]), weights=np.array([6, 7, 8]), synFmt=sf) se1 = SynEntry(prefixOffset=10, idxs=np.array([0, 1]), weights=np.array([16, 17]), synFmt=sf) se2 = SynEntry(prefixOffset=20, idxs=np.array([0, 1, 2]), weights=np.array([26, 27, 28]), synFmt=sf) se3 = SynEntry(prefixOffset=30, idxs=np.array([0, 1]), weights=np.array([36, 37]), synFmt=sf) sg0 = SynapseGroup(groupId=0, synEntries=[[se0, se1], [se1, se2, se3], [se3]]) sg1 = SynapseGroup(groupId=1, synEntries=[[se0, se1], [se2], [se1, se2, se3]]) p = Partition(partitionId=0, chipCounter=0, sizeInterleaved=-1, parentLayer=layer) p.addSynFmt(sf) p.addSynapseGroup(sg0) p.addSynapseGroup(sg1) iag0 = InputAxonGroup(srcNodeIds=np.array([10, 11, 12]), multiplicity=np.array([1, 1, 1]), synGroup=sg0, cxBase=1, parentPartition=p) iag1 = InputAxonGroup(srcNodeIds=np.array([10, 11, 12]), multiplicity=np.array([1, 2, 1]), synGroup=sg0, cxBase=2, parentPartition=p) iag2 = InputAxonGroup(srcNodeIds=np.array([10, 11, 12]), multiplicity=np.array([1, 2, 3]), synGroup=sg1, cxBase=3, parentPartition=p) p.addInputAxonGroup(iag0) p.addInputAxonGroup(iag1) p.addInputAxonGroup(iag2) layer.addPartition(p) board = N2Board(0) c = DnnMapper(board) core = board.allocateCores(1, p.numSyn)[0] c._mapSynapses(p, core) # pylint: disable=protected-access c._mapInputAxons(p, core) # pylint: disable=protected-access # Validate axons of iag0 sMapShared = core.synapseMap[0] self.assertEqual(sMapShared.synapsePtr, 0) self.assertEqual(sMapShared.synapseLen, [5, 7, 2]) self.assertEqual(sMapShared.popSize, 3) self.assertEqual(sMapShared.population32MapEntry.ptr, 0) self.assertEqual(sMapShared.population32MapEntry.length, 3) self.assertEqual(sMapShared.population32MapEntry.cxBase, 1) # Validate axons of iag1 sMapShared = core.synapseMap[1] self.assertEqual(sMapShared.synapsePtr, 0) self.assertEqual(sMapShared.synapseLen, [5, 7, 2]) self.assertEqual(sMapShared.popSize, 3) self.assertEqual(sMapShared.population32MapEntry.ptr, 0) self.assertEqual(sMapShared.population32MapEntry.length, 3) self.assertEqual(sMapShared.population32MapEntry.cxBase, 2) sMapDiscrete = core.synapseMap[2] self.assertEqual(sMapDiscrete.synapsePtr, 5) self.assertEqual(sMapDiscrete.synapseLen, 7) self.assertEqual(sMapDiscrete.discreteMapEntry.ptr, 3) self.assertEqual(sMapDiscrete.discreteMapEntry.length, 3) self.assertEqual(sMapDiscrete.discreteMapEntry.cxBase, 2) # Validate axons of iag2 sMapShared = core.synapseMap[3] self.assertEqual(sMapShared.synapsePtr, 14) self.assertEqual(sMapShared.synapseLen, [5, 3, 7]) self.assertEqual(sMapShared.popSize, 3) self.assertEqual(sMapShared.population32MapEntry.ptr, 9) self.assertEqual(sMapShared.population32MapEntry.length, 3) self.assertEqual(sMapShared.population32MapEntry.cxBase, 3) sMapDiscrete = core.synapseMap[4] self.assertEqual(sMapDiscrete.synapsePtr, 19) self.assertEqual(sMapDiscrete.synapseLen, 3) self.assertEqual(sMapDiscrete.discreteMapEntry.ptr, 12) self.assertEqual(sMapDiscrete.discreteMapEntry.length, 1) self.assertEqual(sMapDiscrete.discreteMapEntry.cxBase, 3) sMapDiscrete = core.synapseMap[5] self.assertEqual(sMapDiscrete.synapsePtr, 22) self.assertEqual(sMapDiscrete.synapseLen, 7) self.assertEqual(sMapDiscrete.discreteMapEntry.ptr, 15) self.assertEqual(sMapDiscrete.discreteMapEntry.length, 3) self.assertEqual(sMapDiscrete.discreteMapEntry.cxBase, 3) # Validate inAxMap self.assertTrue( np.array_equal(c._inAxMap[iag0.id], np.array([[0, 0, 4], [0, 0, 4], [0, 0, 4]], int))) self.assertTrue( np.array_equal(c._inAxMap[iag1.id], np.array([[1, 0, 4], [2, 0, 4], [1, 0, 4]], int))) self.assertTrue( np.array_equal(c._inAxMap[iag2.id], np.array([[3, 0, 4], [4, 0, 4], [5, 0, 4]], int))) if verbose: c.printCore(core, inputAxons=True)