Skip to content

Commit 9410eda

Browse files
authored
Merge pull request #1335 from JuliaRobotics/21Q3/refac/passthrough
2 parents 6191e29 + 77b83a9 commit 9410eda

File tree

4 files changed

+358
-324
lines changed

4 files changed

+358
-324
lines changed

src/Factors/GenericMarginal.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# agnostic factors for internal use
2+
3+
4+
"""
5+
$(TYPEDEF)
6+
"""
7+
mutable struct GenericMarginal <: AbstractRelativeRoots
8+
Zij::Array{Float64,1}
9+
Cov::Array{Float64,1}
10+
W::Array{Float64,1}
11+
GenericMarginal() = new()
12+
GenericMarginal(a,b,c) = new(a,b,c)
13+
end
14+
15+
16+
sampleFactor(::CalcFactor{<:GenericMarginal},N::Int) = (0,)
17+
18+
mutable struct PackedGenericMarginal <: PackedInferenceType
19+
Zij::Array{Float64,1}
20+
Cov::Array{Float64,1}
21+
W::Array{Float64,1}
22+
PackedGenericMarginal() = new()
23+
PackedGenericMarginal(a,b,c) = new(a,b,c)
24+
end
25+
function convert(::Type{PackedGenericMarginal}, d::GenericMarginal)
26+
return PackedGenericMarginal(d.Zij, d.Cov, d.W)
27+
end
28+
function convert(::Type{GenericMarginal}, d::PackedGenericMarginal)
29+
return GenericMarginal(d.Zij, d.Cov, d.W)
30+
end
31+
32+
# ------------------------------------------------------------

src/IncrementalInference.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ const InMemDFGType = DFG.LightDFG{SolverParams}
413413
include("entities/FactorOperationalMemory.jl")
414414

415415

416-
include("entities/GraphConstraintTypes.jl")
416+
include("Factors/GenericMarginal.jl")
417417
include("entities/OptionalDensities.jl")
418418
include("entities/FactorGradients.jl")
419419

@@ -477,7 +477,8 @@ include("NumericalCalculations.jl")
477477
include("DeconvUtils.jl")
478478
include("ExplicitDiscreteMarginalizations.jl")
479479
include("InferDimensionUtils.jl")
480-
include("ApproxConv.jl")
480+
include("services/EvalFactor.jl")
481+
include("services/ApproxConv.jl")
481482
include("GraphProductOperations.jl")
482483
include("SolveTree.jl")
483484
include("TetherUtils.jl")

src/services/ApproxConv.jl

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
2+
export calcFactorResidual
3+
4+
5+
6+
function approxConvBelief(dfg::AbstractDFG,
7+
fc::DFGFactor,
8+
target::Symbol,
9+
measurement::Tuple=(Vector{Vector{Float64}}(),);
10+
solveKey::Symbol=:default,
11+
N::Int=length(measurement[1]),
12+
skipSolve::Bool=false )
13+
#
14+
v1 = getVariable(dfg, target)
15+
N = N == 0 ? getNumPts(v1, solveKey=solveKey) : N
16+
# points and infoPerCoord
17+
pts, ipc = evalFactor(dfg, fc, v1.label, measurement, solveKey=solveKey, N=N, skipSolve=skipSolve)
18+
19+
len = length(ipc)
20+
mask = 1e-14 .< abs.(ipc)
21+
partl = collect(1:len)[ mask ]
22+
23+
# is the convolution infoPerCoord full or partial
24+
if sum(mask) == len
25+
# not partial
26+
return manikde!(getManifold(getVariable(dfg, target)), pts, partial=nothing)
27+
else
28+
# is partial
29+
return manikde!(getManifold(getVariable(dfg, target)), pts, partial=partl)
30+
end
31+
end
32+
33+
34+
approxConv(w...;kw...) = getPoints( approxConvBelief(w...;kw...), false)
35+
36+
"""
37+
$SIGNATURES
38+
39+
Calculate the sequential series of convolutions in order as listed by `fctLabels`, and starting from the
40+
value already contained in the first variable.
41+
42+
Notes
43+
- `target` must be a variable.
44+
- The ultimate `target` variable must be given to allow path discovery through n-ary factors.
45+
- Fresh starting point will be used if first element in `fctLabels` is a unary `<:AbstractPrior`.
46+
- This function will not change any values in `dfg`, and might have slightly less speed performance to meet this requirement.
47+
- pass in `tfg` to get a recoverable result of all convolutions in the chain.
48+
- `setPPE` and `setPPEmethod` can be used to store PPE information in temporary `tfg`
49+
50+
DevNotes
51+
- TODO strong requirement that this function is super efficient on single factor/variable case!
52+
- FIXME must consolidate with `accumulateFactorMeans`
53+
- TODO `solveKey` not fully wired up everywhere yet
54+
- tfg gets all the solveKeys inside the source `dfg` variables
55+
- TODO add a approxConv on PPE option
56+
- Consolidate with [`accumulateFactorMeans`](@ref), `approxConvBinary`
57+
58+
Related
59+
60+
[`approxDeconv`](@ref), `LightDFG.findShortestPathDijkstra`, [`evalFactor`](@ref)
61+
"""
62+
function approxConvBelief(dfg::AbstractDFG,
63+
from::Symbol,
64+
target::Symbol,
65+
measurement::Tuple=(Vector{Vector{Float64}}(),);
66+
solveKey::Symbol=:default,
67+
N::Int = length(measurement[1]),
68+
tfg::AbstractDFG = initfg(),
69+
setPPEmethod::Union{Nothing, Type{<:AbstractPointParametricEst}}=nothing,
70+
setPPE::Bool= setPPEmethod !== nothing,
71+
path::AbstractVector{Symbol}=Symbol[],
72+
skipSolve::Bool=false )
73+
#
74+
# @assert isVariable(dfg, target) "approxConv(dfg, from, target,...) where `target`=$target must be a variable in `dfg`"
75+
76+
if from in ls(dfg, target)
77+
# direct request
78+
# TODO avoid this allocation for direct cases ( dfg, :x1x2f1, :x2[/:x1] )
79+
path = Symbol[from; target]
80+
varLbls = Symbol[target;]
81+
else
82+
# must first discover shortest factor path in dfg
83+
# TODO DFG only supports LightDFG.findShortestPathDijkstra at the time of writing (DFG v0.10.9)
84+
path = 0 == length(path) ? findShortestPathDijkstra(dfg, from, target) : path
85+
@assert path[1] == from "sanity check failing for shortest path function"
86+
87+
# list of variables
88+
fctMsk = isFactor.(dfg, path)
89+
# which factors in the path
90+
fctLbls = path[fctMsk]
91+
# must still add
92+
varLbls = union(lsf.(dfg, fctLbls)...)
93+
neMsk = exists.(tfg, varLbls) .|> x-> xor(x,true)
94+
# put the non-existing variables into the temporary graph `tfg`
95+
# bring all the solveKeys too
96+
addVariable!.(tfg, getVariable.(dfg, varLbls[neMsk]))
97+
# variables adjacent to the shortest path should be initialized from dfg
98+
setdiff(varLbls, path[xor.(fctMsk,true)]) .|> x->initManual!(tfg, x, getBelief(dfg, x))
99+
end
100+
101+
# find/set the starting point
102+
idxS = 1
103+
pts = if varLbls[1] == from
104+
# starting from a variable
105+
getBelief(dfg, varLbls[1]) |> getPoints
106+
else
107+
# chain would start one later
108+
idxS += 1
109+
# get the factor
110+
fct0 = getFactor(dfg,from)
111+
# get the Matrix{<:Real} of projected points
112+
pts1Bel = approxConvBelief(dfg, fct0, path[2], measurement, solveKey=solveKey, N=N, skipSolve=skipSolve)
113+
if length(path) == 2
114+
return pts1Bel
115+
end
116+
getPoints(pts1Bel)
117+
end
118+
# didn't return early so shift focus to using `tfg` more intensely
119+
initManual!(tfg, varLbls[1], pts)
120+
# use in combination with setPPE and setPPEmethod keyword arguments
121+
ppemethod = setPPEmethod === nothing ? MeanMaxPPE : setPPEmethod
122+
!setPPE ? nothing : setPPE!(tfg, varLbls[1], solveKey, ppemethod)
123+
124+
# do chain of convolutions
125+
for idx in idxS:length(path)
126+
if fctMsk[idx]
127+
# this is a factor path[idx]
128+
fct = getFactor(dfg, path[idx])
129+
addFactor!(tfg, fct)
130+
ptsBel = approxConvBelief(tfg, fct, path[idx+1], solveKey=solveKey, N=N, skipSolve=skipSolve)
131+
initManual!(tfg, path[idx+1], ptsBel)
132+
!setPPE ? nothing : setPPE!(tfg, path[idx+1], solveKey, ppemethod)
133+
end
134+
end
135+
136+
# return target variable values
137+
return getBelief(tfg, target)
138+
end
139+
140+
141+
## ====================================================================================
142+
## TODO better consolidate below with existing functions
143+
## ====================================================================================
144+
145+
146+
147+
148+
# TODO should this be consolidated with regular approxConv?
149+
# TODO, perhaps pass Xi::Vector{DFGVariable} instead?
150+
function approxConvBinary(arr::Vector{Vector{Float64}},
151+
meas::AbstractFactor,
152+
outdims::Int,
153+
fmd::FactorMetadata,
154+
measurement::Tuple=(Vector{Vector{Float64}}(),);
155+
varidx::Int=2,
156+
N::Int=length(arr),
157+
vnds=DFGVariable[],
158+
_slack=nothing )
159+
#
160+
# N = N == 0 ? size(arr,2) : N
161+
pts = [zeros(outdims) for _ in 1:N];
162+
ptsArr = Vector{Vector{Vector{Float64}}}()
163+
push!(ptsArr,arr)
164+
push!(ptsArr,pts)
165+
166+
fmd.arrRef = ptsArr
167+
168+
# TODO consolidate with ccwl??
169+
# FIXME do not divert Mixture for sampling
170+
# cf = _buildCalcFactorMixture(ccwl, fmd, 1, ccwl.measurement, ARR) # TODO perhaps 0 is safer
171+
# FIXME 0, 0, ()
172+
cf = CalcFactor( meas, fmd, 0, 0, (), ptsArr)
173+
174+
measurement = length(measurement[1]) == 0 ? sampleFactor(cf, N) : measurement
175+
# measurement = size(measurement[1],2) == 0 ? sampleFactor(meas, N, fmd, vnds) : measurement
176+
177+
zDim = length(measurement[1][1])
178+
ccw = CommonConvWrapper(meas, ptsArr[varidx], zDim, ptsArr, fmd, varidx=varidx, measurement=measurement) # N=> size(measurement[1],2)
179+
180+
for n in 1:N
181+
ccw.cpt[Threads.threadid()].particleidx = n
182+
_solveCCWNumeric!( ccw, _slack=_slack )
183+
end
184+
return pts
185+
end
186+
187+
188+
189+
"""
190+
$SIGNATURES
191+
192+
Calculate both measured and predicted relative variable values, starting with `from` at zeros up to `to::Symbol`.
193+
194+
Notes
195+
- assume single variable separators only.
196+
"""
197+
function accumulateFactorChain( dfg::AbstractDFG,
198+
from::Symbol,
199+
to::Symbol,
200+
fsyms::Vector{Symbol}=findFactorsBetweenNaive(dfg, from, to);
201+
initval=zeros(size(getVal(dfg, from))))
202+
203+
# get associated variables
204+
svars = union(ls.(dfg, fsyms)...)
205+
206+
# use subgraph copys to do calculations
207+
tfg_meas = buildSubgraph(dfg, [svars;fsyms])
208+
tfg_pred = buildSubgraph(dfg, [svars;fsyms])
209+
210+
# drive variable values manually to ensure no additional stochastics are introduced.
211+
nextvar = from
212+
initManual!(tfg_meas, nextvar, initval)
213+
initManual!(tfg_pred, nextvar, initval)
214+
215+
# nextfct = fsyms[1] # for debugging
216+
for nextfct in fsyms
217+
nextvars = setdiff(ls(tfg_meas,nextfct),[nextvar])
218+
@assert length(nextvars) == 1 "accumulateFactorChain requires each factor pair to separated by a single variable"
219+
nextvar = nextvars[1]
220+
meas, pred = approxDeconv(dfg, nextfct) # solveFactorMeasurements
221+
pts_meas = approxConv(tfg_meas, nextfct, nextvar, (meas,ones(Int,100),collect(1:100)))
222+
pts_pred = approxConv(tfg_pred, nextfct, nextvar, (pred,ones(Int,100),collect(1:100)))
223+
initManual!(tfg_meas, nextvar, pts_meas)
224+
initManual!(tfg_pred, nextvar, pts_pred)
225+
end
226+
return getVal(tfg_meas,nextvar), getVal(tfg_pred,nextvar)
227+
end
228+
229+
230+
231+
232+
"""
233+
$(SIGNATURES)
234+
235+
Compute proposal belief on `vertid` through `fct` representing some constraint in factor graph.
236+
Always full dimension variable node -- partial constraints will only influence subset of variable dimensions.
237+
The remaining dimensions will keep pre-existing variable values.
238+
239+
Notes
240+
- fulldim is true when "rank-deficient" -- TODO swap to false (or even float)
241+
"""
242+
function calcProposalBelief(dfg::AbstractDFG,
243+
fct::DFGFactor,
244+
target::Symbol,
245+
measurement::Tuple=(Vector{Vector{Float64}}(),);
246+
N::Int=length(measurement[1]),
247+
solveKey::Symbol=:default,
248+
dbg::Bool=false )
249+
#
250+
# assuming it is properly initialized TODO
251+
proposal = approxConvBelief(dfg, fct, target, measurement, solveKey=solveKey, N=N)
252+
253+
# return the proposal belief and inferdim, NOTE likely to be changed
254+
return proposal
255+
end
256+
257+
258+
function calcProposalBelief(dfg::AbstractDFG,
259+
fct::DFGFactor{<:CommonConvWrapper{<:PartialPriorPassThrough}},
260+
target::Symbol,
261+
measurement::Tuple=(zeros(0,0),);
262+
N::Int=length(measurement[1]),
263+
solveKey::Symbol=:default,
264+
dbg::Bool=false )
265+
#
266+
267+
# density passed through directly from PartialPriorPassThrough.Z
268+
proposal = getFactorType(fct).Z.densityFnc
269+
270+
# return the proposal belief and inferdim, NOTE likely to be changed
271+
return proposal
272+
end
273+
274+
"""
275+
$SIGNATURES
276+
277+
Compute the proposals of a destination vertex for each of `factors` and place the result
278+
as belief estimates in both `dens` and `partials` respectively.
279+
280+
Notes
281+
- TODO: also return if proposals were "dimension-deficient" (aka ~rank-deficient).
282+
"""
283+
function proposalbeliefs!(dfg::AbstractDFG,
284+
destlbl::Symbol,
285+
factors::AbstractVector{<:DFGFactor},
286+
dens::Vector{<:ManifoldKernelDensity},
287+
# partials::Dict{Any, Vector{ManifoldKernelDensity}}, # TODO change this structure
288+
measurement::Tuple=(Vector{Vector{Float64}}(),);
289+
solveKey::Symbol=:default,
290+
N::Int=maximum([length(getPoints(getBelief(dfg, destlbl, solveKey))); getSolverParams(dfg).N]),
291+
dbg::Bool=false )
292+
#
293+
294+
# populate the full and partial dim containers
295+
inferddimproposal = Vector{Float64}(undef, length(factors))
296+
# get a proposal belief from each factor connected to destlbl
297+
for (count,fct) in enumerate(factors)
298+
# data = getSolverData(fct)
299+
ccwl = _getCCW(fct)
300+
# need way to convey partial information
301+
# determine if evaluation is "dimension-deficient" solvable dimension
302+
inferd = getFactorSolvableDim(dfg, fct, destlbl, solveKey)
303+
# convolve or passthrough to get a new proposal
304+
propBel_ = calcProposalBelief(dfg, fct, destlbl, measurement, N=N, dbg=dbg, solveKey=solveKey)
305+
# partial density
306+
propBel = if isPartial(ccwl)
307+
pardims = _getDimensionsPartial(ccwl)
308+
@assert [getFactorType(fct).partial...] == [pardims...] "partial dims error $(getFactorType(fct).partial) vs $pardims"
309+
AMP.marginal(propBel_, Int[pardims...])
310+
else
311+
propBel_
312+
end
313+
push!(dens, propBel)
314+
inferddimproposal[count] = inferd
315+
end
316+
inferddimproposal
317+
end
318+
# group partial dimension factors by selected dimensions -- i.e. [(1,)], [(1,2),(1,2)], [(2,);(2;)]
319+
320+
321+
322+
323+
#

0 commit comments

Comments
 (0)