Esempio n. 1
0
def _vmm_fair_create_splitter_core(height,
                                   num_corelets_per_splitter,
                                   splitter_num,
                                   num_splitter_cores,
                                   num_corelets,
                                   num_neurons=256,
                                   num_axons=256):
    '''
    Creates a splitter core.

    Arguments:
        height -- The height of the matrix. This is the number of elements in 
                  the vector which tells us how many cores we will have.
        num_corelets_per_splitter -- The number of corelets that we will need 
                                     to send the input to for a single splitter.
        splitter_num -- The index of the splitter core that we are creating. 
                        This is when we have multiple splitter cores.
    
    Keyword Arguments:
        num_neurons -- The number of neurons in a core (Default: {256})
        num_axons -- The number of axons in a core (Default: {256})
    '''
    '''
    Determining how many corelets this particular splitter will send to and how many 
    corelets are already hooked up to splitter cores.
    '''
    is_last_splitter = splitter_num == num_splitter_cores - 1
    if is_last_splitter:
        num_corelets_to_send_to = num_corelets - splitter_num * num_corelets_per_splitter
    else:
        num_corelets_to_send_to = num_corelets_per_splitter

    # The number of corelets that already have splitters sending to them
    num_corelets_already_hooked_up = splitter_num * num_corelets_per_splitter
    '''
    Creating all of the neurons in the splitter core
    '''
    neurons = []
    for corelet_num in range(num_corelets_to_send_to):
        for neuron_num in range(4 * height):
            weights = [1, 0, 0, 0]
            destination_core = [num_splitter_cores -\
                                         splitter_num + \
                                        num_corelets_already_hooked_up + \
                                        corelet_num, 0]
            destination_axon = neuron_num

            neurons.append(
                Neuron(reset_potential=0,
                       weights=weights,
                       leak=0,
                       positive_threshold=1,
                       negative_threshold=0,
                       destination_core=destination_core,
                       destination_axon=destination_axon,
                       destination_tick=0,
                       current_potential=0,
                       reset_mode=1))
    '''
    Creating the connections. Connections are just going to be 
    a bunch of identity matrices horizontally concatenated to each other
    '''
    connections = np.zeros((num_axons, num_neurons), dtype=np.int32)
    for corelet in range(num_corelets_to_send_to):
        connections[0:4 * height, corelet * 4 * height:(corelet + 1) * 4 *
                    height] = np.identity(4 * height, dtype=np.int32)

    return Core(axons=[0 for i in range(num_axons)],
                neurons=neurons,
                connections=connections.T,
                coordinates=[splitter_num, 0])
def _vmm_synpase_index_create_core(A, num_neurons=256, num_axons=256):

    height, width = A.shape
    '''
    Creating all of the connections that are going to be used in the VMM. 
    All elements in the crossbar will be connected where there are 
    valid neurons and axons. So setting all of those conections
    to one.
    '''
    connections = np.zeros((num_axons, num_neurons), dtype=np.int32)
    connections[:2 * height, :2 * width] = np.ones((2 * height, 2 * width),
                                                   dtype=np.int32)
    '''
    Creating the list of unique possible weights for every neuron. 
    This is going to depend on the specific elements in the matrix 
    so it will change everytime. This is done so we can maximuze the
    our weight reuse and try to minimize the memory footprint.
    '''
    weight_lists = []
    max_unique_weights = 0
    for col in range(width):
        weights = np.squeeze(np.asarray(A[:, col]))
        weights = np.hstack((weights, -1 * weights))
        weights = list(set(weights))
        weight_lists.append(weights)
        if len(weights) > max_unique_weights:
            max_unique_weights = len(weights)

    for index, weight_list in enumerate(weight_lists):
        for _ in range(max_unique_weights - len(weight_list)):
            weight_lists[index].append(0)
    '''
    Creating all of the neurons that are going to be used in the VMM. Don't have
    to create all `num_neurons` neurons as the simulator and hardware will place
    default neurons that don't do anything for those neurons.
    '''
    neurons = []
    for neuron_num in range(2 * width):
        neurons.append(
            Neuron(reset_potential=0,
                   weights=weight_lists[neuron_num // 2],
                   leak=0,
                   positive_threshold=1,
                   negative_threshold=-1,
                   destination_core=[0, 1],
                   destination_axon=neuron_num,
                   destination_tick=0,
                   current_potential=0,
                   reset_mode=1))

    axons = []
    for col in range(width):
        pos_neuron_axons = []
        neg_neuron_axons = []
        for row in range(height):
            pos_neuron_axons.append(weight_lists[col].index(A[row, col]))
            pos_neuron_axons.append(weight_lists[col].index(-1 * A[row, col]))
            neg_neuron_axons.append(weight_lists[col].index(-1 * A[row, col]))
            neg_neuron_axons.append(weight_lists[col].index(A[row, col]))
        axons.append(pos_neuron_axons)
        axons.append(neg_neuron_axons)

    return Core(axons=axons,
                neurons=neurons,
                connections=connections.T,
                coordinates=[0, 0]), max_unique_weights
Esempio n. 3
0
def _fair_create_third_core(A,
                            num_corelets,
                            num_splitter_cores,
                            core_coordinates,
                            first_core_feedback=True,
                            num_neurons=256,
                            num_axons=256):
    '''
    Creates the third core in the VMM corelet according 
    to the method presented in Kaitlin Fair's dissertation.
    This core applies the initial weights of [16, 1]
    to the output spikes of the second core. 

    NOTE: This will give the right answer but not a one-to-one
    spiking behavior with compass. Because of this it has 
    not been updated in a while so it will probably fail.

    Arguments:
        A -- numpy array defining the matrix we are going to use.
        num_corelets -- The number of corelets in the entire VMM. Used
                        to determine the dx necessary to get to the 
                        output bus
        num_splitter_cores -- The number of splitter cores in the entire
                                VMM. Used to determine the dx necessary
                                to get to the output bus
        core_coordinates -- the (x, y) coordinates of where the core
                            will be placed in the grid
        
    Keyword Arguments:
        first_core_feedback -- Whether there is feedback in the first core.
                               This is necessary because the last core
                               needs to know how many columns of the matrix
                               one corelet can handle so it can send to the
                               correct axons in the output bus (default: {True})
        num_neurons -- The number of neurons in a given core. (default: {256})
        num_axons -- The number of axons in a given core. (default: {256})
    '''
    '''
    Defining the hardcoded values inherent to the mapping process this is 
    to make the code more clear rather than to parameterize the network
    (different values might mess stuff up)
    '''
    num_rep_neurons_per_element = 2
    num_rep_axons_per_neuron = 2
    if first_core_feedback:
        first_core_num_neurons_per_element = 32
    else:
        first_core_num_neurons_per_element = 16

    _, width = A.shape

    # The iteration of corelet we are currrently on
    corelet_num = core_coordinates[0] - num_splitter_cores

    # This is the number of output elements that this core will output
    max_width_per_corelet = num_neurons // first_core_num_neurons_per_element
    '''
    Creating the neurons.
    All of these neurons are the same so just 
    creating a list of identical neurons
    '''
    third_core_neurons = []
    for neuron_num in range(num_neurons):
        destination_axon = neuron_num + corelet_num * \
                            num_rep_neurons_per_element * \
                            max_width_per_corelet
        destination_core = [num_corelets - corelet_num, 0]

        # Only need to create the representation neurons
        if neuron_num < num_rep_neurons_per_element * width:
            third_core_neurons.append(
                Neuron(reset_potential=0,
                       weights=[16, 1, 0, 0],
                       leak=0,
                       positive_threshold=1,
                       negative_threshold=0,
                       destination_core=destination_core,
                       destination_axon=destination_axon,
                       destination_tick=0,
                       current_potential=0,
                       reset_mode=1))
    '''
    Creating the connections
    '''
    third_core_connections = np.zeros((num_axons, num_neurons), dtype=np.int32)
    for col in range(math.ceil(num_axons / num_rep_axons_per_neuron)):
        third_core_connections[col * num_rep_axons_per_neuron:\
                                (col + 1) * num_rep_axons_per_neuron, col] = \
                                    np.ones(num_rep_axons_per_neuron, dtype=np.int32)

    return Core(axons=[i % num_rep_axons_per_neuron for i in range(num_axons)],
                neurons=third_core_neurons,
                connections=third_core_connections.T,
                coordinates=core_coordinates)
Esempio n. 4
0
def _fair_create_third_core_feedback(A,
                                     num_corelets,
                                     num_splitter_cores,
                                     core_coordinates,
                                     first_core_feedback=True,
                                     num_neurons=256,
                                     num_axons=256):
    '''
    Creates the third core in the VMM corelet according 
    to the method presented in Kaitlin Fair's dissertation.
    This method also has feedback which will eliminate the 
    ammount of positive-negative representation neurons spiking.
    Feedback is necessary to get a one-to-one match with Compass.
    This core applies the initial weights of [16, -16, 1, -1]
    to the output spikes of the second core.
    
    Arguments:
        A -- numpy array defining the matrix we are going to use.
        num_corelets -- The number of corelets in the entire VMM. Used
                        to determine the dx necessary to get to the 
                        output bus
        num_splitter_cores -- The number of splitter cores in the entire
                                VMM. Used to determine the dx necessary
                                to get to the output bus
        core_coordinates -- the (x, y) coordinates of where the core
                            will be placed in the grid
        
    Keyword Arguments:
        first_core_feedback -- Whether there is feedback in the first core.
                               This is necessary because the last core
                               needs to know how many columns of the matrix
                               one corelet can handle so it can send to the
                               correct axons in the output bus (default: {True})
        num_neurons -- The number of neurons in a given core. (default: {256})
        num_axons -- The number of axons in a given core. (default: {256})
    '''
    '''
    Defining the hardcoded values inherent to the mapping process this is 
    to make the code more clear rather than to parameterize the network
    (different values might mess stuff up)
    '''
    num_rep_neurons_per_element = 2
    num_feedback_neurons_per_element = 2
    num_rep_axons_per_neuron = 4

    if first_core_feedback:
        first_core_num_neurons_per_element = 32
    else:
        first_core_num_neurons_per_element = 16

    _, width = A.shape

    # The iteration of corelet we are currrently on
    corelet_num = core_coordinates[0] - num_splitter_cores

    # This is the number of output elements that this core will output
    max_width_per_corelet = num_neurons // first_core_num_neurons_per_element
    '''
    Creating the neurons. In this core there are three kinds of 
    neurons: representation neurons, feedback neurons, and 
    do nothing neurons. Need to program each one differently
    '''
    third_core_neurons = []

    for neuron_num in range(num_neurons):
        # If it is a pos or neg representation neuron
        if neuron_num < num_rep_neurons_per_element * width:
            '''
            Packets need to go past the remaining corelets in the grid
            '''
            destination_core = [num_corelets - corelet_num, 0]
            '''
            Axon of the output bus to write into. Need to write
            into the axon after all of the previous corelets.
            Each corelet will write num_rep_neurons_per_element * 
            max_width_per_corelet axons to the output bus as 
            that is the maximum number of elements a core can 
            handle times two as we need to output both the negative 
            and positive representation of the result
            '''
            destination_axon = neuron_num + corelet_num * \
                                num_rep_neurons_per_element * \
                                max_width_per_corelet
            '''
            Positive representation neurons will have the 
            positive weights first while negative representation
            neurons will have the negative weights first.
            '''
            if neuron_num % num_rep_neurons_per_element == 0:
                weight_list = [16, 1, -16, -1]
            else:
                weight_list = [-16, -1, 16, 1]
        # If it is a feedback neuron
        elif neuron_num < (num_rep_neurons_per_element +
                           num_feedback_neurons_per_element) * width:
            destination_core = [0, 0]
            # Send to the axon directly after the inputs from the second core
            destination_axon = num_rep_neurons_per_element * width + neuron_num

            # Feedback weights are the negative of representation weights
            if neuron_num % num_feedback_neurons_per_element == 0:
                weight_list = [-16, -1, 16, 1]
            else:
                weight_list = [16, 1, -16, -1]
        # If it is a do nothing neurons
        else:
            destination_core = [0, 0]
            destination_axon = 0
            weight_list = [0, 0, 0, 0]

        third_core_neurons.append(
            Neuron(reset_potential=0,
                   weights=weight_list,
                   leak=0,
                   positive_threshold=1,
                   negative_threshold=0,
                   destination_core=destination_core,
                   destination_axon=destination_axon,
                   destination_tick=0,
                   current_potential=0,
                   reset_mode=1))

    # Creating the connections
    third_core_connections = np.zeros((num_axons, num_neurons), dtype=np.int32)

    for col in range(width):
        # The number of columns (in the matrix) we still have to process
        num_cols_remaining = width - col
        '''
        Writing the entire column of the matrix. The row will start
        at the first axon that receives input from the second core
        and go all the way down to the feedback connections at the bottom
        '''
        start_row = col * num_rep_axons_per_neuron
        end_row = start_row + num_rep_axons_per_neuron * num_cols_remaining + \
                    num_feedback_neurons_per_element * col + \
                    num_feedback_neurons_per_element
        '''
        The columns of the synaptic crossbar where the representation
        neurons have their connections
        '''
        rep_start_col = col * num_rep_neurons_per_element
        rep_end_col = rep_start_col + num_rep_neurons_per_element
        '''
        The columns of the synaptic crossbar where the feedback 
        neurons have their connections
        '''
        feedback_start_col = rep_start_col + num_rep_neurons_per_element * width
        feedback_end_col = rep_end_col + num_feedback_neurons_per_element * width
        '''
        The number of rows inbetween the representation connections and the
        feedback connections 
        '''
        num_zeros = end_row - start_row - \
                    (num_rep_axons_per_neuron + num_feedback_neurons_per_element)
        '''
        All of the connections of the representation neurons. Look at the
        image to see why it looks like this.
        '''
        rep_connection_matrix = np.vstack(
            (np.ones((num_rep_axons_per_neuron, num_rep_neurons_per_element)),
             np.zeros((num_zeros, num_rep_neurons_per_element)),
             np.identity(num_feedback_neurons_per_element)))
        '''
        Same as representation connection but connection to feedback axons are rotated
        '''
        feedback_connection_matrix = np.vstack(
            (np.ones((num_rep_axons_per_neuron, num_rep_neurons_per_element)),
             np.zeros((num_zeros, num_rep_neurons_per_element)),
             np.rot90(np.identity(num_feedback_neurons_per_element))))

        # writing the connections to the representation neurons
        third_core_connections[start_row:end_row, \
            rep_start_col:rep_end_col] = rep_connection_matrix

        # writing the connections to the feedback neurons
        third_core_connections[start_row:end_row, \
            feedback_start_col:feedback_end_col] = feedback_connection_matrix

    # neurons instructions for axons which receive inputs from the second core
    neuron_instructions = [i % num_rep_axons_per_neuron \
                            for i in range(num_rep_axons_per_neuron * width)]

    # neuron instructions for axons which receive inputs from feedback neurons
    for i in range(num_axons - num_rep_axons_per_neuron * width):
        if i % 2 == 0:
            neuron_instructions.append(1)
        else:
            neuron_instructions.append(3)

    return Core(axons=neuron_instructions,
                neurons=third_core_neurons,
                connections=third_core_connections.T,
                coordinates=core_coordinates)
Esempio n. 5
0
def _fair_create_second_core(A,
                             core_coordinates,
                             num_neurons=256,
                             num_axons=256):
    '''
    Creates the second core in the VMM corelet according 
    to the method presented in Kaitlin Fair's dissertation.
    This core applies the initial weights of [8, 4, 2, 1]
    to the output spikes of the first core.
    
    Arguments:
        A -- numpy array defining the matrix we are going to use.
        core_coordinates -- the (x, y) coordinates of where the core
                            will be placed in the grid
        
    Keyword Arguments:
        num_neurons -- The number of neurons in a given core. (default: {256})
        num_axons -- The number of axons in a given core. (default: {256})
    '''
    '''
    Defining the hardcoded values inherent to the mapping process this is 
    to make the code more clear rather than to parameterize the network
    (different values might mess stuff up)
    '''
    num_axons_per_neuron = 4
    '''
    Creating the neurons.
    All of these neurons are the same so just 
    creating a list of identical neurons
    '''
    second_core_neurons = []
    for neuron_num in range(num_neurons):
        second_core_neurons.append(
            Neuron(reset_potential=0,
                   weights=[8, 4, 2, 1],
                   leak=0,
                   positive_threshold=1,
                   negative_threshold=0,
                   destination_core=[0, 1],
                   destination_axon=neuron_num,
                   destination_tick=0,
                   current_potential=0,
                   reset_mode=1))

    # Creating the connections
    second_core_connections = np.zeros((num_axons, num_neurons),
                                       dtype=np.int32)
    '''
    Each neuron has num_axons_per_neuron vertical sequential synapses
    which connects it to either the four most or least signifigant
    bits of the first core.
    '''
    for col in range(math.ceil(num_axons / num_axons_per_neuron)):
        row_start = col * num_axons_per_neuron
        row_end = (col + 1) * num_axons_per_neuron
        second_core_connections[row_start:row_end,
                                col] = np.ones(num_axons_per_neuron,
                                               dtype=np.int32)

    return Core(axons=[i % num_axons_per_neuron for i in range(num_axons)],
                neurons=second_core_neurons,
                connections=second_core_connections.T,
                coordinates=core_coordinates)
Esempio n. 6
0
def _fair_create_first_core_feedback(A,
                                     core_coordinates,
                                     num_neurons=256,
                                     num_axons=256):
    '''
    Creates the first core in the VMM corelet according 
    to the method presented in Kaitlin Fair's dissertation.
    For every row this core requires 16 positive representation 
    neurons, 16 negative representation neurons, 16 positive feedback
    neurons, and 16 negative feedback neurons. The positive feedback
    neurons provide feedback for the positive representation
    neurons and the negative feedback neruons provide feedback for 
    the negative representation neurons.
    
    Arguments:
        A -- numpy array defining the matrix we are going to use.
        core_coordinates -- the (x, y) coordinates of where the core
                            will be placed in the grid
        
    Keyword Arguments:
        num_neurons -- The number of neurons in a given core. (default: {256})
        num_axons -- The number of axons in a given core. (default: {256})
    '''
    '''
    Defining the hardcoded values inherent to the mapping process this is 
    to make the code more clear rather than to parameterize the network
    (different values might mess stuff up)
    '''
    num_pos_feedback_neurons_per_element = 8
    num_neg_feedback_neurons_per_element = 8
    num_pos_rep_neurons_per_element = 8
    num_neg_rep_neurons_per_element = 8
    num_feedback_neurons_per_element = num_pos_feedback_neurons_per_element + \
                                        num_neg_feedback_neurons_per_element
    num_rep_neurons_per_element = num_pos_rep_neurons_per_element + \
                                    num_neg_rep_neurons_per_element
    num_axons_per_input = 4
    num_neurons_per_element = num_feedback_neurons_per_element + \
                                num_rep_neurons_per_element

    height, width = A.shape
    '''
    Creating the neurons for the first core.
    All of these neurons differ in the weights, 
    the destination axons, and the destination
    core.
    '''
    first_core_neurons = []
    for neuron_num in range(num_neurons):
        '''
        Calculating which element the neuron is a part 
        of (neuron_group) and which neuron it is 
        for that element (neuron_id). Used to figure
        out if the neuron should be a feedback neuron
        or a representation neuron and what size it is
        '''
        neuron_group = math.floor(neuron_num / num_neurons_per_element)
        neuron_id = neuron_num % num_neurons_per_element
        # First quarter neurons are positive representation neurons
        pos_rep_neuron = neuron_id < num_pos_rep_neurons_per_element
        # Second quarter neurons are negative representation neurons
        neg_rep_neuron =  neuron_id >= num_pos_rep_neurons_per_element and \
                            neuron_id < num_rep_neurons_per_element
        # Third quarter neurons are positive feedback neurons
        pos_feedback_neuron =  neuron_id >= num_rep_neurons_per_element and \
                                neuron_id < num_rep_neurons_per_element + \
                                num_pos_rep_neurons_per_element
        # Fourth quarter neurons are negative feedback neurons
        neg_feedback_neuron =  neuron_id >= num_rep_neurons_per_element + \
                                num_pos_rep_neurons_per_element

        if pos_rep_neuron:
            weight_list = [1, -1, 0, 0]
            destination_core = [0, 1]
            destination_axon = neuron_id + neuron_group * num_rep_neurons_per_element
        elif neg_rep_neuron:
            weight_list = [-1, 1, 0, 0]
            destination_core = [0, 1]
            destination_axon = neuron_id + neuron_group * num_rep_neurons_per_element
        elif pos_feedback_neuron:
            weight_list = [-1, 1, 0, 0]
            destination_core = [0, 0]
            destination_axon = height * num_axons_per_input + \
                                neuron_group * num_feedback_neurons_per_element + \
                                neuron_id - num_rep_neurons_per_element
        elif neg_feedback_neuron:
            weight_list = [1, -1, 0, 0]
            destination_core = [0, 0]
            destination_axon = height * num_axons_per_input + \
                                neuron_group * num_feedback_neurons_per_element + \
                                neuron_id - num_rep_neurons_per_element
        else:
            print("[ERROR] Unknown neuron group.")
            exit(1)

        first_core_neurons.append(
            Neuron(reset_potential=0,
                   weights=weight_list,
                   leak=0,
                   positive_threshold=1,
                   negative_threshold=0,
                   destination_core=destination_core,
                   destination_axon=destination_axon,
                   destination_tick=0,
                   current_potential=0,
                   reset_mode=1))
    '''
    Creating the connections for the first 
    core. In Fair's implementation
    the connections is where the binary
    representation of the matrix is stored.
    We also need to create the connections for the 
    feedback neurons.
    '''
    first_core_connections = np.zeros((num_axons, num_neurons), dtype=np.int32)
    for col in range(width):
        for row in range(height):
            '''
            Obtaining the binary representation of the absolute value
            of the element in the matrix
            '''
            binary_rep = np.array([
                int(i) for i in BitArray(uint=abs(A[row, col]), length=8).bin
            ],
                                  dtype=np.int32)
            '''
            Determining the row we will write the element to.
            If the element is positive, we store the binary representation on the upper axons.
            If the element is negative, we store the binary representation on the lower axons.
            '''
            if A[row, col] > 0:
                row_start = row * 2
            else:
                row_start = (row * 2) + 2 * height
            row_end = row_start + 2  # Just writing two rows
            '''
            Determining the columns we will be writing to. We will be able to write 
            a contigious 64 (2 32 bit rows) bits into the connection array as the 
            32 sequential axons all will have the binary representation of the weights 
            and we will write that representation to both the positive and negative 
            input axons.
            '''
            col_start = col * num_neurons_per_element
            col_end = (col + 1) * num_neurons_per_element
            '''
            After determining what row we will be writing the binary representation to, we can write
            these binary values to the positive representation neurons, the negative representation neurons,
            the positive feedback neurons, and the negative feedback neurons.
            '''
            col_connections = np.vstack((binary_rep, binary_rep))
            first_core_connections[row_start:row_end,
                                   col_start:col_end] = np.hstack(
                                       (col_connections, col_connections,
                                        col_connections, col_connections))
        '''
        After writing all of the elements in the row, we need to create the 
        connection arrays for the feedback neurons. This might look confusing
        without first seeing a picture of what the cores look like but there 
        is a pretty simple pattern that arises.
        '''
        # Starting at the first row below all of the input spikes
        row_start = col * num_feedback_neurons_per_element + num_axons_per_input * height
        row_end = row_start + num_feedback_neurons_per_element

        # Starting at the first neuron of the column and going to the last
        col_start = col * num_neurons_per_element
        col_end = col_start + num_neurons_per_element
        '''
        The connections for both the positive representation neuron and 
        the negative feedback. Look at an image of the connections to
        see why it has this pattern
        '''
        pos_rep_neg_feedback_connections = np.vstack(
            (np.identity(num_pos_rep_neurons_per_element),
             np.zeros((num_pos_rep_neurons_per_element,
                       num_pos_rep_neurons_per_element))))
        '''
        The connections for both the negative representation neurons and the 
        positive feedback. Look at an image of the connections to see 
        why it has this pattern.
        '''
        neg_rep_pos_feedback_connections = np.vstack(
            (np.zeros((num_neg_rep_neurons_per_element,
                       num_neg_rep_neurons_per_element)),
             np.identity(num_neg_rep_neurons_per_element)))
        '''
        Writing the feedback connections for the entire column of the matrix. Look 
        at an image of the connections to see why it has this pattern.
        '''
        first_core_connections[row_start:row_end,
                               col_start:col_end] = np.hstack(
                                   (pos_rep_neg_feedback_connections,
                                    neg_rep_pos_feedback_connections,
                                    neg_rep_pos_feedback_connections,
                                    pos_rep_neg_feedback_connections))
    '''
    Creating the neurons instructions for every axon in the core
    '''
    # neuron instructions for the positive input axons
    axon_list = [i % 2 for i in range(2 * height)]
    # neuron instructions for the negative input axons
    axon_list += [1 - (i % 2) for i in range(2 * height)]
    for col in range(width):
        # neurons instructions for the positive feedback neurons
        axon_list += [0 for i in range(num_pos_feedback_neurons_per_element)]
        # neuron instructions for the negative feedback neurons
        axon_list += [1 for i in range(num_neg_feedback_neurons_per_element)]

    return Core(axons=axon_list,
                neurons=first_core_neurons,
                connections=first_core_connections.T,
                coordinates=core_coordinates)
Esempio n. 7
0
def vmm_mendat_create_cores(A, num_neurons=256, num_axons=256, bitwidth=4):
    '''
    Creates a list of Core objects using the method presented by Mendat et. al
    in "Word2vec World Similarities on IBM's TrueNorth Neurosynaptic System".
    
    Arguments:
        A -- numpy array defining the matrix we are going to use.
        
    Keyword Arguments:
        num_neurons -- The number of neurons in a given core. (default: {256})
        num_axons -- The number of axons in a given core. (default: {256})
        bitwidth -- The precision of the VMM. (default: {4})
    '''
    '''
    Checking if all inputs fall within the bitwidth
    '''
    within_bitwidth = np.where(A.flatten() <= 2**(bitwidth - 1) - 1, True,
                               False)
    if not np.all(within_bitwidth):
        print("[ERROR] Not all elements are " \
                "within a bitwidth of {}.".format(bitwidth))
        return None

    height, width = A.shape
    if height > num_axons / 2:
        print("[ERROR] Cannot support matrix of height: {}. " \
                "Increase the number of axons.", height)
        return None
    '''
    Splitting into multiple corelets 
    (where each corelet corresponds to two cores) 
    '''
    num_corelets = math.ceil(width / 64)
    '''
    Iterating through every corelet which contains two cores
    '''
    cores = []
    for corelet_num in range(num_corelets):
        '''
        Creating the neurons for the first core.
        All of these neurons are the same except
        for the destination axon.
        '''
        first_core_neurons = []
        for neuron_num in range(num_neurons):
            destination_axon = (math.floor(neuron_num / bitwidth) + 1) * \
                                bitwidth - 1 - neuron_num % bitwidth
            first_core_neurons.append(
                Neuron(reset_potential=0,
                       weights=[1, 1, 1, 1],
                       leak=0,
                       positive_threshold=1,
                       negative_threshold=0,
                       destination_core=[0, 1],
                       destination_axon=destination_axon,
                       destination_tick=0,
                       current_potential=0,
                       reset_mode=1))
        '''
        Creating the connections for the first 
        core. In Mendat's implementation
        the connections is where the binary
        representation of the matrix is stored.
        Iterate through every element of the
        matrix and store the binary representation
        in the connections 
        '''
        first_core_connections = np.zeros((num_axons, num_neurons),
                                          dtype=np.int32)
        for row in range(height):
            for col in range(width):
                first_core_connections[
                    row * 2, col * bitwidth:(col + 1) *
                    bitwidth] = np.array([
                        int(i)
                        for i in BitArray(int=A[row, col], length=bitwidth).bin
                    ],
                                         dtype=np.int32)
                first_core_connections[
                    row * 2 + 1, col * bitwidth:(col + 1) *
                    bitwidth] = np.array([
                        int(i) for i in BitArray(int=-1 * A[row, col],
                                                 length=bitwidth).bin
                    ],
                                         dtype=np.int32)
        '''
        Appending the first core to the core list
        '''
        cores.append(
            Core(axons=[0 for i in range(num_axons)],
                 neurons=first_core_neurons,
                 connections=first_core_connections.T,
                 coordinates=[corelet_num, 0]))
        '''
        Creating the neurons for the first core.
        All of these neurons are the same so just 
        creating a list of identical neurons
        '''
        second_core_neurons = []
        for neuron_num in range(num_neurons):
            second_core_neurons.append(
                Neuron(reset_potential=0,
                       weights=[1, 2, 4, -8],
                       leak=0,
                       positive_threshold=1,
                       negative_threshold=0,
                       destination_core=[0, 1],
                       destination_axon=neuron_num,
                       destination_tick=0,
                       current_potential=0,
                       reset_mode=1))
        '''
        Creating the connections for the second core.
        In Mendat's implementation the second core is 
        used to apply the proper weight to the spikes 
        of the previous core.
        '''
        second_core_connections = np.zeros((num_axons, num_neurons),
                                           dtype=np.int32)
        for col in range(math.ceil(num_neurons / bitwidth)):
            second_core_connections[col * bitwidth:(col + 1) * bitwidth,
                                    col] = np.ones((1, bitwidth),
                                                   dtype=np.int32)
        '''
        Appending the first core to the core list
        '''
        cores.append(
            Core(axons=[i % bitwidth for i in range(num_axons)],
                 neurons=second_core_neurons,
                 connections=second_core_connections.T,
                 coordinates=[corelet_num, 1]))

    return cores