Skip to content

Commit 5c464be

Browse files
committed
towards partial IPC on relative
1 parent db7ff84 commit 5c464be

File tree

7 files changed

+101
-58
lines changed

7 files changed

+101
-58
lines changed

src/ApproxConv.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,7 @@ function evalPotentialSpecific( Xi::AbstractVector{<:DFGVariable},
399399
mani = getManifold(getVariableType(Xi[sfidx]))
400400

401401
# perform the numeric solutions on the indicated elements
402-
# error("ccwl.xDim=$(ccwl.xDim)")
403402
# FIXME consider repeat solve as workaround for inflation off-zero
404-
# @info "EVALSPEC END" ccwl.varidx string(ccwl.params) string(allelements)
405403
computeAcrossHypothesis!( ccwl, allelements, activehypo, certainidx,
406404
sfidx, maxlen, mani, spreadNH=spreadNH,
407405
inflateCycles=inflateCycles, skipSolve=skipSolve,

src/CalcFactor.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ function calcFactorResidualTemporary( fct::AbstractRelative,
114114
measurement::Tuple,
115115
varTypes::Tuple,
116116
pts::Tuple;
117-
tfg::AbstractDFG = initfg() )
117+
tfg::AbstractDFG = initfg(),
118+
_blockRecursion::Bool=false )
118119
#
119120

120121
# build a new temporary graph
121-
_, _dfgfct = _buildGraphByFactorAndTypes!(fct, varTypes, pts, dfg=tfg)
122+
_, _dfgfct = _buildGraphByFactorAndTypes!(fct, varTypes, pts, dfg=tfg, _blockRecursion=_blockRecursion)
122123

123124
# get a fresh measurement if needed
124125
_measurement = if length(measurement) != 0

src/Deprecated.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ end
2525
## Deprecate code below before v0.27
2626
##==============================================================================
2727

28+
@deprecate calcPerturbationFromVariable( fgc::FactorGradientsCached!, fromVar::Int, infoPerCoord::AbstractVector;tol::Real=0.02*fgc._h ) calcPerturbationFromVariable(fgc, [fromVar => infoPerCoord;], tol=tol )
29+
2830
@deprecate findRelatedFromPotential(w...;kw...) (calcProposalBelief(w...;kw...),)
2931

3032
# function generateNullhypoEntropy( val::AbstractMatrix{<:Real},

src/FactorGraph.jl

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ function calcZDim(cf::CalcFactor{T}) where {T <: AbstractFactor}
605605
M = getManifold(cf.factor)
606606
return manifold_dimension(M)
607607
catch
608-
@warn "no method getManifold(::$T), calcZDim will attempt legacy length(sample) method instead"
608+
@warn "no method getManifold(::$(string(T))), calcZDim will attempt legacy length(sample) method instead"
609609
end
610610
end
611611

@@ -626,22 +626,23 @@ function prepgenericconvolution(Xi::Vector{<:DFGVariable},
626626
multihypo::Union{Nothing, Distributions.Categorical}=nothing,
627627
nullhypo::Real=0.0,
628628
threadmodel=MultiThreaded,
629-
inflation::Real=0.0 ) where {T <: FunctorInferenceType}
629+
inflation::Real=0.0,
630+
_blockRecursion::Bool=false ) where {T <: AbstractFactor}
630631
#
631632
pttypes = getVariableType.(Xi) .|> getPointType
632633
PointType = 0 < length(pttypes) ? pttypes[1] : Vector{Float64}
633634
# FIXME stop using Any, see #1321
634-
varParams = Vector{Vector{Any}}()
635-
maxlen, sfidx, mani = prepareparamsarray!(varParams, Xi, nothing, 0) # Nothing for init.
635+
varParamsAll = Vector{Vector{Any}}()
636+
maxlen, sfidx, mani = prepareparamsarray!(varParamsAll, Xi, nothing, 0) # Nothing for init.
636637

637638
# standard factor metadata
638639
sflbl = 0==length(Xi) ? :null : getLabel(Xi[end])
639-
fmd = FactorMetadata(Xi, getLabel.(Xi), varParams, sflbl, nothing)
640+
fmd = FactorMetadata(Xi, getLabel.(Xi), varParamsAll, sflbl, nothing)
640641

641642
# create a temporary CalcFactor object for extracting the first sample
642643
# TODO, deprecate this: guess measurement points type
643644
# MeasType = Vector{Float64} # FIXME use `usrfnc` to get this information instead
644-
_cf = CalcFactor( usrfnc, fmd, 0, 1, nothing, varParams) # (Vector{MeasType}(),)
645+
_cf = CalcFactor( usrfnc, fmd, 0, 1, nothing, varParamsAll) # (Vector{MeasType}(),)
645646

646647
# get a measurement sample
647648
meas_single = sampleFactor(_cf, 1)
@@ -662,19 +663,25 @@ function prepgenericconvolution(Xi::Vector{<:DFGVariable},
662663
varTypes::Vector{DataType} = typeof.(getVariableType.(Xi))
663664
gradients = nothing
664665
# prepare new cached gradient lambdas (attempt)
665-
# try
666-
# measurement = tuple(((x->x[1]).(meas_single))...)
667-
# pts = tuple(((x->x[1]).(varParams))...)
668-
# gradients = FactorGradientsCached!(usrfnc, varTypes, measurement, pts);
669-
# catch e
670-
# @warn "Unable to create measurements and gradients for $usrfnc during prep of CCW, falling back on no-partial information assumption."
671-
# end
666+
try
667+
if (!_blockRecursion) && usrfnc isa AbstractRelative
668+
# take first value from each measurement-tuple-element
669+
measurement_ = map(x->x[1], meas_single)
670+
# take the first value from each variable param
671+
pts_ = map(x->x[1], varParamsAll)
672+
# FIXME, only using first meas and params values at this time...
673+
# NOTE, must block recurions here, since FGC uses this function to calculate numerical gradients on a temp fg.
674+
gradients = FactorGradientsCached!(usrfnc, tuple(varTypes...), measurement_, tuple(pts_...), _blockRecursion=true);
675+
end
676+
catch e
677+
@warn "Unable to create measurements and gradients for $usrfnc during prep of CCW, falling back on no-partial information assumption."
678+
end
672679

673680
ccw = CommonConvWrapper(
674681
usrfnc,
675682
PointType[],
676683
zdim,
677-
varParams,
684+
varParamsAll,
678685
fmd,
679686
specialzDim = hasfield(T, :zDim),
680687
partial = ispartl,
@@ -707,15 +714,16 @@ function getDefaultFactorData(dfg::AbstractDFG,
707714
potentialused::Bool = false,
708715
edgeIDs = Int[],
709716
solveInProgress = 0,
710-
inflation::Real=getSolverParams(dfg).inflation ) where T <: FunctorInferenceType
717+
inflation::Real=getSolverParams(dfg).inflation,
718+
_blockRecursion::Bool=false ) where T <: FunctorInferenceType
711719
#
712720

713721
# prepare multihypo particulars
714722
# storeMH::Vector{Float64} = multihypo == nothing ? Float64[] : [multihypo...]
715723
mhcat, nh = parseusermultihypo(multihypo, nullhypo)
716724

717725
# allocate temporary state for convolutional operations (not stored)
718-
ccw = prepgenericconvolution(Xi, usrfnc, multihypo=mhcat, nullhypo=nh, threadmodel=threadmodel, inflation=inflation)
726+
ccw = prepgenericconvolution(Xi, usrfnc, multihypo=mhcat, nullhypo=nh, threadmodel=threadmodel, inflation=inflation, _blockRecursion=_blockRecursion)
719727

720728
# and the factor data itself
721729
return FunctionNodeData{typeof(ccw)}(eliminated, potentialused, edgeIDs, ccw, multihypo, ccw.certainhypo, nullhypo, solveInProgress, inflation)
@@ -1188,7 +1196,8 @@ function DFG.addFactor!(dfg::AbstractDFG,
11881196
threadmodel=SingleThreaded,
11891197
suppressChecks::Bool=false,
11901198
inflation::Real=getSolverParams(dfg).inflation,
1191-
namestring::Symbol = assembleFactorName(dfg, Xi) )
1199+
namestring::Symbol = assembleFactorName(dfg, Xi),
1200+
_blockRecursion::Bool=false )
11921201
#
11931202
# depcrecation
11941203

@@ -1199,7 +1208,8 @@ function DFG.addFactor!(dfg::AbstractDFG,
11991208
multihypo=multihypo,
12001209
nullhypo=nullhypo,
12011210
threadmodel=threadmodel,
1202-
inflation=inflation)
1211+
inflation=inflation,
1212+
_blockRecursion=_blockRecursion)
12031213
newFactor = DFGFactor(Symbol(namestring),
12041214
varOrderLabels,
12051215
solverData;

src/SolverUtilities.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ function _buildGraphByFactorAndTypes!(fct::AbstractFactor,
165165
_allVars::AbstractVector{Symbol} = sortDFG(ls(dfg, destPattern)),
166166
currLabel::Symbol = 0 < length(_allVars) ? _allVars[end] : Symbol(destPrefix, 0),
167167
currNumber::Integer = reverse(match(r"\d+", reverse(string(currLabel))).match) |> x->parse(Int,x),
168-
graphinit::Bool = false )
168+
graphinit::Bool = false,
169+
_blockRecursion::Bool=false )
169170
#
170171

171172
# TODO generalize beyond binary
@@ -178,7 +179,7 @@ function _buildGraphByFactorAndTypes!(fct::AbstractFactor,
178179
((0 < length(pts)) && (pts[s_] isa Nothing)) ? nothing : initManual!(dfg, vars[s_], [pts[s_],], solveKey, bw=ones(getDimension(vTyp)))
179180
end
180181
# if newFactor then add the factor on vars, else assume only one existing factor between vars
181-
_dfgfct = newFactor ? addFactor!(dfg, vars, fct, graphinit=graphinit) : getFactor(dfg, intersect((ls.(dfg, vars))...)[1] )
182+
_dfgfct = newFactor ? addFactor!(dfg, vars, fct, graphinit=graphinit, _blockRecursion=_blockRecursion) : getFactor(dfg, intersect((ls.(dfg, vars))...)[1] )
182183

183184
return dfg, _dfgfct
184185
end

src/services/FactorGradients.jl

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ function _prepFactorGradientLambdas(fct::Union{<:AbstractRelativeMinimize,<:Abst
1111
varTypes::Tuple,
1212
pts::Tuple;
1313
tfg::AbstractDFG = initfg(),
14+
_blockRecursion::Bool=true,
1415
# gradients relative to coords requires
15-
slack_resid = calcFactorResidualTemporary(fct, measurement, varTypes, pts, tfg=tfg),
16+
slack_resid = calcFactorResidualTemporary(fct, measurement, varTypes, pts, tfg=tfg, _blockRecursion=_blockRecursion),
1617
# numerical diff perturbation size
1718
h::Real=1e-4 )
1819
#
@@ -76,14 +77,15 @@ function FactorGradientsCached!(fct::Union{<:AbstractRelativeMinimize, <:Abstrac
7677
varTypes::Tuple,
7778
meas_single::Tuple,
7879
pts::Tuple;
79-
h::Real=1e-4 )
80+
h::Real=1e-4,
81+
_blockRecursion::Bool=true )
8082
#
8183
# working memory location for computations
8284
tfg = initfg()
8385

8486
# permanent location for points and later reference
8587
# generate the necessary lambdas
86-
J__, λ_fncs, λ_sizes, slack_resid = _prepFactorGradientLambdas(fct, meas_single, varTypes, pts; tfg=tfg, h=1e-4);
88+
J__, λ_fncs, λ_sizes, slack_resid = _prepFactorGradientLambdas(fct, meas_single, varTypes, pts; tfg=tfg, _blockRecursion=_blockRecursion, h=1e-4);
8789

8890
# get the one factor in tfg
8991
fctsyms = lsf(tfg)
@@ -106,6 +108,12 @@ end
106108
getCoordSizes(fgc::FactorGradientsCached!) = fgc._coord_sizes
107109

108110

111+
_setFGCSlack!(fgc::FactorGradientsCached!{F}, slack) where F = _setPointsMani!(fgc.slack_residual, slack)
112+
113+
function _setFGCSlack!(fgc::FactorGradientsCached!{F,S}, slack::Number) where {F,S<:Number}
114+
fgc.slack_residual = slack
115+
end
116+
109117
function (fgc::FactorGradientsCached!)(meas_pts...)
110118
# separate the measurements (forst) from the variable points (rest)
111119
lenm = length(fgc.measurement)
@@ -123,7 +131,8 @@ function (fgc::FactorGradientsCached!)(meas_pts...)
123131
varTypes = tuple(getVariableType.(getVariable.(fgc._tfg, getVariableOrder(fgc.dfgfct)))...)
124132
new_slack = calcFactorResidualTemporary(fct, measurement, varTypes, pts; tfg=fgc._tfg)
125133
# TODO make sure slack_residual is properly wired up with all the lambda functions as expected
126-
_setPointsMani!(fgc.slack_residual, new_slack)
134+
_setFGCSlack!(fgc, new_slack)
135+
# _setPointsMani!(fgc.slack_residual, new_slack)
127136

128137
# set new points in preparation for new gradient calculation
129138
for (s,pt) in enumerate(meas_pts[(lenm+1):end])
@@ -174,8 +183,8 @@ end
174183
"""
175184
$SIGNATURES
176185
177-
Return a tuple of infoPerCoord vectors that result from input `fromVar::Int`'s `infoPerCoord`.
178-
For example, a binary `LinearRelative` factor has a one to one influence from the input to the one other variable.
186+
Return a tuple of infoPerCoord vectors that result from input variables as vector of `::Pair`, i.e. `fromVar::Int => infoPerCoord`.
187+
For example, a binary `LinearRelative` factor has a one-to-one influence from the input to the one other variable.
179188
180189
Notes
181190
@@ -197,7 +206,7 @@ fgc = FactorGradientsCached!(fct, varTypes, measurement, pts);
197206
fgc(measurement..., pts...)
198207
199208
# check the perturbation influence through gradients on factor
200-
ret = calcPerturbationFromVariable(fgc, 1, [1;1])
209+
ret = calcPerturbationFromVariable(fgc, [1=>[1;1]])
201210
202211
@assert isapprox(ret[2], [1;1])
203212
```
@@ -210,20 +219,23 @@ Related
210219
[`FactorGradientsCached!`](@ref), [`checkGradientsToleranceMask`](@ref)
211220
"""
212221
function calcPerturbationFromVariable(fgc::FactorGradientsCached!,
213-
fromVar::Int,
214-
infoPerCoord::AbstractVector;
222+
from_var_ipc::AbstractVector{<:Pair};
215223
tol::Real=0.02*fgc._h )
216224
#
217225
blkszs = getCoordSizes(fgc)
218-
@assert blkszs[fromVar] == length(infoPerCoord) "Expecting incoming length(infoPerCoord) to equal the block size for variable $fromVar, as per factor used to construct the FactorGradientsCached!: $(getFactorType(fgc.dfgfct))"
219226
# assume projection through pp-factor from first to second variable
220227
# ipc values from first variable belief, and zero for second
221228
ipcAll = zeros(sum(blkszs))
222-
223-
# nextVar = minimum([fromVar+1; length(blkszs)])
224-
curr_b = sum(blkszs[1:(fromVar-1)]) + 1
225-
curr_e = sum(blkszs[1:fromVar])
226-
ipcAll[curr_b:curr_e] .= infoPerCoord
229+
230+
# set any incoming infoPerCoord values
231+
for (fromVar, infoPC) in from_var_ipc
232+
# check on sizes with print warning
233+
(blkszs[fromVar] == length(infoPC)) ? nothing : @warn("Expecting incoming length(infoPerCoord) to equal the block size for variable $fromVar, as per factor used to construct the FactorGradientsCached!: $(getFactorType(fgc.dfgfct))")
234+
# get range of interest
235+
curr_b = sum(blkszs[1:(fromVar-1)]) + 1
236+
curr_e = sum(blkszs[1:fromVar])
237+
ipcAll[curr_b:curr_e] .= infoPC
238+
end
227239

228240
# clamp gradients below numerical solver resolution
229241
mask = checkGradientsToleranceMask(fgc, tol=tol)
@@ -251,4 +263,5 @@ function calcPerturbationFromVariable(fgc::FactorGradientsCached!,
251263
end
252264

253265

266+
254267
#

test/testpartialconstraint.jl

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
using Test
33
using IncrementalInference
44
# going to introduce two new constraint mutable structs
5-
import IncrementalInference: getSample
65
using Statistics
76
using TensorCast
7+
using Manifolds
88

9+
import IncrementalInference: getManifold, getSample
910

1011
##
1112

@@ -27,6 +28,8 @@ mutable struct DevelopPartialPairwise <: AbstractRelativeMinimize
2728
partial::Tuple
2829
DevelopPartialPairwise(x::Distribution) = new(x, (2,))
2930
end
31+
getManifold(::IIF.InstanceType{DevelopPartialPairwise}) = TranslationGroup(1)
32+
3033
getSample(cf::CalcFactor{<:DevelopPartialPairwise}, N::Int=1) = ([rand(cf.factor.x, 1)[:] for _ in 1:N], )
3134

3235
function (dp::CalcFactor{<:DevelopPartialPairwise})(meas,
@@ -93,7 +96,7 @@ pts_ = approxConv(fg, getLabel(f2), :x1, N=N)
9396
end
9497

9598

96-
@testset "check that partials are received through convolutions" begin
99+
@testset "check that partials are received through convolutions of prior" begin
97100
##
98101

99102
# check that a partial belief is obtained
@@ -105,13 +108,6 @@ X1_ = approxConvBelief(fg, :x1f2, :x1)
105108
end
106109

107110

108-
@testset "test partial info per coord through relative convolution (conditional)" begin
109-
110-
@test_broken false
111-
112-
end
113-
114-
115111
@testset "test solving of factor graph" begin
116112
##
117113

@@ -128,30 +124,52 @@ end
128124
# plotKDE(getBelief(fg, :x1),levels=3)
129125

130126

131-
132127
## partial relative gradient and graph
133128

129+
v2 = addVariable!(fg,:x2,ContinuousEuclid{2},N=N)
130+
134131
dpp = DevelopPartialPairwise(Normal(10.0, 1.0))
132+
f3 = addFactor!(fg,[:x1;:x2],dpp)
133+
134+
dp2 = DevelopPartial( Normal(-20.0, 1.0), (1,) )
135+
f4 = addFactor!(fg,[:x2;], dp2, graphinit=false)
135136

136-
# measurement = ([10.0;], )
137-
# pts = ([0;0.0], [0;10.0])
138-
# gradients = FactorGradientsCached!(dpp, (ContinuousEuclid{2}, ContinuousEuclid{2}), measurement, pts);
137+
doautoinit!(fg, :x2)
139138

140139
##
141140

141+
@testset "test partial info per coord through relative convolution (conditional)" begin
142+
##
142143

143-
v2 = addVariable!(fg,:x2,ContinuousEuclid{2},N=N)
144+
one_meas = ([10.0;], )
145+
pts = ([0;0.0], [0;10.0])
146+
gradients = FactorGradientsCached!(dpp, (ContinuousEuclid{2}, ContinuousEuclid{2}), one_meas, pts);
144147

148+
##
145149

146-
f3 = addFactor!(fg,[:x1;:x2],dpp)
150+
# check that the gradients can be calculated
151+
J = gradients(one_meas..., pts...)
147152

153+
@test size(J) == (4,4)
154+
@test norm(J - [0 0 0 0; 0 0 0 1; 0 0 0 0; 0 1 0 0] ) < 1e-4
148155

149-
dp2 = DevelopPartial( Normal(-20.0, 1.0), (1,) )
150-
f4 = addFactor!(fg,[:x2;], dp2, graphinit=false)
151-
doautoinit!(fg, :x2)
156+
## check perturbation logic
157+
158+
prtb = calcPerturbationFromVariable(gradients, [1=>[1;1]])
159+
160+
# self variation is taken as 0 at this time
161+
@test isapprox( prtb[1], [0;0] )
162+
# variable 1 influences 2 only through partial dimension 2 (as per DevelopPartialPairwise)
163+
@test isapprox( prtb[2], [0;1] )
152164

153-
# drawGraph(fg, show=true)
165+
## test evaluation through the convolution operation withing a factor graph
154166

167+
# add relative IPC calculation inside evalFactor
168+
bel = approxConvBelief(fg, getLabel(f3), :x2)
169+
@test_broken isPartial(bel)
170+
171+
##
172+
end
155173

156174
##
157175

0 commit comments

Comments
 (0)