@@ -3,6 +3,7 @@ using LowRankApprox: pqrfact
33using IterativeSolvers
44using . BayesianLinear
55using LinearAlgebra: SVD, svd
6+ using ActiveSetPursuit
67
78@doc raw """
89`struct QR` : linear least squares solver, using standard QR factorisation;
@@ -195,3 +196,95 @@ function solve(solver::TruncatedSVD, A, y)
195196 return Dict {String, Any} (" C" => solver. P \ θP)
196197end
197198
199+
200+ @doc raw """
201+ `struct ASP` : Active Set Pursuit sparse solver
202+ solves the following optimization problem using the homotopy approach:
203+
204+ ```math
205+ \m ax_{y} \l eft( b^T y - \f rac{1}{2} λ y^T y \r ight)
206+ ```
207+ subject to
208+
209+ ```math
210+ \| A^T y\| _{\i nfty} \l eq 1.
211+ ```
212+
213+ * Input
214+ * `A` : `m`-by-`n` explicit matrix or linear operator.
215+ * `b` : `m`-vector.
216+
217+ * Solver parameters
218+ * `min_lambda` : Minimum value for `λ`. Defaults to zero if not provided.
219+ * `loglevel` : Logging level.
220+ * `itnMax` : Maximum number of iterations.
221+ * `actMax` : Maximum number of active constraints.
222+
223+ Constructor
224+ ```julia
225+ ACEfit.ASP(; P = I, select, params)
226+ ```
227+ where
228+ - `P` : right-preconditioner / tychonov operator
229+ - `select`: Selection mode for the final solution.
230+ - `(:byerror, th)`: Selects the smallest active set fit within a factor `th` of the smallest fit error.
231+ - `(:final, nothing)`: Returns the final iterate.
232+ - `params`: The solver parameters, passed as named arguments.
233+ """
234+ struct ASP
235+ P:: Any
236+ select:: Tuple
237+ params:: NamedTuple
238+ end
239+
240+ function ASP (; P = I, select, params... )
241+ params_tuple = NamedTuple (params)
242+ return ASP (P, select, params_tuple)
243+ end
244+
245+ function solve (solver:: ASP , A, y)
246+ # Apply preconditioning
247+ AP = A / solver. P
248+
249+ tracer = asp_homotopy (AP, y; solver. params[1 ]. .. )
250+
251+ new_tracer = Vector {NamedTuple{(:solution, :λ), Tuple{Any, Any}}} (undef, length (tracer))
252+
253+ for i in 1 : length (tracer)
254+ new_tracer[i] = (solution = solver. P \ tracer[i][1 ], λ = tracer[i][2 ])
255+ end
256+
257+ # Select the final solution based on the criterion
258+ xs, in = select_solution (new_tracer, solver, A, y)
259+
260+ println (" done." )
261+ return Dict (" C" => xs, " path" => new_tracer, " nnzs" => length ((tracer[in][1 ]). nzind) )
262+ end
263+
264+ function select_solution (tracer, solver, A, y)
265+ criterion, threshold = solver. select
266+
267+ if criterion == :final
268+ return tracer[end ][1 ], length (tracer)
269+
270+ elseif criterion == :byerror
271+ errors = [norm (A * t[1 ] - y) for t in tracer]
272+ min_error = minimum (errors)
273+
274+ # Find the solution with the smallest error within the threshold
275+ for (i, error) in enumerate (errors)
276+ if error <= threshold * min_error
277+ return tracer[i][1 ], i
278+ end
279+ end
280+ elseif criterion == :bysize
281+ for i in 1 : length (tracer)
282+ if length ((tracer[i][1 ]). nzind) == threshold
283+ return tracer[i][1 ], i
284+ end
285+ end
286+ else
287+ @error (" Unknown selection criterion: $criterion " )
288+ end
289+ end
290+
0 commit comments