def test_u_init(u_init_type): """ Test the u initialization. """ seed = 0 kwargs = _set_up_test(seed) eta = 10.0 n_spatial_maps = 1 n_subjects = 1 X = [kwargs['X']] v = [kwargs['v']] rng = kwargs['rng'] n_atoms = kwargs['n_atoms'] n_voxels = [kwargs['n_voxels']] n_times = [kwargs['n_times']] n_times_atom = kwargs['n_times_atom'] if u_init_type == 'user_defined': u_init_type = [] for n in range(n_subjects): u_init_ = rng.randn(n_atoms, n_voxels[n]) u_init_type.append(u_init_) u_hat = init_u_hat(X, v, rng, u_init_type, eta, n_spatial_maps, n_atoms, n_voxels, n_times, n_times_atom) for n in range(n_subjects): assert u_hat[n].shape == (n_atoms, n_voxels[n])
def test_v_init(hrf_model): """ Test the v initialization. """ seed = 0 kwargs = _set_up_test(seed) constants = dict() delta_init = 1.0 n_subjects = 1 t_r = kwargs['t_r'] n_times_atom = kwargs['n_times_atom'] n_hrf_rois = kwargs['n_hrf_rois'] v_hat, a_hat = init_v_hat(hrf_model, t_r, n_times_atom, n_subjects, n_hrf_rois, constants, delta_init) assert len(v_hat) == n_subjects assert len(a_hat) == n_subjects for n in range(n_subjects): for m in range(n_hrf_rois): assert v_hat[n][m].shape == (n_times_atom, ) if 'hrf_model' == '3_basis_hrf': assert a_hat[n][m].shape == (3, ) elif 'hrf_model' == '2_basis_hrf': assert a_hat[n][m].shape == (2, ) elif 'hrf_model' == 'scaled_hrf': assert a_hat[n][m].shape == (1, )
def test_v_loss(seed): """ Test the loss function. """ rng = check_random_state(seed) kwargs = _set_up_test(seed) X, u, z = kwargs['X'], kwargs['u'], kwargs['z'] t_r, n_times_atom = kwargs['t_r'], kwargs['n_times_atom'] uz = u.T.dot(z) eps = 1.0e-6 a = rng.uniform(MIN_DELTA + eps, MAX_DELTA - eps, 1) def loss_ref(a): n_atoms, _ = z.shape v = scaled_hrf(a, t_r, n_times_atom) X_hat = np.zeros_like(X) for k in range(n_atoms): X_hat += np.outer(u[k, :], np.convolve(v, z[k, :])) residual = (X_hat - X).ravel() return 0.5 * residual.dot(residual) loss_ref_ = loss_ref(a) sum_ztz, sum_ztz_y = _precompute_sum_ztz_sum_ztz_y(uz, X, n_times_atom, factor=2.0) loss_test_ = _loss_v(a, u, z, X, t_r, n_times_atom, sum_ztz, sum_ztz_y) np.testing.assert_allclose(loss_ref_, loss_test_)
def test_precomputed_uvtuv(seed): """ Test the computation of uvtX. """ kwargs = _set_up_test(seed) u, v, rois_idx = kwargs['u'], kwargs['v'], kwargs['rois_idx'] def _precompute_uvtuv_ref(u, v, rois_idx): # computation close to the maths formulation n_atoms, n_voxels = u.shape _, n_times_atom = v.shape uv = np.empty((n_atoms, n_voxels, n_times_atom)) for m in range(rois_idx.shape[0]): indices = get_indices_from_roi(m, rois_idx) for j in indices: for k in range(n_atoms): uv[k, j, :] = u[k, j] * v[m] uvtuv = np.zeros((n_atoms, n_atoms, 2 * n_times_atom - 1)) for k0 in range(n_atoms): for k in range(n_atoms): _sum = np.convolve(uv[k0, 0, ::-1], uv[k, 0, :]) for j in range(1, n_voxels): _sum += np.convolve(uv[k0, j, ::-1], uv[k, j, :]) uvtuv[k0, k, :] = _sum return uvtuv uvtuv_ref = _precompute_uvtuv_ref(u, v, rois_idx) uvtuv = _precompute_uvtuv(u, v, rois_idx) np.testing.assert_allclose(uvtuv_ref, uvtuv)
def test_precompute_d_basis_constant(seed): """ Test the computation of AtA and AtX for the 3 HRF basis. """ kwargs = _set_up_test(seed) t_r, n_times_atom = kwargs['t_r'], kwargs['n_times_atom'] X, u, z = kwargs['X'], kwargs['u'], kwargs['z'] uz = u.T.dot(z) h = hrf_3_basis(t_r, n_times_atom) _, n_times_valid = z.shape n_voxels_rois, n_times = X.shape n_atoms_hrf, _ = h.shape A_d = np.empty((n_voxels_rois, n_times, n_atoms_hrf)) for d in range(n_atoms_hrf): for j in range(n_voxels_rois): A_d[j, :, d] = np.convolve(h[d, :], uz[j, :]) AtX = np.array( [A_d[:, :, d].ravel().dot(X.ravel()) for d in range(n_atoms_hrf)]) AtA = np.empty((n_atoms_hrf, n_atoms_hrf)) for d0 in range(n_atoms_hrf): for d1 in range(n_atoms_hrf): AtA[d0, d1] = A_d[:, :, d0].ravel().dot(A_d[:, :, d1].ravel()) H = np.empty((n_atoms_hrf, n_times, n_times_valid)) for d in range(n_atoms_hrf): H[d, :, :] = make_toeplitz(h[d, :], n_times_valid) AtA_, AtX_ = _precompute_d_basis_constant(X, uz, H) np.testing.assert_allclose(AtA, AtA_) np.testing.assert_allclose(AtX, AtX_)
def test_precompute_B_C(seed): """ Test the computation of B and C for update u. """ kwargs = _set_up_test(seed) X, z, H, v = kwargs['X'], kwargs['z'], kwargs['H'], kwargs['v'] rois_idx = kwargs['rois_idx'] n_hrf_rois, _ = rois_idx.shape def _ref_precompute_B_C(X, z, v, rois_idx): """ Compute list of B, C from givem H (and X, z). """ n_hrf_rois, _ = rois_idx.shape n_voxels, _ = X.shape n_atoms, n_times_valid = z.shape vtvz = np.empty((n_atoms, n_times_valid)) B = np.empty((n_hrf_rois, n_atoms, n_voxels)) C = np.empty((n_hrf_rois, n_atoms, n_atoms)) for m in range(n_hrf_rois): indices = get_indices_from_roi(m, rois_idx) for k in range(n_atoms): vz_k = np.convolve(v[m, :], z[k, :]) vtvz[k, :] = np.convolve(v[m, ::-1], vz_k, mode='valid') zvtvz = vtvz.dot(z.T) C[m, :, :] = zvtvz for j in indices: vtX = np.convolve(v[m, ::-1], X[j, :], mode='valid') B[m, :, j] = z.dot(vtX.T) return B, C B, C = _precompute_B_C(X, z, H, rois_idx) ref_B, ref_C = _ref_precompute_B_C(X, z, v, rois_idx) for m in range(n_hrf_rois): indices = get_indices_from_roi(m, rois_idx) np.testing.assert_allclose(ref_B[m, :, indices], B[m, :, indices]) np.testing.assert_allclose(ref_C, C)
def test_grad_v_scaled_hrf(seed): """ Test the gradient of v (model: scaled hrf). """ rng = check_random_state(seed) kwargs = _set_up_test(seed) t_r, n_times_atom = kwargs['t_r'], kwargs['n_times_atom'] X, u, z = kwargs['X'], kwargs['u'], kwargs['z'] uz = u.T.dot(z) epsilon = 1.0e-6 a = rng.uniform(MIN_DELTA + epsilon, MAX_DELTA - epsilon, 1) # Finite grad v def finite_grad_v(a): def f(a): return _loss_v(a, u, z, X, t_r, n_times_atom) return (f(a + epsilon) - f(a)) / epsilon grad_ref = finite_grad_v(a) # Closed form grad v sum_ztz, sum_ztz_y = _precompute_sum_ztz_sum_ztz_y(uz, X, n_times_atom, factor=1.0) grad_ = _grad_v_scaled_hrf(a, t_r, n_times_atom, sum_ztz, sum_ztz_y) np.testing.assert_allclose(grad_ref, grad_, rtol=1e-2)
def test_grad_z(seed): """ Test the gradient of z. """ kwargs = _set_up_test(seed) n_atoms, n_times_valid = kwargs['n_atoms'], kwargs['n_times_valid'] X, u, z, v, = kwargs['X'], kwargs['u'], kwargs['z'], kwargs['v'] H = kwargs['H'] rois_idx = kwargs['rois_idx'] uvtuv = _precompute_uvtuv(u=u, v=v, rois_idx=rois_idx) uvtX = adjconv_uH(X, u=u, H=H, rois_idx=rois_idx) # Finite grad z def finite_grad_z(z): def f(z): z = z.reshape((n_atoms, n_times_valid)) return _obj(X, _prox_positive_l2_ball, u, z, rois_idx, H=H, valid=False, return_reg=False, lbda=None) grad_ = approx_fprime(xk=z.ravel(), f=f, epsilon=1.0e-6) return grad_.reshape((n_atoms, n_times_valid)) grad_ref = finite_grad_z(z) # Closed form grad z grad_ = _grad_z(z, uvtuv=uvtuv, uvtX=uvtX) np.testing.assert_allclose(grad_ref, grad_, rtol=1e-5, atol=1e-3)
def test_construct_X_hat(seed): """ Test the X construction functions. """ kwargs = _set_up_test(seed) u, z, v, H = kwargs['u'], kwargs['z'], kwargs['v'], kwargs['H'] rois_idx = kwargs['rois_idx'] X_hat = construct_X_hat_from_v(v, z, u, rois_idx) X_hat_ = construct_X_hat_from_H(H, z, u, rois_idx) np.testing.assert_allclose(X_hat, X_hat_)
def test_get_lambda_max(seed): """ Test the lambda max estimation. """ kwargs = _set_up_test(seed) z, u, H, v = kwargs['z'], kwargs['u'], kwargs['H'], kwargs['v'] rois_idx, X = kwargs['rois_idx'], kwargs['X'] lbda_max = _get_lambda_max(X, u, H, rois_idx) constants = dict(H=H, v=v, u=u, rois_idx=rois_idx, X=X, lbda=lbda_max, prox_z='tv', rho=2.0) z_hat = _update_z(z, constants) assert np.linalg.norm(z_hat) / np.linalg.norm(z) < 0.05
def test_adjconv_D(seed): """ Test the computation of uvtX and uHtX. """ kwargs = _set_up_test(seed) u, v, rois_idx = kwargs['u'], kwargs['v'], kwargs['rois_idx'] residual_i, H = kwargs['X'], kwargs['H'], uvtX = adjconv_uv(residual_i, u, v, rois_idx) uvtX_ = adjconv_uH(residual_i, u, H, rois_idx) # all HRFs / Toep. matrices are associated and all HRFs are equal, so we # only take the first: H[0, :, :] uvtX_ref = u.dot(residual_i).dot(H[0, :, :]) np.testing.assert_allclose(uvtX_ref, uvtX) np.testing.assert_allclose(uvtX_ref, uvtX_)
def test_get_lambda_max(seed): """ Test the lambda max estimation. """ kwargs = _set_up_test(seed) z, u, H, v = kwargs['z'], kwargs['u'], kwargs['H'], kwargs['v'] rois_idx, X = kwargs['rois_idx'], kwargs['X'] lbda_max = _get_lambda_max(X, u, H, rois_idx) constants = dict(H=H, v=v, u=u, rois_idx=rois_idx, X=X, lbda=lbda_max, prox_z='tv', rho=2.0) z_hat = _update_z(z, constants) assert np.sum(np.abs(np.diff(z_hat, axis=1))) < 1e-6
def test_prox_l1_simplex(seed): """ Test the positive L1 simplex proximal operator. """ rng = check_random_state(seed) kwargs = _set_up_test(seed) u = kwargs['u'] u_0 = u[0, :] prox_u_0 = _prox_l1_simplex(u_0, 10.0) assert np.all(prox_u_0 >= 0.0) np.testing.assert_allclose(np.sum(np.abs(prox_u_0)), 10.0) n_try = 100 for _ in range(n_try): x = rng.randn(*u_0.shape) x[x < 0.0] = 0.0 norm_x = np.sum(np.abs(x)) if not (norm_x != 10.0): x /= norm_x assert np.linalg.norm(u_0 - prox_u_0) < np.linalg.norm(u_0 - x)
def test_init_z(z_init): """ Test the z initialization. """ seed = 0 kwargs = _set_up_test(seed) n_subjects = 1 rng = kwargs['rng'] n_atoms = kwargs['n_atoms'] n_times_valid = [kwargs['n_times_valid']] if z_init == 'user_defined': z_init = [] for n in range(n_subjects): u_init_ = rng.randn(n_atoms, n_times_valid[n]) z_init.append(u_init_) z_hat = init_z_hat(z_init, n_subjects, n_atoms, n_times_valid) for n in range(n_subjects): assert z_hat[n].shape == (n_atoms, n_times_valid[n])
def test_prox_positive_L2_ball(seed): """ Test the positive L2 ball proximal operator. """ rng = check_random_state(seed) kwargs = _set_up_test(seed) u = kwargs['u'] u_0 = u[0, :] prox_u_0 = _prox_positive_l2_ball(u_0, 1.0) assert np.all(prox_u_0 >= 0.0) assert np.linalg.norm(prox_u_0) <= 1.0 n_try = 100 for _ in range(n_try): x = rng.randn(*u_0.shape) x[x < 0.0] = 0.0 norm_x = x.ravel().dot(x.ravel()) if not (norm_x <= 1.0): x /= norm_x assert np.linalg.norm(u_0 - prox_u_0) < np.linalg.norm(u_0 - x)
def test_cdclinmodel(seed): """ Test the coordinate descente on constraint linear model algo. """ kwargs = _set_up_test(seed) u, C, B = kwargs['u'], kwargs['C'], kwargs['B'] rois_idx = kwargs['rois_idx'] X, z, v = kwargs['X'], kwargs['z'], kwargs['v'] def _prox(u_k): return _prox_positive_l2_ball(u_k, 1.0) constants = dict(C=C, B=B, rois_idx=rois_idx, prox_u=_prox) def obj(u): return _obj(X=X, prox=_prox_positive_l2_ball, u=u, z=z, rois_idx=rois_idx, v=v, valid=False, return_reg=False, lbda=None) u_hat, pobj, _ = cdclinmodel(u, constants=constants, obj=obj, benchmark=True, max_iter=50) assert pobj[0] > pobj[-1]
def test_compute_uvtuv_z(seed): """ Test the computation of uvtuv_z. """ kwargs = _set_up_test(seed) z, u, v = kwargs['z'], kwargs['u'], kwargs['v'] rois_idx, n_voxels = kwargs['rois_idx'], kwargs['n_voxels'] n_hrf_rois, _ = rois_idx.shape _, n_times_valid = z.shape uz = z.T.dot(u).T vtuv_z = np.empty((n_voxels, n_times_valid)) for m in range(n_hrf_rois): indices = get_indices_from_roi(m, rois_idx) for j in indices: uv_z_j = np.convolve(v[m, :], uz[j, :]) vtuv_z[j, :] = np.convolve(v[m, ::-1], uv_z_j, mode='valid') uvtuv_z_ref = u.dot(vtuv_z) uvtuv = _precompute_uvtuv(u, v, rois_idx) uvtuv_z = _compute_uvtuv_z(z, uvtuv) np.testing.assert_allclose(uvtuv_z_ref, uvtuv_z)
def test_grad_v_hrf_d_basis(seed): """ Test the gradient of v (model: hrf 3 basis). """ kwargs = _set_up_test(seed) t_r, n_times_atom = kwargs['t_r'], kwargs['n_times_atom'] X, u, z = kwargs['X'], kwargs['u'], kwargs['z'] h = hrf_3_basis(t_r, n_times_atom) uz = u.T.dot(z) n_voxels_rois, n_times_valid = uz.shape n_atoms_hrf, _ = h.shape _, n_times = X.shape A_d = np.empty((n_voxels_rois, n_times, n_atoms_hrf)) for d in range(n_atoms_hrf): for j in range(n_voxels_rois): A_d[j, :, d] = np.convolve(h[d, :], uz[j, :]) AtX = np.array( [A_d[:, :, d].ravel().dot(X.ravel()) for d in range(n_atoms_hrf)]) AtA = np.empty((n_atoms_hrf, n_atoms_hrf)) for d0 in range(n_atoms_hrf): for d1 in range(n_atoms_hrf): AtA[d0, d1] = A_d[:, :, d0].ravel().dot(A_d[:, :, d1].ravel()) a = np.array([1.0, 0.5, 0.3]) # Finite grad v def finite_grad_v(a): def f(a): X_hat = a[0] * A_d[:, :, 0] for d in range(1, n_atoms_hrf): X_hat += a[d] * A_d[:, :, d] res = (X - X_hat).ravel() return 0.5 * res.dot(res) return approx_fprime(xk=a, f=f, epsilon=1.0e-6) grad_ref = finite_grad_v(a) # Closed form grad v grad_ = _grad_v_hrf_d_basis(a, AtA, AtX) np.testing.assert_allclose(grad_ref, grad_, rtol=1e-5, atol=1e-3)
def test_global_loss(seed): """ Test the loss function. """ kwargs = _set_up_test(seed) X, u, z = kwargs['X'], kwargs['u'], kwargs['z'] v, H = kwargs['v'], kwargs['H'] rois_idx = kwargs['rois_idx'] def loss_ref(X, u, z, v, rois_idx): res = (X - construct_X_hat_from_v(v, z, u, rois_idx)).ravel() return 0.5 * res.dot(res) loss_ref_ = loss_ref(X, u, z, v, rois_idx) loss_test_ = _obj(X, _prox_positive_l2_ball, u, z, rois_idx, H=H, valid=False, return_reg=False, lbda=None) np.testing.assert_allclose(loss_ref_, loss_test_)
def test_grad_u(seed): """ Test the gradient of u. """ kwargs = _set_up_test(seed) n_atoms, n_voxels = kwargs['n_atoms'], kwargs['n_voxels'] X, H, u, z = kwargs['X'], kwargs['H'], kwargs['u'], kwargs['z'] rois_idx = kwargs['rois_idx'] B, C = kwargs['B'], kwargs['C'] # Finite difference with one HRF def finite_grad_one_hrfs_(u): def f(u): u = u.reshape((n_atoms, n_voxels)) return _obj(X, _prox_positive_l2_ball, u, z, rois_idx, H=H, valid=False, return_reg=False, lbda=None) grad_ = approx_fprime(xk=u.ravel(), f=f, epsilon=1.0e-6) return grad_.reshape((n_atoms, n_voxels)) finite_grad_one_hrfs = finite_grad_one_hrfs_(u) # Multiple (identical) HRFs grad_multi_hrfs = np.empty((n_atoms, n_voxels)) for k in range(n_atoms): grad_multi_hrfs[k, :] = _grad_u_k(u, B, C, k, rois_idx) np.testing.assert_allclose(finite_grad_one_hrfs, grad_multi_hrfs, rtol=1e-5, atol=1e-3)