def set_objective(self, X, y, lmbd): self.X, self.y, self.lmbd = X, y, lmbd n_features = self.X.shape[1] if self.restart_strategy == 'greedy': min_beta = 1.0 s_greedy = 1.1 p_lazy = 1.0 q_lazy = 1.0 else: min_beta = None s_greedy = None p_lazy = 1 / 30 q_lazy = 1 / 10 self.fb = ForwardBackward( x=np.zeros(n_features), # this is the coefficient w grad=GradBasic( input_data=y, op=lambda w: self.X@w, trans_op=lambda res: self.X.T@res, ), prox=SparseThreshold(Identity(), lmbd), beta_param=1.0, min_beta=min_beta, metric_call_period=None, restart_strategy=self.restart_strategy, xi_restart=0.96, s_greedy=s_greedy, p_lazy=p_lazy, q_lazy=q_lazy, auto_iterate=False, progress=False, cost=None, )
def fista(gradient_op, linear_op, prox_op, cost_op, lambda_init=1.0, max_nb_of_iter=300, x_init=None, metric_call_period=5, metrics={}, verbose=0, **lambda_update_params): """ The FISTA sparse reconstruction Parameters ---------- gradient_op: instance of class GradBase the gradient operator. linear_op: instance of LinearBase the linear operator: seek the sparsity, ie. a wavelet transform. prox_op: instance of ProximityParent the proximal operator. cost_op: instance of costObj the cost function used to check for convergence during the optimization. lambda_init: float, (default 1.0) initial value for the FISTA step. max_nb_of_iter: int (optional, default 300) the maximum number of iterations in the Condat-Vu proximal-dual splitting algorithm. x_init: np.ndarray (optional, default None) Inital guess for the image metric_call_period: int (default 5) the period on which the metrics are compute. metrics: dict (optional, default None) the list of desired convergence metrics: {'metric_name': [@metric, metric_parameter]}. See modopt for the metrics API. verbose: int (optional, default 0) the verbosity level. lambda_update_params: dict, Parameters for the lambda update in FISTA mode Returns ------- x_final: ndarray the estimated FISTA solution. costs: list of float the cost function values. metrics: dict the requested metrics values during the optimization. """ start = time.perf_counter() # Define the initial primal and dual solutions if x_init is None: x_init = np.squeeze( np.zeros( (gradient_op.linear_op.n_coils, *gradient_op.fourier_op.shape), dtype=np.complex)) alpha_init = linear_op.op(x_init) # Welcome message if verbose > 0: print(" - mu: ", prox_op.weights) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", gradient_op.fourier_op.shape) if hasattr(linear_op, "nb_scale"): print(" - wavelet: ", linear_op, "-", linear_op.nb_scale) print(" - max iterations: ", max_nb_of_iter) print(" - image variable shape: ", gradient_op.fourier_op.shape) print(" - alpha variable shape: ", alpha_init.shape) print("-" * 40) beta_param = gradient_op.inv_spec_rad if lambda_update_params.get("restart_strategy") == "greedy": lambda_update_params["min_beta"] = gradient_op.inv_spec_rad # this value is the recommended one by J. Liang in his article # when introducing greedy FISTA. # ref: https://arxiv.org/pdf/1807.04005.pdf beta_param *= 1.3 # Define the optimizer opt = ForwardBackward(x=alpha_init, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False, metric_call_period=metric_call_period, metrics=metrics, linear=linear_op, lambda_param=lambda_init, beta_param=beta_param, **lambda_update_params) cost_op = opt._cost_func # Perform the reconstruction if verbose > 0: print("Starting optimization...") opt.iterate(max_iter=max_nb_of_iter) end = time.perf_counter() if verbose > 0: # cost_op.plot_cost() if hasattr(cost_op, "cost"): print(" - final iteration number: ", cost_op._iteration) print(" - final log10 cost value: ", np.log10(cost_op.cost)) print(" - converged: ", opt.converge) print("Done.") print("Execution time: ", end - start, " seconds") print("-" * 40) x_final = linear_op.adj_op(opt.x_final) if hasattr(cost_op, "cost"): costs = cost_op._cost_list else: costs = None return x_final, costs, opt.metrics
def sparse_rec_fista(gradient_op, linear_op, prox_op, cost_op, mu=1e-6, nb_scales=4, lambda_init=1.0, max_nb_of_iter=300, atol=1e-4, metric_call_period=5, metrics=None, verbose=0): """ The FISTA sparse reconstruction without reweightings. .. note:: At the moment, tested only with 2D data. Parameters ---------- gradient_op: instance of class GradBase the gradient operator. linear_op: instance of LinearBase the linear operator: seek the sparsity, ie. a wavelet transform. prox_op: instance of ProximityParent the proximal operator. cost_op: instance of costObj the cost function used to check for convergence during the optimization. mu: float, (default 1e-6) coefficient of regularization. nb_scales: int, default 4 the number of scales in the wavelet decomposition. lambda_init: float, (default 1.0) initial value for the FISTA step. max_nb_of_iter: int (optional, default 300) the maximum number of iterations in the Condat-Vu proximal-dual splitting algorithm. atol: float (optional, default 1e-4) tolerance threshold for convergence. metric_call_period: int (default 5) the period on which the metrics are compute. metrics: dict (optional, default None) the list of desired convergence metrics: {'metric_name': [@metric, metric_parameter]}. See modopt for the metrics API. verbose: int (optional, default 0) the verbosity level. Returns ------- x_final: ndarray the estimated FISTA solution. transform: a WaveletTransformBase derived instance the wavelet transformation instance. costs: list of float the cost function values. metrics: dict the requested metrics values during the optimization. """ # Check inputs start = time.clock() if not linear_op.transform.__is_decimated__: warnings.warn("Undecimated wavelets shouldn't be used with FISTA: " "non optimal solution.") # Define the initial primal and dual solutions x_init = np.zeros(gradient_op.fourier_op.shape, dtype=np.complex) alpha = linear_op.op(x_init) alpha[...] = 0.0 # Welcome message if verbose > 0: print(fista_logo()) print(" - mu: ", mu) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", gradient_op.fourier_op.shape) if hasattr(linear_op, "nb_scale"): print(" - wavelet: ", linear_op, "-", linear_op.nb_scale) print(" - max iterations: ", max_nb_of_iter) print(" - image variable shape: ", x_init.shape) print(" - alpha variable shape: ", alpha.shape) print("-" * 40) # Define the proximity dual operator weights = copy.deepcopy(alpha) weights[...] = mu prox_op.weights = weights # Define the optimizer opt = ForwardBackward(x=alpha, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False, metric_call_period=metric_call_period, metrics=metrics or {}, linear=linear_op, beta_param=gradient_op.inv_spec_rad) cost_op = opt._cost_func # Perform the reconstruction if verbose > 0: print("Starting optimization...") opt.iterate(max_iter=max_nb_of_iter) end = time.clock() if verbose > 0: # cost_op.plot_cost() if hasattr(cost_op, "cost"): print(" - final iteration number: ", cost_op._iteration) print(" - final log10 cost value: ", np.log10(cost_op.cost)) print(" - converged: ", opt.converge) print("Done.") print("Execution time: ", end - start, " seconds") print("-" * 40) x_final = linear_op.adj_op(opt.x_final) if hasattr(cost_op, "cost"): costs = cost_op._cost_list else: costs = None return x_final, linear_op.transform, costs, opt.metrics
def sparse_rec_fista(gradient_op, linear_op, mu, lambda_init=1.0, max_nb_of_iter=300, atol=1e-4, verbose=0, get_cost=False): """ The FISTA sparse reconstruction without reweightings. .. note:: At the moment, supports only 2D data. Parameters ---------- gradient_op: Instance of class GradBase The gradient operator of the differentiable part linear_op: Instance of LinearBase The linear operator is the tranform in wich we seek the sparsity. samples: np.ndarray the mask samples in the Fourier domain. mu: float coefficient of regularization. nb_scales: int, default 4 the number of scales in the wavelet decomposition. lambda_init: float, (default 1.0) initial value for the FISTA step. max_nb_of_iter: int (optional, default 300) the maximum number of iterations in the Condat-Vu proximal-dual splitting algorithm. atol: float (optional, default 1e-4) tolerance threshold for convergence. verbose: int (optional, default 0) the verbosity level. get_cost: bool (default False) computes the cost of the objective function Returns ------- x_final: ndarray the estimated FISTA solution. transform: a WaveletTransformBase derived instance the wavelet transformation instance. """ # Check inputs start = time.clock() # Define the initial primal and dual solutions x_init = np.zeros(gradient_op.fourier_op.shape, dtype=np.complex) alpha = linear_op.op(x_init) alpha[...] = 0.0 # Welcome message if verbose > 0: print(fista_logo()) print(" - mu: ", mu) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", gradient_op.obs_data.shape) print(" - max iterations: ", max_nb_of_iter) print(" - image variable shape: ", x_init.shape) print(" - alpha variable shape: ", alpha.shape) print("-" * 40) # Define the proximity dual operator weights = copy.deepcopy(alpha) weights[...] = mu prox_op = Threshold(weights) # Define the optimizer cost_op = None opt = ForwardBackward(x=alpha, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False) # Perform the reconstruction if verbose > 0: print("Starting optimization...") if get_cost: cost = np.zeros(max_nb_of_iter) for i in range(max_nb_of_iter): opt._update() if get_cost: cost[i] = gradient_op.get_cost(opt._x_new) + \ prox_op.get_cost(opt._x_new) if opt.converge: print(' - Converged!') if get_cost: cost = cost[0:i] break opt.x_final = opt._x_new end = time.clock() if verbose > 0: # cost_op.plot_cost() # print(" - final iteration number: ", cost_op._iteration) # print(" - final log10 cost value: ", np.log10(cost_op.cost)) print(" - converged: ", opt.converge) print("Done.") print("Execution time: ", end - start, " seconds") print("-" * 40) x_final = linear_op.adj_op(opt.x_final) if get_cost: return x_final, linear_op.transform, cost else: return x_final, linear_op.transform
def fista_online(kspace_generator, gradient_op, linear_op, prox_op, cost_op, lambda_init=1.0, x_init=None, nb_run=1, metric_call_period=5, metrics=None, estimate_call_period=None, verbose=0, **lambda_update_params): """The FISTA sparse reconstruction Parameters ---------- kspace_generator: instance of class KspaceGenerator the observed data (ie kspace) generated for each iteration of the algorithm gradient_op: instance of class GradBase the gradient operator. linear_op: instance of LinearBase the linear operator: seek the sparsity, ie. a wavelet transform. prox_op: instance of ProximityParent the proximal operator. cost_op: instance of costObj the cost function used to check for convergence during the optimization. lambda_init: float, (default 1.0) initial value for the FISTA step. x_init: np.ndarray (optional, default None) Inital guess for the image metric_call_period: int (default 5) the period on which the metrics are compute. metrics: dict (optional, default None) the list of desired convergence metrics: {'metric_name': [@metric, metric_parameter]}. See modopt for the metrics API. verbose: int (optional, default 0) the verbosity level. lambda_update_params: dict, Parameters for the lambda update in FISTA mode Returns ------- x_final: ndarray the estimated FISTA solution. costs: list of float the cost function values. metrics: dict the requested metrics values during the optimization. """ if metrics is None: metrics = dict() start = time.perf_counter() # Define the initial primal and dual solutions if x_init is None: x_init = np.squeeze( np.zeros( (gradient_op.fourier_op.n_coils, *gradient_op.fourier_op.shape), dtype=np.complex, )) alpha_init = linear_op.op(x_init) # Welcome message if verbose > 0: print(" - mu: ", prox_op.weights) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", gradient_op.fourier_op.shape) if hasattr(linear_op, "nb_scale"): print(" - wavelet: ", linear_op, "-", linear_op.nb_scale) print(" - image variable shape: ", gradient_op.fourier_op.shape) print(" - alpha variable shape: ", alpha_init.shape) print("-" * 40) beta_param = gradient_op.inv_spec_rad if lambda_update_params.get("restart_strategy") == "greedy": lambda_update_params["min_beta"] = gradient_op.inv_spec_rad # this value is the recommended one by J. Liang in his article # when introducing greedy FISTA. # ref: https://arxiv.org/pdf/1807.04005.pdf beta_param *= 1.3 # Define the optimizer opt = ForwardBackward(x=alpha_init, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False, metric_call_period=metric_call_period, metrics=metrics, linear=linear_op, lambda_param=lambda_init, beta_param=beta_param, **lambda_update_params) cost_op = opt._cost_func return online_algorithm(opt, kspace_generator, estimate_call_period=estimate_call_period, nb_run=nb_run)
def sparse_rec_fista(data, wavelet_name, samples, mu, nb_scales=4, lambda_init=1.0, max_nb_of_iter=300, atol=1e-4, non_cartesian=False, uniform_data_shape=None, verbose=0): """ The FISTA sparse reconstruction without reweightings. .. note:: At the moment, supports only 2D data. Parameters ---------- data: ndarray the data to reconstruct (observation are expected in the Fourier space). wavelet_name: str the wavelet name to be used during the decomposition. samples: np.ndarray the mask samples in the Fourier domain. mu: float coefficient of regularization. nb_scales: int, default 4 the number of scales in the wavelet decomposition. lambda_init: float, (default 1.0) initial value for the FISTA step. max_nb_of_iter: int (optional, default 300) the maximum number of iterations in the Condat-Vu proximal-dual splitting algorithm. atol: float (optional, default 1e-4) tolerance threshold for convergence. non_cartesian: bool (optional, default False) if set, use the nfftw rather than the fftw. Expect an 1D input dataset. uniform_data_shape: uplet (optional, default None) the shape of the matrix containing the uniform data. Only required for non-cartesian reconstructions. verbose: int (optional, default 0) the verbosity level. Returns ------- x_final: ndarray the estimated FISTA solution. transform: a WaveletTransformBase derived instance the wavelet transformation instance. """ # Check inputs start = time.clock() if non_cartesian and data.ndim != 1: raise ValueError("Expect 1D data with the non-cartesian option.") elif non_cartesian and uniform_data_shape is None: raise ValueError("Need to set the 'uniform_data_shape' parameter with " "the non-cartesian option.") elif not non_cartesian and data.ndim != 2: raise ValueError("At the moment, this functuion only supports 2D " "data.") # Define the gradient/linear/fourier operators linear_op = Wavelet2( nb_scale=nb_scales, wavelet_name=wavelet_name) if non_cartesian: fourier_op = NFFT2( samples=samples, shape=uniform_data_shape) else: fourier_op = FFT2( samples=samples, shape=data.shape) gradient_op = GradSynthesis2( data=data, linear_op=linear_op, fourier_op=fourier_op) # Define the initial primal and dual solutions x_init = np.zeros(fourier_op.shape, dtype=np.complex) alpha = linear_op.op(x_init) alpha[...] = 0.0 # Welcome message if verbose > 0: print(fista_logo()) print(" - mu: ", mu) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", data.shape) print(" - wavelet: ", wavelet_name, "-", nb_scales) print(" - max iterations: ", max_nb_of_iter) print(" - image variable shape: ", x_init.shape) print(" - alpha variable shape: ", alpha.shape) print("-" * 40) # Define the proximity dual operator weights = copy.deepcopy(alpha) weights[...] = mu prox_op = SparseThreshold(linear_op, weights, thresh_type="soft") # Define the optimizer cost_op = None opt = ForwardBackward( x=alpha, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False) # Perform the reconstruction end = time.clock() if verbose > 0: print("Starting optimization...") opt.iterate(max_iter=max_nb_of_iter) if verbose > 0: # cost_op.plot_cost() # print(" - final iteration number: ", cost_op._iteration) # print(" - final log10 cost value: ", np.log10(cost_op.cost)) print(" - converged: ", opt.converge) print("Done.") print("Execution time: ", end - start, " seconds") print("-" * 40) x_final = linear_op.adj_op(opt.x_final) return x_final, linear_op.transform
def fista(gradient_op, linear_op, prox_op, cost_op, kspace_generator=None, estimate_call_period=None, lambda_init=1.0, max_nb_of_iter=300, x_init=None, metric_call_period=5, metrics={}, verbose=0, **lambda_update_params): """FISTA sparse reconstruction. Parameters ---------- gradient_op: instance of class GradBase the gradient operator. linear_op: instance of LinearBase the linear operator: seek the sparsity, ie. a wavelet transform. prox_op: instance of ProximityParent the proximal operator. cost_op: instance of costObj the cost function used to check for convergence during the optimization. kspace_generator: instance of class BaseKspaceGenerator, default None If not None, run the algorithm in an online way, where the data is updated between iterations. estimate_call_period: int, default None In an online configuration (kspace_generator is defined), retrieve partial results at this interval. lambda_init: float, (default 1.0) initial value for the FISTA step. max_nb_of_iter: int (optional, default 300) the maximum number of iterations in the Condat-Vu proximal-dual splitting algorithm. x_init: np.ndarray (optional, default None) Inital guess for the image metric_call_period: int (default 5) the period on which the metrics are compute. metrics: dict (optional, default None) the list of desired convergence metrics: {'metric_name': [@metric, metric_parameter]}. See modopt for the metrics API. verbose: int (optional, default 0) the verbosity level. lambda_update_params: dict, Parameters for the lambda update in FISTA mode Returns ------- x_final: ndarray the estimated FISTA solution. costs: list of float the cost function values. metrics: dict the requested metrics values during the optimization. """ # Define the initial primal and dual solutions if x_init is None: x_init = np.squeeze( np.zeros( (gradient_op.linear_op.n_coils, *gradient_op.fourier_op.shape), dtype=np.complex)) alpha_init = linear_op.op(x_init) # Welcome message if verbose > 0: print(" - mu: ", prox_op.weights) print(" - lipschitz constant: ", gradient_op.spec_rad) print(" - data: ", gradient_op.fourier_op.shape) if hasattr(linear_op, "nb_scale"): print(" - wavelet: ", linear_op, "-", linear_op.nb_scale) print(" - max iterations: ", max_nb_of_iter) print(" - image variable shape: ", gradient_op.fourier_op.shape) print(" - alpha variable shape: ", alpha_init.shape) print("-" * 40) beta_param = gradient_op.inv_spec_rad if lambda_update_params.get("restart_strategy") == "greedy": lambda_update_params["min_beta"] = gradient_op.inv_spec_rad # this value is the recommended one by J. Liang in his article # when introducing greedy FISTA. # ref: https://arxiv.org/pdf/1807.04005.pdf beta_param *= 1.3 # Define the optimizer opt = ForwardBackward(x=alpha_init, grad=gradient_op, prox=prox_op, cost=cost_op, auto_iterate=False, metric_call_period=metric_call_period, metrics=metrics, linear=linear_op, lambda_param=lambda_init, beta_param=beta_param, **lambda_update_params) if kspace_generator is not None: return run_online_algorithm(opt, kspace_generator, estimate_call_period, verbose) return run_algorithm(opt, max_nb_of_iter, verbose)