static void unisoftthresh_apply(const operator_data_t* _data, float mu, complex float* dst, const complex float* src) { const auto data = CAST_DOWN(thresh_s, _data); if (0. == mu) { md_copy(data->D, data->dim, dst, src, CFL_SIZE); } else { const long* transform_dims = linop_codomain(data->unitary_op)->dims; const long* transform_strs = linop_codomain(data->unitary_op)->strs; complex float* tmp = md_alloc_sameplace(data->D, transform_dims, CFL_SIZE, dst); linop_forward(data->unitary_op, data->D, transform_dims, tmp, data->D, data->dim, src); complex float* tmp_norm = md_alloc_sameplace(data->D, data->norm_dim, CFL_SIZE, dst); md_zsoftthresh_core2(data->D, transform_dims, data->lambda * mu, data->flags, tmp_norm, transform_strs, tmp, transform_strs, tmp); md_free(tmp_norm); linop_adjoint(data->unitary_op, data->D, data->dim, dst, data->D, transform_dims, tmp); md_free(tmp); } }
/** * Proximal operator for l1-norm with unitary transform: f(x) = lambda || T x ||_1 * * @param D number of dimensions * @param dim dimensions of x * @param lambda threshold parameter * @param unitary_op unitary linear operator * @param flags bitmask for joint soft-thresholding */ extern const struct operator_p_s* prox_unithresh_create(unsigned int D, const struct linop_s* unitary_op, const float lambda, const unsigned long flags) { PTR_ALLOC(struct thresh_s, data); SET_TYPEID(thresh_s, data); data->lambda = lambda; data->D = D; data->flags = flags; data->unitary_op = unitary_op; const long* dims = linop_domain(unitary_op)->dims; PTR_ALLOC(long[D], ndim); md_copy_dims(D, *ndim, dims); data->dim = *PTR_PASS(ndim); PTR_ALLOC(long[D], nstr); md_calc_strides(D, *nstr, data->dim, CFL_SIZE); data->str = *PTR_PASS(nstr); // norm dimensions are the flagged transform dimensions // FIXME should use linop_codomain(unitary_op)->N PTR_ALLOC(long[D], norm_dim); md_select_dims(D, ~flags, *norm_dim, linop_codomain(unitary_op)->dims); data->norm_dim = *PTR_PASS(norm_dim); return operator_p_create(D, dims, D, dims, CAST_UP(PTR_PASS(data)), unisoftthresh_apply, thresh_del); }
/** * Efficiently chain two matrix linops by multiplying the actual matrices together. * Stores a copy of the new matrix. * Returns: C = B A * * @param a first matrix (applied to input) * @param b second matrix (applied to output of first matrix) */ struct linop_s* linop_matrix_chain(const struct linop_s* a, const struct linop_s* b) { const struct operator_matrix_s* a_data = linop_get_data(a); const struct operator_matrix_s* b_data = linop_get_data(b); // check compatibility assert(linop_codomain(a)->N == linop_domain(b)->N); assert(md_calc_size(linop_codomain(a)->N, linop_codomain(a)->dims) == md_calc_size(linop_domain(b)->N, linop_domain(b)->dims)); assert(a_data->K_dim != b_data->T_dim); // FIXME error for now -- need to deal with this specially. assert((a_data->T_dim == b_data->K_dim) && (a_data->T == b_data->K)); unsigned int N = linop_domain(a)->N; long max_dims[N]; md_singleton_dims(N, max_dims); max_dims[a_data->T_dim] = a_data->T; max_dims[a_data->K_dim] = a_data->K; max_dims[b_data->T_dim] = b_data->T; long matrix_dims[N]; long matrix_strs[N]; md_select_dims(N, ~MD_BIT(a_data->T_dim), matrix_dims, max_dims); md_calc_strides(N, matrix_strs, matrix_dims, CFL_SIZE); complex float* matrix = md_alloc_sameplace(N, matrix_dims, CFL_SIZE, a_data->mat); md_clear(N, matrix_dims, matrix, CFL_SIZE); md_zfmac2(N, max_dims, matrix_strs, matrix, a_data->mat_iovec->strs, a_data->mat, b_data->mat_iovec->strs, b_data->mat); struct linop_s* c = linop_matrix_create(N, linop_codomain(b)->dims, linop_domain(a)->dims, matrix_dims, matrix); md_free(matrix); return c; }
const struct operator_p_s* prox_lineq_create(const struct linop_s* op, const complex float* y) { PTR_ALLOC(struct prox_lineq_data, pdata); unsigned int N = linop_domain(op)->N; const long* dims = linop_domain(op)->dims; pdata->op = op; pdata->adj = md_alloc_sameplace(N, dims, CFL_SIZE, y); linop_adjoint(op, N, dims, pdata->adj, N, linop_codomain(op)->dims, y); pdata->tmp = md_alloc_sameplace(N, dims, CFL_SIZE, y); return operator_p_create(N, dims, N, dims, CAST_UP(PTR_PASS(pdata)), prox_lineq_apply, prox_lineq_del); }
/* Chambolle Pock Primal Dual algorithm. Solves G(x) + F(Ax) * Assumes that G is in prox_ops[0], F is in prox_ops[1], A is in ops[1] */ void iter2_chambolle_pock(iter_conf* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s* prox_ops[D], const struct linop_s* ops[D], const float* biases[D], const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, struct iter_monitor_s* monitor) { assert(D == 2); assert(NULL == biases); assert(NULL == normaleq_op); UNUSED(xupdate_op); UNUSED(image_adj); auto conf = CAST_DOWN(iter_chambolle_pock_conf, _conf); const struct iovec_s* iv = linop_domain(ops[1]); const struct iovec_s* ov = linop_codomain(ops[1]); assert((long)md_calc_size(iv->N, iv->dims) * 2 == size); // FIXME: sensible way to check for corrupt data? #if 0 float eps = md_norm(1, MD_DIMS(size), image_adj); if (checkeps(eps)) goto cleanup; #else float eps = 1.; #endif chambolle_pock(conf->maxiter, eps * conf->tol, conf->tau, conf->sigma, conf->theta, conf->decay, 2 * md_calc_size(iv->N, iv->dims), 2 * md_calc_size(ov->N, ov->dims), select_vecops(image), OPERATOR2ITOP(ops[1]->forward), OPERATOR2ITOP(ops[1]->adjoint), OPERATOR_P2ITOP(prox_ops[1]), OPERATOR_P2ITOP(prox_ops[0]), image, monitor); //cleanup: //; }
int main_bpsense(int argc, char* argv[]) { // ----------------------------------------------------------- // set up conf and option parser struct bpsense_conf conf = bpsense_defaults; struct iter_admm_conf iconf = iter_admm_defaults; conf.iconf = &iconf; conf.iconf->rho = 10; // more sensibile default bool usegpu = false; const char* psf = NULL; const char* image_truth_fname = NULL; bool im_truth = false; bool use_tvnorm = false; double start_time = timestamp(); const struct opt_s opts[] = { OPT_FLOAT('e', &conf.eps, "eps", "data consistency error"), OPT_FLOAT('r', &conf.lambda, "lambda", "l2 regularization parameter"), OPT_FLOAT('u', &conf.iconf->rho, "rho", "ADMM penalty parameter"), OPT_SET('c', &conf.rvc, "real-value constraint"), OPT_SET('t', &use_tvnorm, "use TV norm"), OPT_STRING('T', &image_truth_fname, "file", "compare to truth image"), OPT_UINT('i', &conf.iconf->maxiter, "iter", "max. iterations"), OPT_SET('g', &usegpu, "(use gpu)"), OPT_STRING('p', &psf, "file", "point-spread function"), }; cmdline(&argc, argv, 3, 3, usage_str, help_str, ARRAY_SIZE(opts), opts); if (NULL != image_truth_fname) im_truth = true; // ----------------------------------------------------------- // load data and print some info about the recon int N = DIMS; long dims[N]; long dims1[N]; long img_dims[N]; long ksp_dims[N]; complex float* kspace_data = load_cfl(argv[1], N, ksp_dims); complex float* sens_maps = load_cfl(argv[2], N, dims); for (int i = 0; i < 4; i++) // sizes2[4] may be > 1 if (ksp_dims[i] != dims[i]) error("Dimensions of kspace and sensitivities do not match!\n"); assert(1 == ksp_dims[MAPS_DIM]); (usegpu ? num_init_gpu : num_init)(); if (dims[MAPS_DIM] > 1) debug_printf(DP_INFO, "%ld maps.\nESPIRiT reconstruction.\n", dims[4]); if (conf.lambda > 0.) debug_printf(DP_INFO, "l2 regularization: %f\n", conf.lambda); if (use_tvnorm) debug_printf(DP_INFO, "use Total Variation\n"); else debug_printf(DP_INFO, "use Wavelets\n"); if (im_truth) debug_printf(DP_INFO, "Compare to truth\n"); md_select_dims(N, ~(COIL_FLAG | MAPS_FLAG), dims1, dims); md_select_dims(N, ~COIL_FLAG, img_dims, dims); // ----------------------------------------------------------- // initialize sampling pattern complex float* pattern = NULL; long pat_dims[N]; if (NULL != psf) { pattern = load_cfl(psf, N, pat_dims); // FIXME: check compatibility } else { pattern = md_alloc(N, dims1, CFL_SIZE); estimate_pattern(N, ksp_dims, COIL_DIM, pattern, kspace_data); } // ----------------------------------------------------------- // print some statistics size_t T = md_calc_size(N, dims1); long samples = (long)pow(md_znorm(N, dims1, pattern), 2.); debug_printf(DP_INFO, "Size: %ld Samples: %ld Acc: %.2f\n", T, samples, (float)T/(float)samples); // ----------------------------------------------------------- // fftmod to un-center data fftmod(N, ksp_dims, FFT_FLAGS, kspace_data, kspace_data); fftmod(N, dims, FFT_FLAGS, sens_maps, sens_maps); // ----------------------------------------------------------- // apply scaling float scaling = estimate_scaling(ksp_dims, NULL, kspace_data); debug_printf(DP_INFO, "Scaling: %f\n", scaling); if (scaling != 0.) md_zsmul(N, ksp_dims, kspace_data, kspace_data, 1. / scaling); // ----------------------------------------------------------- // create l1 prox operator and transform long minsize[DIMS] = { [0 ... DIMS - 1] = 1 }; minsize[0] = MIN(img_dims[0], 16); minsize[1] = MIN(img_dims[1], 16); minsize[2] = MIN(img_dims[2], 16); const struct linop_s* l1op = NULL; const struct operator_p_s* l1prox = NULL; if (use_tvnorm) { l1op = grad_init(DIMS, img_dims, FFT_FLAGS); l1prox = prox_thresh_create(DIMS + 1, linop_codomain(l1op)->dims, 1., 0u, usegpu); conf.l1op_obj = l1op; } else { bool randshift = true; l1op = linop_identity_create(DIMS, img_dims); conf.l1op_obj = wavelet_create(DIMS, img_dims, FFT_FLAGS, minsize, false, usegpu); l1prox = prox_wavethresh_create(DIMS, img_dims, FFT_FLAGS, minsize, 1., randshift, usegpu); } // ----------------------------------------------------------- // create image and load truth image complex float* image = create_cfl(argv[3], N, img_dims); md_clear(N, img_dims, image, CFL_SIZE); long img_truth_dims[DIMS]; complex float* image_truth = NULL; if (im_truth) image_truth = load_cfl(image_truth_fname, DIMS, img_truth_dims); // ----------------------------------------------------------- // call recon if (usegpu) #ifdef USE_CUDA bpsense_recon_gpu(&conf, dims, image, sens_maps, dims1, pattern, l1op, l1prox, ksp_dims, kspace_data, image_truth); #else assert(0); #endif else
/** * Efficiently chain two matrix linops by multiplying the actual matrices together. * Stores a copy of the new matrix. * Returns: C = B A * * @param a first matrix (applied to input) * @param b second matrix (applied to output of first matrix) */ struct linop_s* linop_matrix_chain(const struct linop_s* a, const struct linop_s* b) { const struct operator_matrix_s* a_data = CAST_DOWN(operator_matrix_s, linop_get_data(a)); const struct operator_matrix_s* b_data = CAST_DOWN(operator_matrix_s, linop_get_data(b)); // check compatibility assert(linop_codomain(a)->N == linop_domain(b)->N); assert(md_check_compat(linop_codomain(a)->N, 0u, linop_codomain(a)->dims, linop_domain(b)->dims)); unsigned int D = linop_domain(a)->N; unsigned long outB_flags = md_nontriv_dims(D, linop_codomain(b)->dims); unsigned long inB_flags = md_nontriv_dims(D, linop_domain(b)->dims); unsigned long delB_flags = inB_flags & ~outB_flags; unsigned int N = a_data->N; assert(N == 2 * D); long in_dims[N]; md_copy_dims(N, in_dims, a_data->in_dims); long matA_dims[N]; md_copy_dims(N, matA_dims, a_data->mat_dims); long matB_dims[N]; md_copy_dims(N, matB_dims, b_data->mat_dims); long out_dims[N]; md_copy_dims(N, out_dims, b_data->out_dims); for (unsigned int i = 0; i < D; i++) { if (MD_IS_SET(delB_flags, i)) { matA_dims[2 * i + 0] = a_data->mat_dims[2 * i + 1]; matA_dims[2 * i + 1] = a_data->mat_dims[2 * i + 0]; in_dims[2 * i + 0] = a_data->in_dims[2 * i + 1]; in_dims[2 * i + 1] = a_data->in_dims[2 * i + 0]; } } long matrix_dims[N]; md_singleton_dims(N, matrix_dims); unsigned long iflags = md_nontriv_dims(N, in_dims); unsigned long oflags = md_nontriv_dims(N, out_dims); unsigned long flags = iflags | oflags; // we combine a and b and sum over dims not in input or output md_max_dims(N, flags, matrix_dims, matA_dims, matB_dims); debug_printf(DP_DEBUG1, "tensor chain: %ld x %ld -> %ld\n", md_calc_size(N, matA_dims), md_calc_size(N, matB_dims), md_calc_size(N, matrix_dims)); complex float* matrix = md_alloc(N, matrix_dims, CFL_SIZE); debug_print_dims(DP_DEBUG2, N, matrix_dims); debug_print_dims(DP_DEBUG2, N, in_dims); debug_print_dims(DP_DEBUG2, N, matA_dims); debug_print_dims(DP_DEBUG2, N, matB_dims); debug_print_dims(DP_DEBUG2, N, out_dims); md_ztenmul(N, matrix_dims, matrix, matA_dims, a_data->mat, matB_dims, b_data->mat); // priv2 takes our doubled dimensions struct operator_matrix_s* data = linop_matrix_priv2(N, out_dims, in_dims, matrix_dims, matrix); /* although we internally use different dimensions we define the * correct interface */ struct linop_s* c = linop_create(linop_codomain(b)->N, linop_codomain(b)->dims, linop_domain(a)->N, linop_domain(a)->dims, CAST_UP(data), linop_matrix_apply, linop_matrix_apply_adjoint, linop_matrix_apply_normal, NULL, linop_matrix_del); md_free(matrix); return c; }
void opt_reg_configure(unsigned int N, const long img_dims[N], struct opt_reg_s* ropts, const struct operator_p_s* prox_ops[NUM_REGS], const struct linop_s* trafos[NUM_REGS], unsigned int llr_blk, bool randshift, bool use_gpu) { float lambda = ropts->lambda; if (-1. == lambda) lambda = 0.; // if no penalities specified but regularization // parameter is given, add a l2 penalty struct reg_s* regs = ropts->regs; if ((0 == ropts->r) && (lambda > 0.)) { regs[0].xform = L2IMG; regs[0].xflags = 0u; regs[0].jflags = 0u; regs[0].lambda = lambda; ropts->r = 1; } int nr_penalties = ropts->r; long blkdims[MAX_LEV][DIMS]; int levels; for (int nr = 0; nr < nr_penalties; nr++) { // fix up regularization parameter if (-1. == regs[nr].lambda) regs[nr].lambda = lambda; switch (regs[nr].xform) { case L1WAV: debug_printf(DP_INFO, "l1-wavelet regularization: %f\n", regs[nr].lambda); if (0 != regs[nr].jflags) debug_printf(DP_WARN, "joint l1-wavelet thresholding not currently supported.\n"); long minsize[DIMS] = { [0 ... DIMS - 1] = 1 }; minsize[0] = MIN(img_dims[0], 16); minsize[1] = MIN(img_dims[1], 16); minsize[2] = MIN(img_dims[2], 16); unsigned int wflags = 0; for (unsigned int i = 0; i < DIMS; i++) { if ((1 < img_dims[i]) && MD_IS_SET(regs[nr].xflags, i)) { wflags = MD_SET(wflags, i); minsize[i] = MIN(img_dims[i], 16); } } trafos[nr] = linop_identity_create(DIMS, img_dims); prox_ops[nr] = prox_wavelet3_thresh_create(DIMS, img_dims, wflags, minsize, regs[nr].lambda, randshift); break; case TV: debug_printf(DP_INFO, "TV regularization: %f\n", regs[nr].lambda); trafos[nr] = linop_grad_create(DIMS, img_dims, regs[nr].xflags); prox_ops[nr] = prox_thresh_create(DIMS + 1, linop_codomain(trafos[nr])->dims, regs[nr].lambda, regs[nr].jflags | MD_BIT(DIMS), use_gpu); break; case LLR: debug_printf(DP_INFO, "lowrank regularization: %f\n", regs[nr].lambda); // add locally lowrank penalty levels = llr_blkdims(blkdims, regs[nr].jflags, img_dims, llr_blk); assert(1 == levels); assert(levels == img_dims[LEVEL_DIM]); for(int l = 0; l < levels; l++) #if 0 blkdims[l][MAPS_DIM] = img_dims[MAPS_DIM]; #else blkdims[l][MAPS_DIM] = 1; #endif int remove_mean = 0; trafos[nr] = linop_identity_create(DIMS, img_dims); prox_ops[nr] = lrthresh_create(img_dims, randshift, regs[nr].xflags, (const long (*)[DIMS])blkdims, regs[nr].lambda, false, remove_mean, use_gpu); break; case MLR: #if 0 // FIXME: multiscale low rank changes the output image dimensions // and requires the forward linear operator. This should be decoupled... debug_printf(DP_INFO, "multi-scale lowrank regularization: %f\n", regs[nr].lambda); levels = multilr_blkdims(blkdims, regs[nr].jflags, img_dims, 8, 1); img_dims[LEVEL_DIM] = levels; max_dims[LEVEL_DIM] = levels; for(int l = 0; l < levels; l++) blkdims[l][MAPS_DIM] = 1; trafos[nr] = linop_identity_create(DIMS, img_dims); prox_ops[nr] = lrthresh_create(img_dims, randshift, regs[nr].xflags, (const long (*)[DIMS])blkdims, regs[nr].lambda, false, 0, use_gpu); const struct linop_s* decom_op = sum_create( img_dims, use_gpu ); const struct linop_s* tmp_op = forward_op; forward_op = linop_chain(decom_op, forward_op); linop_free(decom_op); linop_free(tmp_op); #else debug_printf(DP_WARN, "multi-scale lowrank regularization not yet supported: %f\n", regs[nr].lambda); #endif break; case IMAGL1: debug_printf(DP_INFO, "l1 regularization of imaginary part: %f\n", regs[nr].lambda); trafos[nr] = linop_rdiag_create(DIMS, img_dims, 0, &(complex float){ 1.i }); prox_ops[nr] = prox_thresh_create(DIMS, img_dims, regs[nr].lambda, regs[nr].jflags, use_gpu); break; case IMAGL2: debug_printf(DP_INFO, "l2 regularization of imaginary part: %f\n", regs[nr].lambda); trafos[nr] = linop_rdiag_create(DIMS, img_dims, 0, &(complex float){ 1.i }); prox_ops[nr] = prox_leastsquares_create(DIMS, img_dims, regs[nr].lambda, NULL); break; case L1IMG: debug_printf(DP_INFO, "l1 regularization: %f\n", regs[nr].lambda); trafos[nr] = linop_identity_create(DIMS, img_dims); prox_ops[nr] = prox_thresh_create(DIMS, img_dims, regs[nr].lambda, regs[nr].jflags, use_gpu); break; case L2IMG: debug_printf(DP_INFO, "l2 regularization: %f\n", regs[nr].lambda); trafos[nr] = linop_identity_create(DIMS, img_dims); prox_ops[nr] = prox_leastsquares_create(DIMS, img_dims, regs[nr].lambda, NULL); break; case FTL1: debug_printf(DP_INFO, "l1 regularization of Fourier transform: %f\n", regs[nr].lambda); trafos[nr] = linop_fft_create(DIMS, img_dims, regs[nr].xflags); prox_ops[nr] = prox_thresh_create(DIMS, img_dims, regs[nr].lambda, regs[nr].jflags, use_gpu); break; }
void iter2_admm(void* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s** prox_ops, const struct linop_s** ops, const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, const float* image_truth, void* obj_eval_data, float (*obj_eval)(const void*, const float*)) { struct iter_admm_conf* conf = _conf; struct admm_plan_s admm_plan = { .maxiter = conf->maxiter, .maxitercg = conf->maxitercg, .rho = conf->rho, .image_truth = image_truth, .num_funs = D, .do_warmstart = conf->do_warmstart, .dynamic_rho = conf->dynamic_rho, .hogwild = conf->hogwild, .ABSTOL = conf->ABSTOL, .RELTOL = conf->RELTOL, .alpha = conf->alpha, .tau = conf->tau, .mu = conf->mu, .fast = conf->fast, }; struct admm_op a_ops[D]; struct admm_prox_op a_prox_ops[D]; for (unsigned int i = 0; i < D; i++) { a_ops[i].forward = linop_forward_iter; a_ops[i].normal = linop_normal_iter; a_ops[i].adjoint = linop_adjoint_iter; a_ops[i].data = (void*)ops[i]; a_prox_ops[i].prox_fun = operator_p_iter; a_prox_ops[i].data = (void*)prox_ops[i]; } admm_plan.ops = a_ops; admm_plan.prox_ops = a_prox_ops; admm_plan.xupdate_fun = operator_p_iter; admm_plan.xupdate_data = (void*)xupdate_op; struct admm_history_s admm_history; long z_dims[D]; for (unsigned int i = 0; i < D; i++) z_dims[i] = 2 * md_calc_size(linop_codomain(ops[i])->N, linop_codomain(ops[i])->dims); if (NULL != image_adj) { float eps = md_norm(1, MD_DIMS(size), image_adj); if (checkeps(eps)) goto cleanup; } admm(&admm_history, &admm_plan, admm_plan.num_funs, z_dims, size, (float*)image, image_adj, select_vecops(image), operator_iter, (void*)normaleq_op, obj_eval_data, obj_eval); cleanup: ; } void iter2_pocs(void* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s** prox_ops, const struct linop_s** ops, const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, const float* image_truth, void* obj_eval_data, float (*obj_eval)(const void*, const float*)) { const struct iter_pocs_conf* conf = _conf; assert(NULL == normaleq_op); assert(NULL == ops); assert(NULL == image_adj); UNUSED(xupdate_op); UNUSED(image_adj); struct pocs_proj_op proj_ops[D]; for (unsigned int i = 0; i < D; i++) { proj_ops[i].proj_fun = operator_p_iter; proj_ops[i].data = (void*)prox_ops[i]; } pocs(conf->maxiter, D, proj_ops, select_vecops(image), size, image, image_truth, obj_eval_data, obj_eval); } void iter2_call_iter(void* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s** prox_ops, const struct linop_s** ops, const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, const float* image_truth, void* obj_eval_data, float (*obj_eval)(const void*, const float*)) { assert(D <= 1); assert(NULL == ops); UNUSED(xupdate_op); struct iter_call_s* it = _conf; it->fun(it->_conf, normaleq_op, (1 == D) ? prox_ops[0] : NULL, size, image, image_adj, image_truth, obj_eval_data, obj_eval); }
void iter2_niht(iter_conf* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s* prox_ops[D], const struct linop_s* ops[D], const float* biases[D], const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, struct iter_monitor_s* monitor) { UNUSED(xupdate_op); UNUSED(biases); assert(D == 1); auto conf = CAST_DOWN(iter_niht_conf, _conf); struct niht_conf_s niht_conf = { .maxiter = conf->maxiter, .N = size, .trans = 0, .do_warmstart = conf->do_warmstart, }; struct niht_transop trans; if (NULL != ops) { trans.forward = OPERATOR2ITOP(ops[0]->forward); trans.adjoint = OPERATOR2ITOP(ops[0]->adjoint); trans.N = 2 * md_calc_size(linop_codomain(ops[0])->N, linop_codomain(ops[0])->dims); niht_conf.trans = 1; } float eps = md_norm(1, MD_DIMS(size), image_adj); if (checkeps(eps)) goto cleanup; niht_conf.epsilon = eps * conf->tol; niht(&niht_conf, &trans, select_vecops(image_adj), OPERATOR2ITOP(normaleq_op), OPERATOR_P2ITOP(prox_ops[0]), image, image_adj, monitor); cleanup: ; } void iter2_call_iter(iter_conf* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s* prox_ops[D], const struct linop_s* ops[D], const float* biases[D], const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, struct iter_monitor_s* monitor) { assert(D <= 1); assert(NULL == ops); assert(NULL == biases); UNUSED(xupdate_op); auto it = CAST_DOWN(iter_call_s, _conf); it->fun(it->_conf, normaleq_op, (1 == D) ? prox_ops[0] : NULL, size, image, image_adj, monitor); }
void iter2_admm(iter_conf* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s* prox_ops[D], const struct linop_s* ops[D], const float* biases[D], const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, struct iter_monitor_s* monitor) { auto conf = CAST_DOWN(iter_admm_conf, _conf); struct admm_plan_s admm_plan = { .maxiter = conf->maxiter, .maxitercg = conf->maxitercg, .cg_eps = conf->cg_eps, .rho = conf->rho, .num_funs = D, .do_warmstart = conf->do_warmstart, .dynamic_rho = conf->dynamic_rho, .dynamic_tau = conf->dynamic_tau, .relative_norm = conf->relative_norm, .hogwild = conf->hogwild, .ABSTOL = conf->ABSTOL, .RELTOL = conf->RELTOL, .alpha = conf->alpha, .tau = conf->tau, .tau_max = conf->tau_max, .mu = conf->mu, .fast = conf->fast, .biases = biases, }; struct admm_op a_ops[D ?:1]; struct iter_op_p_s a_prox_ops[D ?:1]; for (unsigned int i = 0; i < D; i++) { a_ops[i].forward = OPERATOR2ITOP(ops[i]->forward), a_ops[i].normal = OPERATOR2ITOP(ops[i]->normal); a_ops[i].adjoint = OPERATOR2ITOP(ops[i]->adjoint); a_prox_ops[i] = OPERATOR_P2ITOP(prox_ops[i]); } admm_plan.ops = a_ops; admm_plan.prox_ops = a_prox_ops; admm_plan.xupdate = OPERATOR_P2ITOP(xupdate_op); long z_dims[D ?: 1]; for (unsigned int i = 0; i < D; i++) z_dims[i] = 2 * md_calc_size(linop_codomain(ops[i])->N, linop_codomain(ops[i])->dims); if (NULL != image_adj) { float eps = md_norm(1, MD_DIMS(size), image_adj); if (checkeps(eps)) goto cleanup; } admm(&admm_plan, admm_plan.num_funs, z_dims, size, (float*)image, image_adj, select_vecops(image), OPERATOR2ITOP(normaleq_op), monitor); cleanup: ; } void iter2_pocs(iter_conf* _conf, const struct operator_s* normaleq_op, unsigned int D, const struct operator_p_s* prox_ops[D], const struct linop_s* ops[D], const float* biases[D], const struct operator_p_s* xupdate_op, long size, float* image, const float* image_adj, struct iter_monitor_s* monitor) { auto conf = CAST_DOWN(iter_pocs_conf, _conf); assert(NULL == normaleq_op); assert(NULL == ops); assert(NULL == biases); assert(NULL == image_adj); UNUSED(xupdate_op); UNUSED(image_adj); struct iter_op_p_s proj_ops[D]; for (unsigned int i = 0; i < D; i++) proj_ops[i] = OPERATOR_P2ITOP(prox_ops[i]); pocs(conf->maxiter, D, proj_ops, select_vecops(image), size, image, monitor); }