
This microsite is associated to the paper
L. Jaulin (2023).
Karush–Kuhn–Tucker conditions to build efficient contractors,
Application to TDoA localization
arXiv, math.NA 2306.09679.
A zip file with the source codes (use Vibes Viewer):
See details below.
from roblib import * # available at https://www.ensta-bretagne.fr/jaulin/roblib.py
def draw3d(x1,x2,f):
fig = figure()
ax = Axes3D(fig,auto_add_to_figure=False)
fig.add_axes(ax)
ax.plot_surface(x1,x2,f)
def drawcontour(x1,x2,f):
contour(x1,x2,f,30)
draw_box_border(a[0]-e,a[0]+e,a[1]-e,a[1]+e,"red",1)
draw_box_border(b[0]-e,b[0]+e,b[1]-e,b[1]+e,"red",1)
def Cardinals(X1,X2,a,b):
def inside(x1,a1,b1):
return (a1<x1<b1) or (b1<x1<a1)
def φ1(x1,a,b):
a1,a2,b1,b2=a[0],a[1],b[0],b[1]
return (a2*abs(x1-b1)-b2*abs(x1-a1))/(abs(x1-b1)-abs(x1-a1))
def φ2(x2,a,b):
return φ1(x2,[a[1],a[0]],[b[1],b[0]])
draw_box_border(X1[0],X1[1],X2[0],X2[1],'darkblue',1)
Card=[[X1[0],X2[0]]]
Card.append([X1[0],X2[1]])
Card.append([X1[1],X2[0]])
Card.append([X1[1],X2[1]])
x2=φ1(X1[0],a,b)
if inside(x2,X2[0],X2[1]): Card.append([X1[0],x2])
x2=φ1(X1[1],a,b)
if inside(x2,X2[0],X2[1]): Card.append([X1[1],x2])
x1=φ2(X2[0],a,b)
if inside(x1,X1[0],X1[1]): Card.append([x1,X2[0]])
x1=φ2(X2[1],a,b)
if inside(x1,X1[0],X1[1]): Card.append([x1,X2[1]])
for P in Card:
draw_box_border(P[0]-e,P[0]+e,P[1]-e,P[1]+e,"green",1)
e=0.02
a=[-1,-3]
b=[2,2]
x1,x2 = meshgrid(arange(-5,5,0.01), arange(-5,5,0.01))
dxa1=x1-a[0]
dxa2=x2-a[1]
dxb1=x1-b[0]
dxb2=x2-b[1]
f=sqrt(dxa1**2+dxa2**2)-sqrt(dxb1**2+dxb2**2)
drawcontour(x1,x2,f)
Cardinals([-1.2,4],[-4.1,1.2],a,b)
from vibes import vibes
import numpy as np
from codac import *
class CtcTDoAFwdKKT(Ctc):
def __init__(C,a,b):
Ctc.__init__(C,3)
C.a,C.b=a,b
def contract(C,X):
def φ1(x1,a,b):
a1,a2,b1,b2=a[0],a[1],b[0],b[1]
X1=Interval(x1,x1)
return (a2*abs(X1-b1)-b2*abs(X1-a1))/(abs(X1-b1)-abs(X1-a1))
def φ2(x2,a,b): return φ1(x2,[a[1],a[0]],[b[1],b[0]])
def f(x1,x2):
return sqrt((x1-a[0])**2+(x2-a[1])**2)-sqrt((x1-b[0])**2+(x2-b[1])**2)
if X.is_empty(): return IntervalVector.empty(3)
a,b=C.a,C.b
X1,X2=X[0],X[1]
F=[f(X1.lb(),X2.lb()),f(X1.lb(),X2.ub()),f(X1.ub(),X2.lb()),f(X1.ub(),X2.ub())]
F=Interval(np.min(F),np.max(F))
for x1 in [X1.lb(),X1.ub()]:
if not(X2.is_disjoint(φ1(x1,a,b))): F=F|f(x1,φ1(x1,a,b))
for x2 in [X2.lb(),X2.ub()]:
if not(X1.is_disjoint(φ2(x2,a,b))): F=F|f(φ2(x2,a,b),x2)
X[2]=X[2]&F
return IntervalVector([X[0],X[1],X[2]])
class SepTDoA_Fwd_ab(Sep):
def __init__(S,a,b,Y):
Sep.__init__(S,2)
S.Y=Y
S.Cfwd=CtcTDoAFwdKKT(a,b)
def separate(S,Xin,Xout):
X=Xin|Xout
Z=IntervalVector([X[0],X[1],Interval(-oo,oo)])
S.Cfwd.contract(Z)
if Z[2].is_subset(S.Y):
Xin=IntervalVector.empty(2)
return (Xin,Xout)
if Z[2].is_disjoint(S.Y):
Xout=IntervalVector.empty(2)
return (Xin,Xout)
return Xin,Xout
vibes.beginDrawing()
X=IntervalVector([[-15,15],[-15,15]])
a,b=[-1,-2],[2,3]
S=SepTDoA_Fwd_ab(a,b,Interval(3,5))
SIVIA(X,S,0.01)
vibes.endDrawing()
from vibes import vibes
import numpy as np
from codac import *
from ctclib import draw_tiny_box
class CtcTDoAFwdClassic(Ctc):
def __init__(C,a,b):
Ctc.__init__(C,3)
a1,a2,b1,b2=a[0],a[1],b[0],b[1]
_g="sqrt((x1-a1)^2+(x2-a2)^2)-sqrt((x1-b1)^2+(x2-b2)^2)-x3"
_g=_g.replace("a1",str(a1)).replace("a2",str(a2)).replace("b1",str(b1)).replace("b2",str(b2))
C.Cg=CtcFwdBwd(Function('x1','x2','x3',_g))
def contract(C,X):
if X.is_empty(): return IntervalVector.empty(3)
X[2]=Interval(-oo,oo)
C.Cg.contract(X)
return X
class CtcTDoABwdclassic(Ctc):
def __init__(C,a,b):
Ctc.__init__(C,3)
C.a,C.b=a,b
def contract(C,X):
if X.is_empty(): return IntervalVector.empty(3)
a1,a2,b1,b2=C.a[0],C.a[1],C.b[0],C.b[1]
_g="sqrt((x1-a1)^2+(x2-a2)^2)-sqrt((x1-b1)^2+(x2-b2)^2)"
_g=_g.replace("a1",str(a1)).replace("a2",str(a2)).replace("b1",str(b1)).replace("b2",str(b2))
g=Function('x1','x2',_g)
Cg=CtcFwdBwd(g,X[2])
Zout=IntervalVector([X[0],X[1]])
Cg.contract(Zout)
X[0],X[1]=Zout[0],Zout[1]
return IntervalVector([X[0],X[1],X[2]])
class SepTDoA_Fwd_ab(Sep):
def __init__(S,a,b,Y,Cfwd):
Sep.__init__(S,2)
S.Y=Y
S.Cfwd=Cfwd(a,b)
def separate(S,Xin,Xout):
X=Xin|Xout
Z=IntervalVector([X[0],X[1],Interval(-oo,oo)])
S.Cfwd.contract(Z)
if Z[2].is_subset(S.Y):
Xin=IntervalVector.empty(2)
return (Xin,Xout)
if Z[2].is_disjoint(S.Y):
Xout=IntervalVector.empty(2)
return (Xin,Xout)
return Xin,Xout
vibes.beginDrawing()
X=IntervalVector([[-15,15],[-15,15]])
a,b=[-1,-2],[2,3]
Y=Interval(3,5)
S=SepTDoA_Fwd_ab(a,b,Y,CtcTDoAFwdClassic)
SIVIA(X,S,0.01)
vibes.endDrawing()
from vibes import vibes
import numpy as np
from codac import *
class SepAct(Sep): # Return the separator S = C \bullet Sy
def __init__(S,Cfwd,Cback,Sy):
Sep.__init__(S,2)
S.Cfwd=Cfwd
S.Cback=Cback
S.Sy=Sy
def separate(S,Xin,Xout):
I=range(0,S.Sy.nb_var)
X=Xin|Xout
Z=[IntervalVector([X[0],X[1],Interval(-oo,oo)]) for i in I]
for i in I: S.Cfwd[i].contract(Z[i])
Y=IntervalVector([Z[i][2] for i in I])
Yin,Yout=Y.copy(),Y.copy()
S.Sy.separate(Yin,Yout)
Zin= [IntervalVector([Z[i][0],Z[i][1],Yin [i]]) for i in I]
Zout=[IntervalVector([Z[i][0],Z[i][1],Yout[i]]) for i in I]
def Cbwd(Z):
X=IntervalVector([Interval(-oo,oo),Interval(-oo,oo)])
for i in I:
S.Cback[i].contract(Z[i])
X=X&IntervalVector([Z[i][0],Z[i][1]])
return X
Xin=Cbwd(Zin)
Xout=Cbwd(Zout)
return Xin,Xout
def sivia_TDoA_Sy(X,M,Sy,epsi):
I = range(0,Sy.nb_var)
Cfwd=[CtcTDoAFwdKKT(M[i],M[i+1]) for i in I]
Cback=[CtcTDoABwdclassic(M[i],M[i+1]) for i in I]
S=SepAct(Cfwd,Cback,Sy)
SIVIA(X,S,0.01)
vibes.beginDrawing()
X=IntervalVector([[-15,15],[-15,15]])
Sy1=SepFwdBwd(Function("y1","y2","(y1-2)^2+(y2-1)^2-1"),Interval(-oo,0))
Sy2=SepFwdBwd(Function("y1","y2","(y1+1)^2+(y2+2)^2-1"),Interval(-oo,0))
Sy=Sy1|Sy2
M=[[-1,-2],[2,3],[4,1]]
sivia_TDoA_Sy(X,M,Sy,0.01)
vibes.endDrawing()
from vibes import vibes
import numpy as np
from codac import *
from ctclib import draw_tiny_box
from ctctdoaKKT import CtcTDoAFwdKKT
from ctctdoaDisk import SepAct,drawMicro,sivia_TDoA_Sy
col0={SetValue.IN: "#FFCC00[#FFAAFFEE]", SetValue.OUT: "#AADDFF[#AAFFFFEE]",SetValue.UNKNOWN: "yellow[white]"}
col6={SetValue.IN: "#CCAA33[#FF00FF22]", SetValue.OUT: "#AADDFF[#AAFFFFEE]",SetValue.UNKNOWN: "yellow[white]"}
col5={SetValue.IN: "#CCAA66[#FF00DD55]", SetValue.OUT: "transparent",SetValue.UNKNOWN: "yellow[white]"}
col4={SetValue.IN: "#CCAA88[#FF00BB77]", SetValue.OUT: "transparent",SetValue.UNKNOWN: "yellow[white]"}
col3={SetValue.IN: "#CCAAAA[#FF0099AA]", SetValue.OUT: "transparent",SetValue.UNKNOWN: "yellow[white]"}
col2={SetValue.IN: "#CCAACC[#FF0033CC]", SetValue.OUT: "transparent",SetValue.UNKNOWN: "yellow[white]"}
col1={SetValue.IN: "#FF0000FF[orange]", SetValue.OUT: "transparent",SetValue.UNKNOWN: "yellow[white]"}
def confidence_regions_abc(X,M):
Col=[col6,col5,col4,col3,col2,col1]
z=[16,8,4,2,1,0.5]
imax=len(Col)
for i in range(0,imax):
_g="(y1-2)^2+(y2-1)^2-z".replace("z",str(z[i]))
Sy=SepFwdBwd(Function("y1","y2",_g),Interval(-oo,0))
sivia_TDoA_Sy(X,M,Sy,Col[i],0.01)
drawMicro(M)
def disk_regions_abc():
X=IntervalVector([[-10,10],[-10,10]])
Col=[col6,col5,col4,col3,col2,col1]
z=[16,8,4,2,1,0.5]
imax=len(Col)
for i in range(0,imax):
_g="(y1-2)^2+(y2-1)^2-z".replace("z",str(z[i]))
Sy=SepFwdBwd(Function("y1","y2",_g),Interval(-oo,0))
SIVIA(X,Sy,0.01,color_map=Col[i])
vibes.beginDrawing()
X=IntervalVector([[-15,15],[-15,15]])
confidence_regions_abc(X,[[-1,-2],[2,3],[4,1]])
#disk_regions_abc()
vibes.endDrawing()