Skip to content

Commit 9f02139

Browse files
aris-mavarismav
andauthored
add nnls extension (#84)
Co-authored-by: arismav <aris.mav@hotmail.com>
1 parent 9374c41 commit 9f02139

File tree

10 files changed

+120
-38
lines changed

10 files changed

+120
-38
lines changed

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
/Manifest.toml
2-
test.jl
3-
42
docs/Manifest.toml
3+
ext/Manifest.toml
54
docs/build
6-
example_data/d_t2/

Project.toml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
1717
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
1818
QuadraticModels = "f468eda6-eac5-11e8-05a5-ff9e497bcd19"
1919
RipQP = "1e40b3f8-35eb-4cd8-8edd-3e515bb9de08"
20+
NonNegLeastSquares = "b7351bd1-99d9-5c5d-8786-f205a815c4d7"
2021

2122
[extensions]
2223
gui_ext = "GLMakie"
24+
nnls_ext = "NonNegLeastSquares"
2325
tikhonov_jump_ext = "JuMP"
2426
tikhonov_ripqp_ext = ["RipQP", "QuadraticModels"]
2527

@@ -28,6 +30,7 @@ DelimitedFiles = "1"
2830
GLMakie = "0.11, 0.12, 0.13"
2931
JuMP = "1"
3032
NativeFileDialog = "0.2"
33+
NonNegLeastSquares = "0.4.1"
3134
Optim = "1.10.0"
3235
PolygonOps = "0.1"
3336
QuadraticModels = "0.9"
@@ -43,11 +46,4 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
4346
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4447

4548
[targets]
46-
test = [
47-
"Test",
48-
"GLMakie",
49-
"NMRInversions",
50-
"Random",
51-
"SparseArrays",
52-
"LinearAlgebra"
53-
]
49+
test = ["Test", "GLMakie", "NMRInversions", "Random", "SparseArrays", "LinearAlgebra"]

docs/src/types_structs.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ cdL1
3535
optim_nnls
3636
```
3737

38+
The following are also implemented through
39+
external package extensions.
40+
41+
```@docs
42+
nnls
43+
jump_nnls
44+
```
45+
3846
## Finding optimal alpha
3947
These are methods for finding the optimal regularization parameter.
4048
They can be used as input to the `invert` function as the 'alpha' argument

ext/Manifest.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# This file is machine-generated - editing it directly is not advised
22

3-
julia_version = "1.12.4"
3+
julia_version = "1.12.5"
44
manifest_format = "2.0"
5-
project_hash = "36034e3e9c89d48d9c539f2b0060983a7836f13f"
5+
project_hash = "55fd022efc13f657a37564ea8957b17208f5633d"
66

77
[[deps.ADTypes]]
88
git-tree-sha1 = "f7304359109c768cf32dc5fa2d371565bb63b68a"
@@ -1069,16 +1069,18 @@ version = "7.10.0"
10691069
deps = ["DelimitedFiles", "LinearAlgebra", "NativeFileDialog", "Optim", "Pkg", "PolygonOps", "SparseArrays"]
10701070
path = ".."
10711071
uuid = "55c20db2-0166-4687-95c3-62a9c7afb29b"
1072-
version = "1.2.0"
1072+
version = "1.3.0"
10731073

10741074
[deps.NMRInversions.extensions]
10751075
gui_ext = "GLMakie"
1076+
nnls_ext = "NonNegLeastSquares"
10761077
tikhonov_jump_ext = "JuMP"
10771078
tikhonov_ripqp_ext = ["RipQP", "QuadraticModels"]
10781079

10791080
[deps.NMRInversions.weakdeps]
10801081
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
10811082
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
1083+
NonNegLeastSquares = "b7351bd1-99d9-5c5d-8786-f205a815c4d7"
10821084
QuadraticModels = "f468eda6-eac5-11e8-05a5-ff9e497bcd19"
10831085
RipQP = "1e40b3f8-35eb-4cd8-8edd-3e515bb9de08"
10841086

@@ -1110,6 +1112,12 @@ version = "1.1.1"
11101112
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
11111113
version = "1.3.0"
11121114

1115+
[[deps.NonNegLeastSquares]]
1116+
deps = ["LinearAlgebra", "SparseArrays"]
1117+
git-tree-sha1 = "cdc11138e74a0dd0b82e7d64eb1350fdf049d3b1"
1118+
uuid = "b7351bd1-99d9-5c5d-8786-f205a815c4d7"
1119+
version = "0.4.1"
1120+
11131121
[[deps.Observables]]
11141122
git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225"
11151123
uuid = "510215fc-4207-5dde-b226-833fc4488ee2"

ext/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
33
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
44
NMRInversions = "55c20db2-0166-4687-95c3-62a9c7afb29b"
55
NativeFileDialog = "e1fe445b-aa65-4df4-81c1-2041507f0fd4"
6+
NonNegLeastSquares = "b7351bd1-99d9-5c5d-8786-f205a815c4d7"
67
PolygonOps = "647866c9-e3ac-4575-94e7-e3d426903924"

ext/nnls_ext.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module nnls_ext
2+
3+
using NMRInversions
4+
using LinearAlgebra, NonNegLeastSquares
5+
6+
function NMRInversions.solve_regularization(K::AbstractMatrix, g::AbstractVector, α::Real, solver::nnls)
7+
8+
L = if isa(solver.L,Int)
9+
NMRInversions.Γ(size(K, 2), solver.L)
10+
else
11+
if size(solver.L, 2) == size(K, 2)
12+
solver.L
13+
else
14+
throw("Size mismatch between `solver.L` and the Kernel.")
15+
end
16+
end
17+
18+
A = [K; (α) .* L]
19+
b = [g; zeros(size(A, 1) - size(g, 1))]
20+
21+
f = vec(nonneg_lsq(A, b ; alg=solver.algorithm))
22+
23+
r = K * f - g
24+
25+
return f, r
26+
end
27+
28+
end

ext/tikhonov_jump_ext.jl

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,22 @@ using NMRInversions, JuMP, LinearAlgebra, SparseArrays
88
# import JuMP: @constraints
99
# import JuMP: @objective
1010

11-
export jump_nnls
12-
"""
13-
jump_nnls(order, solver)
14-
Jump non-negative least squares method for tikhonov (L2) regularization,
15-
implemented using the JuMP extension.
16-
All around effective, but can be slow for large problems, such as 2D inversions,
17-
unless a powerful solver like gurobi is used.
18-
19-
- `solver` is passed directly into JuMP.
20-
- `order` determines the tikhonov finite-difference matrix. \
21-
If 0 is chosen, the identity matrix is used.
22-
23-
This one is still experimental and not well-tested,
24-
please submit an issue if you encounter any difficulties.
25-
"""
26-
struct jump_nnls <: regularization_solver
27-
order::Int
28-
solver::Symbol
29-
end
3011

3112
function NMRInversions.solve_regularization(K::AbstractMatrix, g::AbstractVector,
3213
α::Real, solver::Type{jump_nnls})
3314

34-
A = sparse([K; (α) .* NMRInversions.Γ(size(K, 2), solver.order)])
35-
b = sparse([g; zeros(size(A, 1) - size(g, 1))])
15+
L = if isa(solver.L,Int)
16+
NMRInversions.Γ(size(K, 2), solver.L)
17+
else
18+
if size(solver.L, 2) == size(K, 2)
19+
solver.L
20+
else
21+
throw("Size mismatch between `solver.L` and the Kernel.")
22+
end
23+
end
24+
25+
A = [K; (α) .* L]
26+
b = [g; zeros(size(A, 1) - size(g, 1))]
3627

3728
@eval model = JuMP.Model($(solver.solver).Optimizer)
3829
set_silent(model)

src/brd.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Optim
22

33
"""
4-
brd
4+
brd()
55
Solver for tikhonov (L2) regularization, following \
66
[this paper](https://doi.org/10.1109/78.995059)
77
[Venka2002](@cite).

src/optim_least_squares.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ using Optim
22

33
export optim_nnls
44
"""
5-
optim_nnls(order)
5+
optim_nnls(; L, algorithm, opts)
66
Simple non-negative least squares method for regularization,
77
implemented using Optim.jl.
88
All around effective, but can be slow for large problems, such as 2D inversions.
@@ -31,7 +31,7 @@ function solve_regularization(K::AbstractMatrix, g::AbstractVector, α::Real, so
3131
if size(solver.L, 2) == size(K, 2)
3232
solver.L
3333
else
34-
throw("Size mismatch between `optim_nnls.L` and the Kernel.")
34+
throw("Size mismatch between `solver.L` and the Kernel.")
3535
end
3636
end
3737

src/types.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,55 @@ mutable struct expfit_struct
191191
eqn::String
192192
title::String
193193
end
194+
195+
196+
# define structs for extension solvers
197+
function solve_regularization(K, g, α, solver::regularization_solver)
198+
error("The package needed for $(typeof(solver)) is not loaded.")
199+
end
200+
201+
export nnls
202+
"""
203+
nnls(; L, algorithm)
204+
Non-negative least squares method for regularization,
205+
implemented using the `NonNegLeastSquares` package extension.
206+
Decent speed for small, 1D regularizations, but very slow for larger, 2D problems.
207+
It can be used as a "solver" for invert function.
208+
209+
- `L` determines the tikhonov matrix.\
210+
Can be either a matrix or an integer `n`. The latter will create a finite difference matrix\
211+
of order `n`, which means that the penalty term will be the `n`'th derivative of the distribution.\
212+
Defaults to `0`, where `L` becomes the Identity matrix.
213+
- `algorithm` is one of:
214+
* `:nnls`
215+
* `:fnnls`
216+
* `:pivot`
217+
"""
218+
struct nnls <: regularization_solver
219+
L::Union{Int, AbstractMatrix}
220+
algorithm::Symbol
221+
end
222+
nnls(;L=0, algorithm=:nnls) = nnls(L, algorithm)
223+
224+
225+
export jump_nnls
226+
"""
227+
jump_nnls(L, solver)
228+
Jump non-negative least squares method for tikhonov (L2) regularization,
229+
implemented using the JuMP package extension.
230+
All around effective, but can be slow for large problems, such as 2D inversions,
231+
unless a powerful solver like gurobi is used.
232+
233+
- `L` determines the tikhonov matrix.\
234+
Can be either a matrix or an integer `n`. The latter will create a finite difference matrix\
235+
of order `n`, which means that the penalty term will be the `n`'th derivative of the distribution.\
236+
Defaults to `0`, where `L` becomes the Identity matrix.
237+
- `solver` is passed directly into JuMP, see its documentation for available options.
238+
239+
This one is still experimental and not well-tested,
240+
please submit an issue if you encounter any difficulties.
241+
"""
242+
struct jump_nnls <: regularization_solver
243+
order::Int
244+
solver::Symbol
245+
end

0 commit comments

Comments
 (0)