Reconstruction via convex programming
In this tutorial, we show how to implement in practice the reconstruction method introduced in (Harrach, 2023). It is based on the resolution of a convex nonlinear semidefinite program of the form
\[\underset{\sigma\in[a,b]^n}{\mathrm{min}}~\langle c,\sigma\rangle~~\mathrm{s.t.}~~\Lambda(\sigma)\leq y,\]
where $c$ is a weight vector with positive entries and $y$ is the vector of observations. If the number of measurements $m$ is large enough, then there exists a vector $c$ such that, for every $\sigma^\dagger\in[a,b]^n$, the above problem with ${y=\Lambda(\sigma^\dagger)}$ has a unique solution which is $\sigma^\dagger$.
Setting
We define the forward problem. There are two annuli with the inner radius being 0.5.
using RadialCalderon
n = 2
r = [0.5]
forward = ForwardProblem(r)
a = 0.5
b = 1.5;
Estimation of the weight vector
For a given $m$, one can try to find a universal vector $c$ by solving a feasibility problem constructed via build_c_estimation_problem
. Below, we check that, when $n=2$, this is possible for $m=3$ but not for $m=2$. First, we define a set of conductivities which will be used to estimate $c$.
using Base.Iterators: product
k = 2
prod_it = product([range(a, b, k) for i=1:forward.n]...)
σ = hcat(collect.(collect(prod_it))...)
σ = σ[:, 1:end-1]; # remove b*ones(n)
We build the $c$ estimation problem for $m=2$ and check that it is not feasible. The problem is a linear program, so that any suitable solver other than Ipopt can be used (in our experiments, MOSEK performed best).
using JuMP
import Ipopt
m = 2
model = build_c_estimation_problem(σ, a, b, m, forward)
optimizer = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0, "sb" => "yes")
set_optimizer(model, optimizer)
optimize!(model)
We check that there is no admissible vector $c$ for $m=2$.
is_solved_and_feasible(model)
false
We check that there is an admissible vector $c$ for $m=3$.
m = 3
model = build_c_estimation_problem(σ, a, b, m, forward)
set_optimizer(model, optimizer)
optimize!(model)
is_solved_and_feasible(model)
true
The estimated vector $c$ can be accessed as follows.
c = value.(model[:c])
2-element Vector{Float64}:
1.0
0.06123693145634692
We stress that, when $n$ is larger, the tolerance of the solver might have to be adjusted to ensure that the problem is solved with good precision.
Resolution of the convex nonlinear SDP
Once the weight vector $c$ is estimated, one can solve the convex nonlinear SDP defined above. To do so, we build the problem using build_nonlinear_sdp
and use the Optimization package with the Ipopt solver.
using Optimization, OptimizationMOI
σ_true = [0.8, 1.2] # unknown conductivity
obs = [forward_map(forward, j, σ_true) for j=1:m] # observations
problem = ConvexCalderonProblem(c, a, b, obs, forward)
σ_init = (0.9*b) .* ones(forward.n) # intial guess
prob = build_nonlinear_sdp(problem, σ_init) # container for the nonlinear SDP
optimizer = OptimizationMOI.MOI.OptimizerWithAttributes(
Ipopt.Optimizer,
"print_level" => 0,
"sb" => "yes"
)
sol = solve(prob, optimizer)
σ_hat = sol.u
isapprox(σ_hat, σ_true, rtol=1e-6)
true
This page was generated using Literate.jl.