def test_fracridge_fracs(frac, nn, pp, bb): X, y, coef_ols, _ = make_data(nn, pp, bb) # Make sure that you get the fraction you asked for coef, _ = fracridge(X, y, fracs=np.array([frac])) assert np.all( np.abs(frac - vec_len(coef, axis=0) / vec_len(coef_ols, axis=0)) < 0.01)
def test_fracridge_ols(nn, pp, bb): X, y, coef_ols, _ = make_data(nn, pp, bb) fracs = np.arange(.1, 1.1, .1) coef, alpha = fracridge(X, y, fracs=fracs) coef = coef[:, -1, ...] assert np.allclose(coef, coef_ols, atol=10e-3) assert np.all(np.diff(alpha, axis=0) <= 0)
def test_fracridge_sfm(): fdata, fbvals, fbvecs = get_fnames("sherbrooke_3shell") gtab = gradient_table(fbvals, fbvecs, b0_threshold=0) img = nib.load(fdata) data = img.get_fdata() mask = np.zeros(data.shape[:3], dtype=bool) mask[40:50, 40:50, 40:50] = True X, y = prep_sfm(gtab, data, mask=mask) valid_targets = np.where(np.isfinite(y))[1] coef, alphas = fracridge(X, y[:, np.unique(valid_targets)], [0.3]) # Shape is n_regressors, n_alphas, n_voxels: assert coef.shape == (362, 1000)
def run_fracridge(X, y, fracs, jit): fracridge(X, y, fracs=fracs, jit=jit)
def test_fracridge_unsorted(nn, pp, bb): X, y, coef_ols, _ = make_data(nn, pp, bb) fracs = np.array([0.1, 0.8, 1.0, 0.2]) # Frac input needs to be sorted: with pytest.raises(ValueError): coef, alpha = fracridge(X, y, fracs=fracs)
def run_fracridge(X, y, fracs): fracridge(X, y, fracs=fracs)
def fit_model(design, data2, tr, hrfmodel, hrfknobs, opt, combinedmatrix, cache=None): """[summary] f, cache = fit_model(design, data2, tr, hrfmodel, hrfknobs, opt, combinedmatrix, cache) if hrfmodel is 'fir', then <f> will be voxels x conditions x time (flattened format) if hrfmodel is 'assume' or 'optimize', then <f> will be {A B} where A is time x 1 and B is voxels x conditions (flattened format). <hrffitvoxels> is [] unless hrfmodel is 'optimize', in which case it will be a column vector of voxel indices. note: cache.rawdesign will exist for 'fir' and 'assume' but not 'optimize'. Args: design ([list]): [description] data2 ([list]): [description] tr ([int]): [description] hrfmodel ([str]): [description] hrfknobs ([array]): [description] opt ([dict]): [description] combinedmatrix ([array]): [description] cache ([dict]): [description] Returns: [tuple]: [f, cache] """ # internal constants minR2 = 99 # in 'optimize' mode, if R^2 between previous HRF and new HRF # is above this threshold (and we have at least gone through # one complete round of fitting (just so that we can change # a little from the initial seed)), then we stop fitting. # init hrffitvoxels = [] # make sure data is np.float32 for p in range(len(data2)): data2[p] = data2[p].astype(np.float32, copy=False) # for p in range(len(design)): # design[p] = design[p].astype(np.float32, copy=False) n_runs = len(design) if cache is None: cache = {} cache['design'] = [None for x in range(n_runs)] cache['rawdesign'] = [None for x in range(n_runs)] if hrfmodel == 'fir': # since 'fir', we can assume design is not the onset case, but check it np.testing.assert_equal(type(design[0]) is int, True) # calc numconditions = design[0].shape[1] # prepare design matrix desw = [] for p in range(len(design)): # expand original design matrix using delta basis functions. # the length of each timecourse is L. desw.append( construct_stim_matrices(design[p].T, 0, hrfknobs, 0).astype(np.float32)) # time x L*conditions # save a record of the raw design matrix cache['rawdesign'][p] = desw[p] # remove polynomials and extra regressors desw[p] = combinedmatrix[p].astype(np.float32) @ desw[p] # time x L*conditions # save a record of the projected-out design matrix cache['design'][p] = desw[p] # fit model if opt['wantfracridge']: f = fracridge( np.concatenate(desw), np.concatenate(data2), opt['frac'], )[0] else: f = mtimes_stack(olsmatrix(np.concatenate(desw)), data2) # L*conditions x voxels # voxels x conditions x L f = np.transpose(np.reshape(f, [hrfknobs + 1, numconditions]), [2, 1, 0]) fout = {} fout['betas'] = f.T.astype(np.float32) fout['hrffitvoxels'] = hrffitvoxels elif hrfmodel == 'assume': # prepare design matrix desopts = {'n_times': data2[0].shape[0], 'tr': tr} desw = [] for p in range(n_runs): # convolve original design matrix with HRF # number of time points desw.append( convolve_design(design[p], hrfknobs, desopts).astype(np.float32)) # save a record of the raw design matrix cache['rawdesign'][p] = desw[p] # remove polynomials and extra regressors desw[p] = combinedmatrix[p].astype(np.float32) @ desw[p] # time x conditions # save a record of the projected-out design matrix cache['design'][p] = desw[p] # fit model if opt['wantfracridge']: f = fracridge(np.concatenate(desw), np.concatenate(data2), opt['frac'])[0] # conditions x voxels else: f = mtimes_stack(olsmatrix(np.concatenate(desw)), data2) # conditions x voxels fout = {} fout['hrfknobs'] = hrfknobs fout['betas'] = f.T.astype(np.float32) fout['hrffitvoxels'] = hrffitvoxels elif hrfmodel == 'optimize': # since 'optimize', we can assume design is not the onset case, # but check it np.testing.assert_true(type(design[0]), list) # calc numinhrf = len(hrfknobs) numconds = design[0].shape[1] numruns = len(design) numconds = design[0].shape[1] postnumlag = numinhrf - 1 if 'design2pre' not in cache: # precompute for speed design2pre = [] for p in range(len(data2)): # expand design matrix using delta functions ntime = design[p].shape[0] # number of time points design2pre.append( construct_stim_matrices(design[p].T, prenumlag=0, postnumlag=postnumlag).reshape( -1, numconds, order='F').astype(np.float32)) # time*L x conditions # record it cache['design2pre'] = design2pre else: if 'design2pre' in cache: design2pre = cache['design2pre'] # collect ntimes per run ntimes = [data2[p].shape[0] for p in range(numruns)] # loop until convergence currenthrf = hrfknobs # initialize cnt = 1 while True: # fix the HRF, estimate the amplitudes if cnt % 2 == 1: # prepare design matrix design2 = [] for p in range(numruns): # get design matrix with HRF # number of time points design2.append( make_design(design[p], tr, ntimes[p], currenthrf)) # project the polynomials out design2[p] = combinedmatrix[p] @ design2[p] # time x conditions # estimate the amplitudes (output: conditions x voxels) currentbeta = mtimes_stack(olsmatrix(np.vstack(design2)), data2) # calculate R^2 modelfit = [(design2[p] @ currentbeta).astype(np.float32) for p in range(numruns)] R2 = calc_cod_stack(data2, modelfit) # figure out indices of good voxels if opt['hrffitmask'] == 1: temp = R2 else: # if people provided a mask for hrf fitting temp = np.zeros((R2.shape)) temp[np.invert(opt['hrffitmask'].ravel())] = -np.inf # shove -Inf in where invalid temp[np.isnan(temp)] = -np.inf ii = np.argsort(temp) nii = len(ii) iichosen = ii[np.max((1, nii - opt['numforhrf'])):nii] iichosen = np.setdiff1d( iichosen, iichosen[temp[iichosen] == -np.inf]).tolist() hrffitvoxels = iichosen # fix the amplitudes, estimate the HRF else: nhrfvox = len(hrffitvoxels) # prepare design matrix design2 = [] for p in range(numruns): # calc # weight and sum based on the current amplitude estimates. # only include the good voxels. # return shape time*L x voxels design2.append( (design2pre[p] @ currentbeta[:, hrffitvoxels]).astype( np.float32)) # remove polynomials and extra regressors # time x L*voxels design2[p] = design2[p].reshape((ntimes[p], -1), order='F') design2[p] = combinedmatrix[p] @ design2[p] design2[p] = design2[p].reshape((ntimes[p], numinhrf, -1), order='F') design2[p] = np.transpose(design2[p], (0, 2, 1)) # estimate the HRF previoushrf = currenthrf datasubset = np.array( np.vstack( [data2[x][:, hrffitvoxels] for x in range(numruns)])) stackdesign = np.vstack(design2) ntime = stackdesign.shape[0] stackdesign = stackdesign.reshape((ntime * nhrfvox, numinhrf), order='F') stackdesign = olsmatrix(stackdesign) currenthrf = np.asarray( stackdesign.dot(datasubset.reshape((-1), order='F')))[0] # if HRF is all zeros (this can happen when the data # are all zeros) # get out prematurely if np.all(currenthrf == 0): print(f'current hrf went all to 0 after {cnt} attempts\n') break # check for convergence hrfR2 = calc_cod(previoushrf, currenthrf, wantmeansub=0) if hrfR2 >= minR2 and cnt > 2: break cnt += 1 # sanity check # we want to see that we're not converging in a weird place # so we compute the coefficient of determination between the # current estimate and the seed hrf hrfR2 = calc_cod(hrfknobs, previoushrf, wantmeansub=0) # sanity check to make sure that we are not doing worse. if hrfR2 < opt['hrfthresh']: print("Global HRF estimate is far from the initial seed," "probably indicating low SNR. We are just going to" "use the initial seed as the HRF estimate.\n") fout = fit_model( design, data2, tr, 'assume', hrfknobs, opt, combinedmatrix, )[0] return fout, cache # normalize results mx = np.max(previoushrf) previoushrf = previoushrf / mx currentbeta = currentbeta * mx # return fout = dict() fout["hrf"] = previoushrf fout['betas'] = currentbeta.T fout['hrffitvoxels'] = hrffitvoxels return fout, cache