InverseProblem
is a Julia package defining tools
used in the general inverse problems framework. The general problem to solve is
to estimate an array x
representing the variable
where
Currently, the package assumes that the data can be expressed as
with Mapping
structure of the LazyAlgebra
package.
The type Cost
defined in this package inherits all properties of the Mapping
type of the package LazyAlgebra
. For every structure C
inheriting the type
Cost
, it is necessary to define two functions:
call
which will be used to applyC
to an arrayx
, with an optional multiplication to a scalara
. These following uses ofC
are similar:# Computes a*C(x) a*call(C, x) call(a, C, x) C(a, x)
call!
which will applyC
to an arrayx
and store the gradient in a given arrayg
. The use of this function is as follows:As forcall!(C, x, g) C(x, g)
call
, an optional scalar can be given. There is also an optional boolean keywordincr
which when put totrue
will add tog
the gradient instead of erasing the values ofg
to store the gradient.
Using the InverseProblem
package, it is possible to define a likelihood
structure Lkl
, of type Cost
, composed of a Mapping
operator A
, a data array d
and
optionally an array w
(full of ones by default) representing the precision of
the data:
L = Lkl(A, d, w)
As for any other structure of type Cost
, L
can be applied to an array x
as
a Mapping
operator or with the functions call
and call!
:
# computes likelihood in x
L(x)
call(L, x)
# computes likelihood in x and stores the gradient in g
L(x, g)
call!(L, x, g)
which computes
with w
.
A type Regularization
is defined, inheriting the properties of the type
Cost
, to group different definition of regularization. From this type, a
Regul
structure can be used to define a regularization function. To do so, the
user must create a custom structure which inherit from the Regularization
type
and define the two functions call
and call!
associated:
# Custom structure
struct CustomReg <:Regularization end
# Definition of call! and call
function call!(a::Real, ::CustomReg, x::AbstractArray{T,N}, g::AbstractArray{T,N};
incr::Bool = false) where {T,N}
...
end
function call(a::Real, ::CustomReg, x::AbstractArray{T,N}) where {T,N}
...
end
These two functions are usually used when solving iteratively an optimization problem for the computation of the loss function. If the user wants to solve analytically the problem by directly inverting the Normal equations, it is possible to define a function yielding the gradient operator:
function get_grad_op(a::Real, ::CustomReg)
...
end
The regularization can then be defined as a function:
mu = 10^3 # hyper-parameter
f = CustomReg() # custom regularization as a structure
direct_inversion = false # or true if CustomReg can be directly inverted
reg() = Regul(mu, f, direct_inversion)
As for certain optimization method the use of particular regularization
function is needed, it is possible to create its own instance of type
Regularization
. An example is given in the package for homogeneous
regularization, that is regularization
with
As it requires the knowledge of the degree, it is possible to create a new structure:
struct HomogenRegul{T} <: Regularization
Reg::Regul{T}
deg::T
end
HomogenRegul(mu::Real, f, inv::Bool, deg::Real) = HomogenRegul(Regul(mu, f, inv), deg)
# define the function call! and call, generic for every type Cost
call!(a, R::HomogenRegul, x, g; kwds...) = call!(a, R.Reg, x, g; kwds...)
call(a, R::HomogenRegul, x) = call(a, R.Reg, x)
From then, the definition of a custom regularization is as it is described above
except when creating the function reg()
:
# function to retrieve the degree of regularization
degree(::CustomReg) = ...
# definition of the regularization function
reg() = HomogenRegul(mu, f, direct_inversion, degree(::CustomReg))
Several commonly used regularization are already defined in the file
regularization.jl
:
-
$\ell 1$ norm asnorml1()
; -
$\ell 2$ norm asnorml2()
; - Tikhonov as
tikhonov()
; - edge-preserving as
edgepreserving($\tau$)
with$\tau$ a parameter tuning the$\ell 1$ -$\ell 2$ threshold; - an homogeneous version of edge-preserving as
homogenedgepreserving($\tau$)
;
It is possible to call a sum of Regularization
structure which will behave as
the sum of the two structure. For instance, the following lines yield the same
results:
# create array x
x = randn(50,50)
# creation of a SumRegul structure and application to x
sumreg = tikhonov() + edgepreserving(10^1)
sumreg(x)
# or without SumRegul structure
tikhonov()(x) + edgepreserving(10^1)(x)
The package provides also an InvProblem
structure which can be used to store
a loss function in the form of a sum of likelihood and regularization. To define
such loss function, it is possible to call:
G = InvProblem(A, d, w, R)
with the arguments of the structure defined as above. As it is of type Cost
,
G
can be used as the other structures described here (either as an operator or
by using call!
and call
).
The packages also defines structures of type Solver
which can be used to solve
the problem defined by an instance S
of type Cost
(e.g. an Lkl
or
InvProblem structure
). Such structure calls the functions solve
and solve!
to find the solution using a chosen optimization method m
:
# define a cache to store the criterion's values
c = []
# initialization of the optimization method
x0 = randn(50,50)
# call to solve the problem S and store the criterion's value at each iteration in c
solve(x0, S, m, c; keep_loss=true)
Multiple optimization strategies are already called as the VMLM-B
quasi-Newton
method, the Brent fmin
method and Powell's Bobyqa
and Newuoa
methods, all
defined in the OptimPackNextGen
package.
This part of the package is to be considered as a wrapper on these optimization methods, used to facilitate the storage and extraction of intermediate values of the computation.