示例#1
0
def locregress_weights_old ( x, span = 0.2, order = 1, use_pbar = False ):
    '''DO NOT USE'''
    # Shortcuts
    N = len( x )
    n_thresh = ceil( span * N )
    
    # Pre-allocate
    ret = np.zeros( (N, N) )
    
    method = 2
    
    if use_pbar:
        iterator = pbar( range( N ) )
    else:
        iterator = range( N )
    
    for i in iterator:
        x0 = x[i]
        dx = x - x0
        
        # Choose adaptive kernel width
        # TODO Do without sorting
        h_thresh = sort( abs( dx ) )[n_thresh]
        
        if method == 1:
            # Old method
            w = _kernel_tricube( dx, h_thresh )
            S1 = sum( w * dx )
            S2 = sum( w * (dx ** 2) )

            den = sum( w * S2 - w * dx * S1 )

            for j in range( N ):
                ret[i,j] = (1 / den) * ( w[j] * S2 - w[j] * dx[j] * S1 )
                
        if method == 2:
            # New method
            w = _kernel_tricube( dx, h_thresh )
            w_slice = w != 0

            e1 = np.zeros( (order + 1, 1) )
            e1[0] = 1.0

            bigW = np.diag( w[w_slice] )

            bigX = np.ones( (N, order + 1) )
            bigX[:, 1] = dx
            if order > 1:
                bigX[:, 2] = dx ** 2
            bigX = bigX[w_slice, :]

            A2 = np.dot( bigX.T, bigW )
            A1 = np.dot( A2, bigX )

            ret[i, w_slice] = np.dot( np.dot( e1.T, np.linalg.inv( A1 ) ), A2 )
        
    return ret
示例#2
0
def tvdbn2(X,
           step=1,
           predict_lag=1,
           reg_weight=1.0,
           kernel_width=4.0,
           kernel_thresh=0.001,
           show_pbar=False):
    ''' ... '''

    # Shortcuts
    T = X.shape[0]
    N = X.shape[1]
    ts = np.arange(T)

    # Pre-allocate output
    t_out = np.arange(predict_lag, T, step)
    ret = np.zeros((N, N, t_out.shape[0]))

    # TODO Correct mapping between lambda and alpha??
    model = lm.Ridge(alpha=reg_weight)

    t_iterator = pbar(range(t_out.shape[0])) if show_pbar else range(
        t_out.shape[0])
    for i_t in t_iterator:
        t = t_out[i_t]

        # Compute weighting kernel
        cur_kernel = _tvdbn_kernel(ts, t, kernel_width)
        # Create slicing vectors for present and past, using threshold to cut fat
        kernel_slice = np.nonzero(cur_kernel[predict_lag:] > kernel_thresh)[0]
        kernel_slice_prev = kernel_slice - predict_lag

        for ch in range(N):
            # "All other channels"
            ch_slice = np.arange(N) != ch

            # Perform regression
            Xt = X[kernel_slice_prev, :][:, ch_slice]
            for ch_x in range(N - 1):
                Xt[:, ch_x] = np.multiply(cur_kernel[kernel_slice], Xt[:,
                                                                       ch_x])

            Yt = X[kernel_slice, :][:, ch]
            Yt = np.multiply(cur_kernel[kernel_slice], Yt)

            result = np.dot(
                np.dot(
                    np.linalg.pinv((np.dot(Xt.T, Xt)) +
                                   reg_weight * np.eye(N - 1)), Xt.T), Yt.T)

            # Add regression output to return value
            ret[ch, ch_slice, i_t] = result

    return ret
示例#3
0
文件: ar.py 项目: itsthefedora/maxcog
def tvdbn2 ( X,
             step = 1,
             predict_lag = 1,
             reg_weight = 1.0,
             kernel_width = 4.0,
             kernel_thresh = 0.001,
             show_pbar = False):
    ''' ... '''

    # Shortcuts
    T = X.shape[0]
    N = X.shape[1]
    ts = np.arange( T )
    
    # Pre-allocate output
    t_out = np.arange( predict_lag, T, step )
    ret = np.zeros( (N, N, t_out.shape[0]) )
    
    # TODO Correct mapping between lambda and alpha??
    model = lm.Ridge( alpha = reg_weight )
    
    t_iterator = pbar( range( t_out.shape[0] ) ) if show_pbar else range( t_out.shape[0] )
    for i_t in t_iterator:
        t = t_out[i_t]

        # Compute weighting kernel
        cur_kernel = _tvdbn_kernel( ts, t, kernel_width )
        # Create slicing vectors for present and past, using threshold to cut fat
        kernel_slice = np.nonzero( cur_kernel[predict_lag:] > kernel_thresh )[0]
        kernel_slice_prev = kernel_slice - predict_lag
        
        for ch in range( N ):
            # "All other channels"
            ch_slice = np.arange( N ) != ch
            
            # Perform regression
            Xt = X[kernel_slice_prev,:][:,ch_slice]
            for ch_x in range( N - 1 ):
                Xt[:, ch_x] = np.multiply( cur_kernel[kernel_slice], Xt[:, ch_x] )

            Yt = X[kernel_slice,:][:,ch]
            Yt = np.multiply( cur_kernel[kernel_slice], Yt )

            result = np.dot( np.dot( np.linalg.pinv( ( np.dot( Xt.T, Xt ) ) + reg_weight * np.eye( N - 1 ) ),
                                     Xt.T ),
                             Yt.T )
            
            # Add regression output to return value
            ret[ch, ch_slice, i_t] = result
    
    return ret
示例#4
0
文件: ar.py 项目: itsthefedora/maxcog
def tvdbn ( X,
            step = 1,
            predict_lag = 1,
            reg_weight = 1.0,
            kernel_width = 4.0,
            kernel_thresh = 0.001,
            show_pbar = False ):
    ''' ... '''

    # Shortcuts
    T = X.shape[0]
    N = X.shape[1]
    ts = np.arange( T )
    
    # Pre-allocate output
    t_out = np.arange( predict_lag, T, step )
    ret = np.zeros( (N, N, t_out.shape[0]) )
    
    # TODO Correct mapping between lambda and alpha??
    model = lm.Ridge( alpha = reg_weight )
    
    t_iterator = pbar( range( t_out.shape[0] ) ) if show_pbar else range( t_out.shape[0] )
    for i_t in t_iterator:
        t = t_out[i_t]

        # Compute weighting kernel
        cur_kernel = _tvdbn_kernel( ts, t, kernel_width )
        # Create slicing vectors for present and past, using threshold to cut fat
        kernel_slice = np.nonzero( cur_kernel[predict_lag:] > kernel_thresh )[0]
        kernel_slice_prev = kernel_slice - predict_lag
        
        for ch in range( N ):
            # "All other channels"
            ch_slice = np.arange( N ) != ch
            
            # Perform regression
            # TODO Don't know why I have to double-index like this
            model.fit( X[kernel_slice_prev,:][:,ch_slice],
                       X[kernel_slice,:][:,ch],
                       sample_weight = cur_kernel[kernel_slice] )
            
            # Add regression output to return value
            params = model.coef_
            ret[ch, ch_slice, i_t] = params
    
    return ret
示例#5
0
    def extract(self):
        '''Pulls out the data from all events into a LabeledArray. Each event is
        placed into its own entry along the 'trial' axis of the returned array.

        Output is a LabeledArray of signature ('time', 'channel', 'trial')
        '''

        # TODO Get this to work without copying ...

        # Preallocate return value
        ret_axes = self._axes()
        ret_array = np.zeros(tuple(map(len, ret_axes.values())))
        i = 0
        event_names = []
        for event in pbar(self.events):
            ret_array[:, :, i] = self.eeg[self._t_slice(event['start_idx']), :]
            event_names.append(event['name'])
            i += 1

        ret_axes['trial'] = recfunctions.append_fields(ret_axes['trial'],
                                                       'event_name',
                                                       np.array(event_names))
        return LabeledArray(ret_array, ret_axes)
示例#6
0
    def extract ( self ):
        '''Pulls out the data from all events into a LabeledArray. Each event is
        placed into its own entry along the 'trial' axis of the returned array.

        Output is a LabeledArray of signature ('time', 'channel', 'trial')
        '''

        # TODO Get this to work without copying ...

        # Preallocate return value
        ret_axes = self._axes()
        ret_array = np.zeros( tuple( map( len, ret_axes.values() ) ) )
        i = 0
        event_names = []
        for event in pbar( self.events ):
            ret_array[:, :, i] = self.eeg[self._t_slice( event['start_idx'] ), :]
            event_names.append( event[ 'name' ] )
            i += 1

        ret_axes[ 'trial' ] = recfunctions.append_fields( ret_axes[ 'trial' ], 
                                                         'event_name', 
                                                         np.array( event_names ) )
        return LabeledArray( ret_array, ret_axes )
示例#7
0
def fm(eeg,
       lock_events,
       aux_ch=[],
       bad_ch=[],
       taps_filt=200,
       f_filt=[70.0, 110.0],
       decimation=1,
       t_buffer=0.5,
       t_pre=1.0,
       t_safe=0.1,
       t_post=3.0,
       stat_method='bins',
       alpha=0.05,
       nu_max=100,
       plt_width=8,
       plt_height_per=0.15,
       plt_cmax=12):
    '''fm - ...'''

    # Basics
    fs_raw = eeg.get_rate()
    fs = fs_raw / decimation

    all_ch = eeg.get_labels()
    exclude_ch = aux_ch + bad_ch
    car_ch = [s for s in all_ch if s not in exclude_ch]

    n_t_raw = fs_raw * (t_pre + t_post + 2 * t_buffer)
    n_ch = len(car_ch)
    n_trials = len(lock_events)

    ## Preprocessing

    # CAR
    eeg.auto_CAR(exclude_ch)

    # Filtering & trial separation

    #print( 'Filtering:' )

    # Construct FIR BP filter parameters
    a_filt = np.array([1.0])
    b_filt = sig.firwin(taps_filt, f_filt, nyq=fs / 2.0, pass_zero=False)

    # Test decimation to get bounds
    if decimation > 1:
        event_start = fs * (t_pre + t_buffer) + 1
        t_slice = range(int(round(event_start - fs_raw * (t_pre + t_buffer))),
                        int(round(event_start + fs_raw * (t_post + t_buffer))))
        #tv_raw = np.linspace( -(t_pre + t_buffer), (t_post + t_buffer), len( t_slice ) )
        cur_data_raw = eeg[t_slice, [car_ch[0]]]
        cur_data = sig.decimate(cur_data_raw, decimation, axis=0)
        n_t = len(cur_data)
    else:
        n_t = n_t_raw

    # Preallocate total data
    big_data = np.zeros((n_t, n_ch, n_trials))

    i_trial = 0
    n_eta = 10  # TODO Magic

    for event_code, event_start, event_duration in pbar(lock_events,
                                                        task='Filtering'):

        #print( '.', end = '' )

        t_slice = range(int(round(event_start - fs_raw * (t_pre + t_buffer))),
                        int(round(event_start + fs_raw * (t_post + t_buffer))))

        timer_start = time.clock()

        cur_data_raw = eeg[t_slice, car_ch]
        if decimation > 1:
            cur_data = sig.decimate(cur_data_raw, decimation, axis=0)
        else:
            cur_data = cur_data_raw

        #print( cur_data_raw.shape )
        #print( cur_data.shape )

        cur_data_filt = sig.filtfilt(b_filt, a_filt, cur_data, axis=0)
        cur_data_env = np.abs(sig.hilbert(cur_data_filt, axis=0))

        big_data[:, :, i_trial] = cur_data_env

        timer_end = time.clock()
        i_trial = i_trial + 1

        # Show ETA
        if False and not (i_trial % n_eta):

            dt = timer_end - timer_start  # s
            dmin, dsec = dt // 60, dt % 60

            eta = dt * (n_trials - i_trial)
            emin, esec = eta // 60, eta % 60

            print(' ETA: {:01.0f}m {:04.1f}s'.format(emin, esec))

    #print( 'Done.' )

    ## Aggregate baseline

    baseline_mean = np.zeros((n_ch))
    baseline_std = np.zeros((n_ch))

    t_slice_baseline = range(int(round(fs * t_buffer)),
                             int(round(fs * (t_buffer + t_pre - t_safe))))
    n_baseline = len(t_slice_baseline) * n_trials

    for i in range(n_ch):
        baseline_mean[i] = np.mean(
            np.log(np.ravel(big_data[t_slice_baseline, i, :])))
        baseline_std[i] = np.std(
            np.log(np.ravel(big_data[t_slice_baseline, i, :])))

    ## Log-transform and baseline normalize data

    if False:
        big_data_norm = np.zeros(big_data.shape)
        for i_ch in range(n_ch):
            for i_trial in range(n_trials):
                big_data_norm[:, i_ch, i_trial] = (
                    (np.log(big_data[:, i_ch, i_trial]) - baseline_mean[i_ch])
                    / (baseline_std[i] / np.sqrt(n_trials)))

    ## Statistics

    #print( 'Statistics:' )

    if stat_method == 'bins':

        big_data_mean = np.mean(np.log(big_data), axis=2)
        big_data_std = np.std(np.log(big_data), axis=2)
        big_data_mean_norm = np.zeros(big_data_mean.shape)
        for i in range(n_ch):
            big_data_mean_norm[:,
                               i] = (big_data_mean[:, i] - baseline_mean[i]
                                     ) / (baseline_std[i] / np.sqrt(n_trials))

        n_tests = fs * t_post

        alpha_pointwise = alpha / 2.0
        alpha_global = alpha_pointwise / n_tests

        t_crit_cache = [
            dist.t.ppf(1 - alpha_global, nu) for nu in range(0, nu_max)
        ]
        z_crit = dist.norm.ppf(1 - alpha_global)
        t_crit = lambda nu: t_crit_cache[nu] if nu < nu_max else z_crit

        t_stat = lambda t, ch: (
            (big_data_mean[t, ch] - baseline_mean[ch]) / np.sqrt(
                (big_data_std[t, ch]**2 / n_trials) +
                (baseline_std[ch]**2 / n_baseline)))
        nu_stat = lambda t, ch: (((big_data_std[t, ch]**2 / n_trials) +
                                  (baseline_std[ch]**2 / n_baseline))**2 /
                                 ((big_data_std[t, ch]**4 / (n_trials**2 *
                                                             (n_trials - 1))) +
                                  (baseline_std[ch]**4 / (n_baseline**2 *
                                                          (n_baseline - 1)))))

        big_data_t = np.zeros(big_data_mean.shape)
        big_data_nu = np.zeros(big_data_mean.shape)
        big_data_t_crit = np.zeros(big_data_mean.shape)

        for i_ch in pbar(range(n_ch), task='Statisticsing'):
            for t in range(int(n_t)):
                big_data_t[t, i_ch] = t_stat(t, i_ch)
                big_data_nu[t, i_ch] = nu_stat(t, i_ch)
                big_data_t_crit[t, i_ch] = t_crit(
                    int(np.floor(big_data_nu[t, i_ch])))

        big_data_thresh = np.abs(big_data_t) > big_data_t_crit

    #print( 'Done.' )

    ## Construct FM object

    ## Display

    plt_height = plt_height_per * n_ch

    fig, ax = plt.subplots(figsize=(plt_width, plt_height))

    im = ax.imshow(big_data_thresh.T * big_data_mean_norm.T,
                   aspect='auto',
                   interpolation='none',
                   cmap=cm.Spectral_r,
                   extent=(-(t_pre + t_buffer), t_post + t_buffer, -0.5,
                           n_ch - 0.5),
                   clim=(-plt_cmax, plt_cmax))

    ax.set_xlim(-t_pre, t_post)

    ax.set_yticks(range(n_ch))
    ax.set_yticklabels(car_ch)

    ax.grid(True)

    fig.colorbar(im)

    plt.show()

    return big_data_mean_norm.T
示例#8
0
def sr(eeg,
       lock_events,
       ch,
       aux_ch=[],
       bad_ch=[],
       decimation=1,
       t_buffer=0.5,
       t_pre=1.0,
       t_safe=0.1,
       t_post=3.0,
       spec_dict={},
       plt_width=8,
       plt_height=6,
       plt_cmax=12):
    '''sr - ...'''

    # Basics
    fs_raw = eeg.get_rate()
    fs = fs_raw / decimation

    all_ch = eeg.get_labels()
    exclude_ch = aux_ch + bad_ch
    car_ch = [s for s in all_ch if s not in exclude_ch]

    n_t_raw = fs_raw * (t_pre + t_post + 2 * t_buffer)
    n_trials = len(lock_events)

    ## Preprocessing

    # CAR
    eeg.auto_CAR(exclude_ch)

    ## Trial separation

    make_t_slice = lambda x: range(
        int(round(x - fs_raw * (t_pre + t_buffer))),
        int(round(x + fs_raw * (t_post + t_buffer))))

    # Test decimation to get bounds
    event_start = fs * (t_pre + t_buffer) + 1
    t_slice_raw = make_t_slice(event_start)
    #tv_raw = np.linspace( -(t_pre + t_buffer), (t_post + t_buffer), len( t_slice ) )
    cur_data_raw = np.squeeze(eeg[t_slice_raw, [ch]])
    if decimation > 1:
        cur_data = sig.decimate(cur_data_raw, decimation)
    else:
        cur_data = cur_data_raw
    n_t = len(cur_data)

    # Test spectrogram to get bounds
    f_spec, t_spec, spec_test = sig.spectrogram(np.squeeze(cur_data),
                                                axis=0,
                                                fs=fs,
                                                **spec_dict)
    t_spec = t_spec - (t_pre + t_buffer)

    n_t_spec = len(t_spec)
    n_f_spec = len(f_spec)

    # Preallocate total data
    big_data = np.zeros((n_t_spec, n_f_spec, n_trials))

    i_trial = 0
    n_eta = 10  # TODO Magic

    for event_code, event_start, event_duration in pbar(lock_events,
                                                        task='Filtering'):

        #print( '.', end = '' )

        t_slice_raw = make_t_slice(event_start)

        cur_data_raw = np.squeeze(eeg[t_slice_raw, [ch]])
        if decimation > 1:
            cur_data = sig.decimate(cur_data_raw, decimation, axis=0)
        else:
            cur_data = cur_data_raw

        tmp1, tmp2, cur_data_spec = sig.spectrogram(np.squeeze(cur_data),
                                                    axis=0,
                                                    fs=fs,
                                                    **spec_dict)

        big_data[:, :, i_trial] = cur_data_spec.T
        i_trial = i_trial + 1

    ## Aggregate baseline

    baseline_mean = np.zeros((n_f_spec))
    baseline_std = np.zeros((n_f_spec))

    t_slice_baseline = np.where(
        np.logical_and(t_spec > -t_pre, t_spec < -t_safe))
    n_baseline = len(t_slice_baseline) * n_trials

    for i in range(n_f_spec):
        baseline_mean[i] = np.mean(
            np.log(np.ravel(big_data[t_slice_baseline, i, :])))
        baseline_std[i] = np.std(
            np.log(np.ravel(big_data[t_slice_baseline, i, :])))

    ## Normalization

    big_data_mean = np.mean(np.log(big_data), axis=2)
    big_data_std = np.std(np.log(big_data), axis=2)
    big_data_mean_norm = np.zeros(big_data_mean.shape)
    for i in range(n_f_spec):
        big_data_mean_norm[:, i] = (big_data_mean[:, i] - baseline_mean[i]) / (
            baseline_std[i] / np.sqrt(n_trials))

    ## Display

    fig, ax = plt.subplots(figsize=(plt_width, plt_height))

    big_data_thresh = np.abs(big_data_mean_norm) > 3.0

    im = ax.imshow(big_data_thresh.T * big_data_mean_norm.T,
                   aspect='auto',
                   origin='lower',
                   interpolation='none',
                   cmap=cm.Spectral_r,
                   extent=(t_spec[0], t_spec[-1], f_spec[0], f_spec[-1]),
                   clim=(-plt_cmax, plt_cmax))

    ax.set_xlim(-t_pre, t_post)
    ax.set_ylim(0, 2000)

    #ax.set_yticks( range( n_ch ) )
    #ax.set_yticklabels( car_ch )

    ax.grid(True)
    ax.set_title(ch)

    fig.colorbar(im)

    plt.show()

    return big_data_mean_norm.T
示例#9
0
def train(model, train_loader, valid_loader, loss_function, optimizer,
          num_epochs):
    start = time.time()

    for epoch in range(1, num_epochs + 1):
        print('Epoch ' + str(epoch))

        # Port model to the gpu
        model = model.to('cuda' if torch.cuda.is_available() else 'cpu')

        # Put model on train mode
        model.train()

        # Instantiate the train loss and total train samples
        train_loss = 0.0
        train_total = 0

        for i, (inputs, targets) in enumerate(train_loader):
            # Port data to gpu
            inputs = inputs.to('cuda' if torch.cuda.is_available() else 'cpu')
            targets = targets.to(
                'cuda' if torch.cuda.is_available() else 'cpu')

            # Clear the weight gradients each batch of data
            optimizer.zero_grad()

            # Forward pass and loss
            outputs = model(inputs)
            loss = loss_function(outputs, targets)

            # Perform backward pass (calculating gradients of the weights) and use the optimizer to update weights
            loss.backward()
            optimizer.step()

            # Add loss and total samples
            train_loss += loss.item()
            train_total += targets.size(0)

            # Get number of batches in decimal form
            num_batches = len(train_loader.dataset) / train_loader.batch_size
            # If there is no excess samples, make the number of batches equal to the division, if not, add one to account for the excess
            if num_batches == int(num_batches):
                num_batches = int(num_batches)
            else:
                num_batches = int(num_batches) + 1
            # Check if number of items in the dataset is less than the batch size, if it is, change the batch size
            # Else, make the batch size the default
            if num_batches == 1:
                batch_size = len(train_loader.dataset)
            else:
                batch_size = train_loader.batch_size

            # Print the pbar and stats
            pbar((i + 1) * batch_size, num_batches * batch_size, ['Loss'],
                 [round(loss.item(), 3)])

        # Print average loss of training
        print('\nTraining - Average Loss: {}'.format(
            round(train_loss / (train_total / train_loader.batch_size), 3)))

        # Put model in evaluation mode
        model.eval()

        # Instantiate validation loss and total number of validation samples
        valid_loss = 0.0
        valid_total = 0

        # Use no grad because of no weight updates
        with torch.no_grad():
            for i, (inputs, targets) in enumerate(valid_loader):
                # Port data to gpu
                inputs = inputs.to(
                    'cuda' if torch.cuda.is_available() else 'cpu')
                targets = targets.to(
                    'cuda' if torch.cuda.is_available() else 'cpu')

                # Forward pass and loss calculation
                outputs = model(inputs)
                loss = loss_function(outputs, targets)

                # No backward pass and weight update

                # Add losses and total samples
                valid_loss += loss.item()
                valid_total += targets.size(0)

                # Get number of batches in decimal form
                num_batches = len(
                    valid_loader.dataset) / valid_loader.batch_size
                # If there is no excess samples, make the number of batches equal to the division, if not, add one to account for the excess
                if num_batches == int(num_batches):
                    num_batches = int(num_batches)
                else:
                    num_batches = int(num_batches) + 1
                # Check if number of items in the dataset is less than the batch size, if it is, change the batch size
                # Else, make the batch size the default
                if num_batches == 1:
                    batch_size = len(valid_loader.dataset)
                else:
                    batch_size = valid_loader.batch_size

                # Print the pbar and stats
                pbar((i + 1) * batch_size, num_batches * batch_size, ['Loss'],
                     [round(loss.item(), 3)])

        # Print average loss of validation
        print('\nValidation - Average Loss: {}'.format(
            round(valid_loss / (valid_total / valid_loader.batch_size), 3)))

        print()

    # Print total time for training
    total_time = time.time() - start
    print('Training complete in {:.0f}m {:.0f}s'.format(
        total_time // 60, total_time % 60))

    return model
示例#10
0
def locregress_weights ( x, span = 0.2, order = 1, fast_interior = True, return_stats = False ):
    '''DO NOT USE'''

    # Cache some constants
    n = x.shape[0] # Number of input points
    d = x.shape[1] # Dimensionality of fit
    span_samples = np.ceil( span * n ) # Number of samples in the span

    # Preallocate sparse return value
    ret = sparse.lil_matrix( (n, n) )

    # Preallocate return statistics
    stat_dict = {}

    # Apply intuition that, for regularly spaced grids, weights on the interior
    # are just shifted versions of the same kernel
    if fast_interior:

        # Find "center" point
        x_center = 0.5 * ( np.max( x, axis = 0 ) + np.min( x, axis = 0 ) )
        dx_center = x - np.tile( x_center, (n, 1) )
        dx_norm = linalg.norm( dx_center, axis = 1 )
        idx_center = np.argmin( dx_norm )

        # Find critical kernel width
        x_star = x[idx_center, :]
        dx = x - np.tile( x_star, (n, 1) )
        dx_norm = linalg.norm( dx, axis = 1 )
        dx_norm = np.sort( dx_norm, axis = 0 )
        h_interior = dx_norm[span_samples + 1]
        stat_dict['h_interior'] = h_interior

        # Find bounding box for where we can use interior kernel
        x_interior_min = np.min( x, axis = 0 ) + h_interior
        x_interior_max = np.max( x, axis = 0 ) - h_interior

        # Lambda for interior kernel
        kh_interior = lambda x_v : _kernel_tricube( linalg.norm( x_v, axis = 1 ), h_interior )

        # Compute weight matrix for the interior
        w_vec = kh_interior( dx )
        w_interior_slice = np.where( w_vec > 0 )
        w_vec = w_vec[w_interior_slice]
        w = np.diag( w_vec )

        # Include quadratic terms
        if order > 1:
            for j in range( d ):
                for k in range( j, d ):
                    dx = np.c_[dx, dx[:, j] * dx[:, k]]
        # Include affine terms
        dx = np.c_[np.ones( (n, 1) ), dx]
        # Slice out only relevant items
        dx = dx[w_interior_slice]

        e1 = np.zeros( (dx.shape[1], 1) )
        e1[0] = 1

        a2 = np.dot( dx.T, w )
        a1 = np.dot( a2, dx )

        ret_interior = np.dot( e1.T, linalg.solve( a1, a2 ) )
        # Cache the index offsets for quick copying
        offset_interior = w_interior_slice - idx_center

    # Compute weight vectors at each point

    for i in pbar( range( n ), task = 'locregress_weights' ):

        x0 = x[i, :]

        # Apply interior heuristic
        if fast_interior:

            # Check if we're in the interior
            is_interior = np.logical_and( np.logical_not( np.any( x0 <= x_interior_min ) ),
                                          np.logical_not( np.any( x0 >= x_interior_max ) ) )
            if is_interior:
                # Copy known interior weights
                ret[i, i + offset_interior] = ret_interior
                continue

        # Not in interior; have to manually compute

        # For debugging purposes, screw that lol
        #continue

        # Determine input-space deltas
        dx = x - np.tile( x0, (n, 1) )

        # Determine correct kernel width
        dx_norm = linalg.norm( dx, axis = 1 )
        dx_norm = np.sort( dx_norm, axis = 0 )
        h = dx_norm[span_samples + 1]

        # Lambda for interior kernel
        kh = lambda x_v : _kernel_tricube( linalg.norm( x_v, axis = 1 ), h )

        # Calculate weight matrix
        w_vec = kh( dx )
        w_slice = np.where( w_vec > 0 )
        w_vec = w_vec[w_slice]
        w = np.diag( w_vec )

        # Include quadratic terms
        if order > 1:
            for j in range( d ):
                for k in range( j, d ):
                    dx = np.c_[dx, dx[:, j] * dx[:, k]]
        # Include affine terms
        dx = np.c_[np.ones( (n, 1) ), dx]
        # Slice out only relevant items
        dx = dx[w_slice]

        e1 = np.zeros( (dx.shape[1], 1) )
        e1[0] = 1

        a2 = np.dot( dx.T, w )
        a1 = np.dot( a2, dx )

        ret[i, w_slice] = np.dot( e1.T, linalg.solve( a1, a2 ) )

    if return_stats:
        return ret.tocsr(), stat_dict

    return ret.tocsr()