diff --git a/NEWS.md b/NEWS.md index 978cc919..e0189097 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,9 @@ Listing news on any major breaking changes in DFG. For regular changes, see integrated Github.com project milestones for DFG. # v0.29 +- Breaking: Serialized types finalized toward DFG v1, see #1177 and sub-issues. + Serialization is breaking from v0.28 towards a version to remain stable within v1. + `State.belief` fields and related abstract types are still considered **unstable** and will be finalized in a future releases. - `AbstractPointParametricEst` (`MeanMaxPPE`) and related `PPE` functions are obsolete, see #1133. - Rename filter keyword arguments to `where`-prefixed form: - `solvableFilter` -> `whereSolvable` diff --git a/src/Deprecated.jl b/src/Deprecated.jl index 375f279e..5c7614aa 100644 --- a/src/Deprecated.jl +++ b/src/Deprecated.jl @@ -761,3 +761,40 @@ end @deprecate mergeStorelinks! mergeBlobproviders! @deprecate addBlob! putBlob! @deprecate LinkStore LinkBlobprovider + +""" + $(SIGNATURES) + +!!! warning "Deprecated" + `getSolverParams(dfg)` is deprecated in DFG v0.29 Pass `SolverParams` directly + to `solveTree!()` as a keyword argument instead. +""" +function getSolverParams(dfg::AbstractDFG) + Base.depwarn( + "getSolverParams(dfg) is deprecated. SolverParams will be removed from the DFG object. " * + "Pass SolverParams directly to solveTree!() as a keyword argument instead.", + :getSolverParams, + ) + return dfg.solverParams +end + +# TODO +# The `ref*` accessors use topology-specific lens names while the fields use topology-neutral homotopy names: +# Something like: +# refPrincipalElements(state::State) = state.belief.principal_elements +# refPrincipalForms(state::State) = state.belief.principal_forms +# refTrailingForms(state::State) = state.belief.trailing_forms +# Then IIF defines the lens-specific wrappers with topology dispatch, something like: +# refMeans(state) = refPrincipalElements(state) # RootsOnly view +# refCovariances(state) = refPrincipalForms(state) # RootsOnly view +# refBandwidth(state) = refTrailingForms(state)[1] # LeavesOnly view +# But my (JT) preference is for HomotopyBeliefDFG to contain a neutral homotopy tree of nodes +# and use tree accessors on the node level and not raw references. + +refMeans(state::State) = state.belief.principal_elements +refCovariances(state::State) = state.belief.principal_forms +refWeights(state::State) = state.belief.weights +refPoints(state::State) = state.belief.points +refBandwidth(state::State) = state.belief.trailing_forms[1] +refBandwidths(state::State) = SparseArrays.nonzeros(state.belief.trailing_forms) +getTopologyKind(state::State) = state.belief.topologykind diff --git a/src/DistributedFactorGraphs.jl b/src/DistributedFactorGraphs.jl index 14c93843..4e1e92b3 100644 --- a/src/DistributedFactorGraphs.jl +++ b/src/DistributedFactorGraphs.jl @@ -403,6 +403,7 @@ include("entities/Tags.jl") include("entities/Timestamp.jl") include("entities/Agent_and_Graph.jl") include("entities/Factor.jl") +include("entities/HomotopyDensity.jl") include("entities/State.jl") include("entities/Variable.jl") include("entities/equality.jl") diff --git a/src/Serialization/DFGStructStyles.jl b/src/Serialization/DFGStructStyles.jl index 00da8c3a..470cf493 100644 --- a/src/Serialization/DFGStructStyles.jl +++ b/src/Serialization/DFGStructStyles.jl @@ -53,6 +53,7 @@ function StructUtils.lift(::DFGJSONStyle, ::Type{TimeDateZone}, x::AbstractStrin end #TODO StructUtils v2.7 adds support for StaticArrays, update, test, and remove these overloads if they work as expected +# does not work in v2.7.0 without these overloads. # SArray serialization overloads StructUtils.lower(::DFGJSONStyle, x::SArray) = x @@ -106,3 +107,17 @@ end # function StructUtils.lift(::DFGJSONStyle, ::Type{T}, x::JSON.LazyValue) where T <: Complex # return T(x.re[], x.im[]), nothing # end + +# SparseVector — serialize as struct (n, nzind, nzval fields). +# Must override arraylike since SparseVector <: AbstractVector. +# lower avoids isassigned dispatch going through getindex→zero(eltype). +StructUtils.structlike(::DFGJSONStyle, ::Type{<:SparseVector}) = true +StructUtils.arraylike(::DFGJSONStyle, ::Type{<:SparseVector}) = false + +function StructUtils.lower(::DFGJSONStyle, x::SparseVector) + return ( + n = length(x), + nzind = SparseArrays.nonzeroinds(x), + nzval = SparseArrays.nonzeros(x), + ) +end diff --git a/src/Serialization/StateSerialization.jl b/src/Serialization/StateSerialization.jl index 8001697b..95d2e6ec 100644 --- a/src/Serialization/StateSerialization.jl +++ b/src/Serialization/StateSerialization.jl @@ -131,18 +131,14 @@ function unpackOldState(d) label = Symbol(d.solveKey) !isempty(d.covar) && error("covar field is not supported") if label == :parametric - belief = StoredHomotopyBelief( - RootsOnlyTopology(), - statekind; - means = vals, - shapes = [BW], + belief = HomotopyDensityDFG{T, getPointType(T)}(; + principal_elements = vals, + principal_forms = [BW], ) else - belief = StoredHomotopyBelief( - LeavesOnlyTopology(), - statekind; + belief = HomotopyDensityDFG{T, getPointType(T)}(; points = vals, - bandwidths = [BW], + trailing_forms = sparsevec(Dict(1 => BW)), ) end return State{T, getPointType(T)}(; diff --git a/src/entities/Factor.jl b/src/entities/Factor.jl index 4004a474..106a5813 100644 --- a/src/entities/Factor.jl +++ b/src/entities/Factor.jl @@ -30,7 +30,7 @@ end eliminated::Bool = false potentialused::Bool = false end - +#TODO add `tension` and consider keying as per variable statelabel ##============================================================================== ## Factors ##============================================================================== diff --git a/src/entities/HomotopyDensity.jl b/src/entities/HomotopyDensity.jl new file mode 100644 index 00000000..d1c3742a --- /dev/null +++ b/src/entities/HomotopyDensity.jl @@ -0,0 +1,134 @@ + +##============================================================================== +## Abstract Types +##============================================================================== + +abstract type AbstractStateType{N} end +const StateType = AbstractStateType + +abstract type AbstractHomotopyTopology end +struct DefaultTopologyKind <: AbstractHomotopyTopology end + +function StructUtils.lower(::StructUtils.StructStyle, p::AbstractHomotopyTopology) + return StructUtils.lower(Packed(p)) +end +@choosetype AbstractHomotopyTopology resolvePackedType + +abstract type AbstractDensityForm end +struct DefaultFormKind <: AbstractDensityForm end +function StructUtils.lower(::StructUtils.StructStyle, p::AbstractDensityForm) + return StructUtils.lower(Packed(p)) +end +@choosetype AbstractDensityForm resolvePackedType + +# TBD for best future looking structure here. ignore = true, so no serialization +abstract type AbstractPartialTrait end + +# ============================================================================== +# HomotopyDensityDFG +# ============================================================================== +# @kwarg struct HomotopyReprDFG{ +@defaults struct HomotopyReprDFG{ + T <: StateType, + HT <: AbstractHomotopyTopology, + RF <: AbstractDensityForm, + PT <: Union{Nothing, <:AbstractPartialTrait}, +} + topologykind::HT + formkind::RF + statekind::T + partial::PT = nothing & (ignore = true,) #TODO deprecated field, still needed for AMP. + # partials field needed for AMP v0.15, will be JSON ignored in DFG v0.29, needs better solution by DFG v0.30 +end + +StructUtils.structlike(::StructUtils.StructStyle, ::Type{<:HomotopyReprDFG}) = true + +function resolveHomotopyReprDFGType(lazyobj) + topo = resolveType(lazyobj.topologykind[]) + form = resolveType(lazyobj.formkind[]) + statekind = liftStateKind(lazyobj.statekind[]) + return HomotopyReprDFG{typeof(statekind), topo, form, Nothing} +end +@choosetype HomotopyReprDFG resolveHomotopyReprDFGType + +""" + HomotopyDensityDFG{T <: StateType, P} + +Hybrid belief representation with natural transition between (non)parametric representations. + +**THIS IS IMPORTANT**: Fundamentally related to `HomotopyDensity` definition in AMP.jl. + +!!! warning "Raw Data Container" + `HomotopyDensityDFG` is the raw data schema used for database storage and serialization. + Mutating this structure in-place is discouraged. Rather, construct a new `State` object + and call `addState!` or `mergeState!`. + +Notes: +- These are the internal raw beliefs and need to be viewed through a lens such as +provided by AMP for features like pdf evaluation. +- Allows partials as identified by list of coordinate dimensions via `.reprkind{...L}.partial` + - e.g. legacy `partial = [1;3] or (1,3)` + - When building a partial belief, use full points with necessary information in the specified partial coords. +- Replaces ManellicTree, ManifoldKernelDensity, KernelDensityEstimate, GaussianMixtureModel, PCA, Mixtures + - a.k.a. model order reduction given a topology selection +""" +@kwdef struct HomotopyDensityDFG{T <: StateType, P} + reprkind::HomotopyReprDFG{T} = + HomotopyReprDFG(DefaultTopologyKind(), DefaultFormKind(), T(), nothing) + """A hint for downstream solvers on how to interpret this data (The 'How')""" + topologykind::AbstractHomotopyTopology = DefaultTopologyKind() + + points::Vector{P} = P[] # previously `val` + weights::Vector{Float64} = Float64[] + """ + In model order reduction, PCA, and modal analysis, the terms for the eigenvectors associated with the largest and smallest eigenvalues are commonly: + Major eigenvectors are often called "dominant eigenvectors," or simply "leading modes." In Principal Component Analysis (PCA), these are the "principal components." + Minor eigenvectors are sometimes called "trailing eigenvectors," or "residual modes." In PCA, these correspond to the components with the smallest variance. + """ + principal_coeffs::Vector{Float64} = Vector{Float64}() # FIXME getMajorsLength(reprkind)) + principal_elements::Vector{P} = P[] # previously `val[1]` for Gaussian + principal_forms::Vector{Matrix{Float64}} = Matrix{Float64}[] # previously `covar` existed but was stored in `bw` (hacky) + """ + Store minor eigenvalue details such as leaf bandwidth or reconstruction vectors. + - When lifted for compute efficiency, this field is likely to hold something like PDMats. + - When lowered or for serde, this field is likely to hold Dict{Int, Vector{Float64}}. + """ + trailing_forms::SparseVector{Matrix{Float64}, Int} = spzeros(Matrix{Float64}, 1) #previously `bw` + """ + Geometric points permute field, allows fast binary tree operations and geometric points splits for manellic (ball) trees. + - Geometric split reqs at least 2*(N+1)-1 points -- e.g. when nodes have only right children, points=[1,2,-3]. + """ + structure::SparseVector{Vector{Int}, Int} = spzeros(Vector{Int}, 1) +end + +JSON.omit_empty(::Type{<:HomotopyDensityDFG}) = true + +function HomotopyDensityDFG(T::AbstractStateType) + return HomotopyDensityDFG{typeof(T), getPointType(T)}() +end + +function StructUtils.fielddefaults( + ::StructUtils.StructStyle, + ::Type{HomotopyDensityDFG{T, P}}, +) where {T, P} + return ( + topologykind = DefaultTopologyKind(), + principal_coeffs = Float64[], + principal_elements = P[], + principal_forms = Matrix{Float64}[], + weights = Float64[], + points = P[], + trailing_forms = sparsevec(Int[], Matrix{Float64}[]), + ) +end + +function StructUtils.fieldtags(::StructUtils.StructStyle, ::Type{<:HomotopyDensityDFG}) + return (reprkind = (choosetype = resolveHomotopyReprDFGType,),) +end + +function resolveHomotopyDensityDFGType(lazyobj) + statekind = liftStateKind(lazyobj.reprkind.statekind[]) + return HomotopyDensityDFG{typeof(statekind), getPointType(statekind)} +end + +@choosetype HomotopyDensityDFG resolveHomotopyDensityDFGType diff --git a/src/entities/State.jl b/src/entities/State.jl index 7caccdad..f783728f 100644 --- a/src/entities/State.jl +++ b/src/entities/State.jl @@ -1,164 +1,3 @@ -##============================================================================== -## Abstract Types -##============================================================================== - -abstract type AbstractStateType{N} end -const StateType = AbstractStateType - -# ============================================================================== -# StoredHomotopyBelief -# ============================================================================== -""" - AbstractHomotopyTopology - -Describes the physical layout of the nodes within a `StoredHomotopyBelief`. - -Since all beliefs in the Caesar ecosystem are fundamentally Homotopy densities, -this trait acts as a lightweight dispatch hint (a "Lens Selector"). It indicates -which parts of the tree (Roots vs. Leaves) are currently populated and how they -are wired, without requiring downstream packages to inspect the underlying vectors. - -**Role of the Topology Trait:** -- **DFG:** Determines how to serialize and spatial-index the belief in the database. -- **Visualizers:** Decides how to render the data (e.g., drawing ellipses for roots vs. a point cloud for leaves). -- **IIF/AMP:** Selects the correct mathematical view to construct (e.g., `MvNormal` vs. `ManifoldKernelDensity` vs. a full `HomotopyDensity`). - -!!! note "State, not Strategy" - The trait purely describes the *current physical shape* of the data (e.g., "I currently only have Roots populated"). - It does *not* dictate the solver strategy (e.g., "You must use a parametric solver"). - The math engine is always free to convert or expand the data based on the graph's needs. - -**Extending:** -If the standard tree-based Homotopy model does not fit your specific data layout -or solver requirements, you are encouraged to extend this abstract type with your -own custom topology struct. Alternatively, if you believe your use case represents -a missing core layout, please open an issue to discuss adding it to the -foundational ecosystem. -""" -abstract type AbstractHomotopyTopology end - -# --- 1. The Roots --- -"L1 structural nodes only. No L2 samples. (Schema: `means`, `weights`, `shapes` populated. `points` empty.)" -struct RootsOnlyTopology <: AbstractHomotopyTopology end - -# --- 2. The Leaves --- -"L2 raw samples only. No L1 structure. (Schema: `points`, `bandwidths` populated. `means` empty.)" -struct LeavesOnlyTopology <: AbstractHomotopyTopology end - -# --- 3. The Full Trees --- -"Tree packed in arrays using 2i, 2i+1 math.(Schema: L1 and L2 populated. Parent arrays empty.)" -struct ImplicitTreeTopology <: AbstractHomotopyTopology end - -"Full tree using adjacency lists. (Schema: L1, L2, and Parent arrays fully populated.)" -struct ExplicitTreeTopology <: AbstractHomotopyTopology end - -function StructUtils.lower(::StructUtils.StructStyle, p::AbstractHomotopyTopology) - return StructUtils.lower(Packed(p)) -end -@choosetype AbstractHomotopyTopology resolvePackedType - -""" - StoredHomotopyBelief{T <: StateType, P} - -A multi-resolution "Grove of Trees" representing a manifold belief. -Each tree can be as deep (ExplicitTreeTopology) or as shallow (RootsOnlyTopology) -as the evidence requires, but they all speak the same language of Nodes and Parents. - -These are the internal raw beliefs and need to be viewed through a lens such as -provided by AMP for features like pdf evaluation. Organized into structural -Tree/Branch layers (L1) and empirical Leaf layers (L2). - -!!! warning "Raw Data Container" - `StoredHomotopyBelief` is the raw data schema used for database storage and serialization. - Mutating this structure in-place is discouraged. Rather, construct a new `State` object - and call `addState!` or `mergeState!`. -""" -@kwdef struct StoredHomotopyBelief{T <: StateType, P} - statekind::T = T()# NOTE duplication for serialization and self description. - """A hint for downstream solvers on how to interpret this data (The 'How')""" - topologykind::AbstractHomotopyTopology = LeavesOnlyTopology() - - # L1 Nodes - """ - [Order 0] The relative importance or probability of each node in L1. - """ - weights::Vector{Float64} = Float64[] - """ - [Order 1] The location/center of each node, stored directly on the manifold. - """ - means::Vector{P} = P[] # previously `val[1]` for Gaussian - """ - [Order 2] The spread/curvature of each node (e.g., Covariance or Precision matrix). - """ - shapes::Vector{Matrix{Float64}} = Matrix{Float64}[] # previously `covar` existed but was stored in `bw` (hacky) - - # L2 Nodes - """ - The raw empirical samples on the manifold. Used for KDE and particle representations. - """ - points::Vector{P} = P[] # previously `val` - """ - The second-order bandwidths for the non-parametric points, supports variable bandwidth kernels. - """ - bandwidths::Vector{Matrix{Float64}} = Matrix{Float64}[] #previously `bw` --- - - # --- Topology (The Hierarchy) --- - """ - L1 Internal Topology: mean_parents[i] = j means means[i] is a child of means[j]. A value of 0 indicates a Root node. - """ - mean_parents::Vector{Int} = Int[] - """ - L2-to-L1 Bridge: point_parents[i] = j means points[i] is governed by means[j]. Points are leaves. - """ - point_parents::Vector{Int} = Int[] -end - -JSON.omit_empty(::Type{<:StoredHomotopyBelief}) = true - -function StoredHomotopyBelief(T::AbstractStateType) - return StoredHomotopyBelief{typeof(T), getPointType(T)}(; statekind = T) -end - -function StoredHomotopyBelief(::LeavesOnlyTopology, T::AbstractStateType; kwargs...) - return StoredHomotopyBelief{typeof(T), getPointType(T)}(; - statekind = T, - topologykind = LeavesOnlyTopology(), - bandwidths = [zeros(getDimension(T), getDimension(T))], - kwargs..., - ) -end - -function StoredHomotopyBelief(::RootsOnlyTopology, T::AbstractStateType; kwargs...) - return StoredHomotopyBelief{typeof(T), getPointType(T)}(; - statekind = T, - topologykind = RootsOnlyTopology(), - kwargs..., - ) -end - -function StructUtils.fielddefaults( - ::StructUtils.StructStyle, - ::Type{StoredHomotopyBelief{T, P}}, -) where {T, P} - return ( - statekind = T(), - topologykind = LeavesOnlyTopology(), - means = P[], - shapes = Matrix{Float64}[], - weights = Float64[], - points = P[], - bandwidths = Matrix{Float64}[], - mean_parents = Int[], - point_parents = Int[], - ) -end - -function resolveStoredBeliefType(lazyobj) - statekind = liftStateKind(lazyobj.statekind[]) - return StoredHomotopyBelief{typeof(statekind), getPointType(statekind)} -end - -@choosetype StoredHomotopyBelief resolveStoredBeliefType ##============================================================================== ## State @@ -182,7 +21,7 @@ $(TYPEDFIELDS) """ Generic stored belief for this state. """ - belief::StoredHomotopyBelief{T, P} = StoredHomotopyBelief{T, P}()#; statekind = T()) + belief::HomotopyDensityDFG{T, P} = HomotopyDensityDFG{T, P}()#; statekind = T()) """List of symbols for separator variables for this state, used in variable elimination and inference computations.""" separator::Vector{Symbol} = Symbol[] """False if initial numerical values are not yet available or stored values are not ready for further processing yet.""" @@ -208,7 +47,7 @@ end # ============================================================================== # FUTURE VIEW WRAPPER (Internal DFG Placeholder) # ============================================================================== -# NOTE: The `StoredHomotopyBelief` is currently expressive and fast enough that +# NOTE: The `HomotopyDensityDFG` is currently expressive and fast enough that # DFG does not need to store a resolved view next to it in memory. # # If future profiling requires it, DFG will introduce a verbose View wrapper @@ -217,7 +56,7 @@ end # abstract type AbstractHomotopyBeliefView end # # struct HomotopyBeliefView{T, P, M} <: AbstractHomotopyBeliefView -# stored::StoredHomotopyBelief{T, P} +# stored::HomotopyDensityDFG{T, P} # math_engine::M # Read-only instantiated solver object (e.g., AMP.HomotopyDensity) # end @@ -246,7 +85,7 @@ function StructUtils.fielddefaults( ::Type{State{T, P}}, ) where {T, P} return ( - belief = StoredHomotopyBelief{T, P}(; statekind = T()), + belief = HomotopyDensityDFG{T, P}(), separator = Symbol[], initialized = false, observability = Float64[], @@ -256,14 +95,6 @@ function StructUtils.fielddefaults( ) end -refMeans(state::State) = state.belief.means -refCovariances(state::State) = state.belief.shapes -refWeights(state::State) = state.belief.weights -refPoints(state::State) = state.belief.points -refBandwidth(state::State) = state.belief.bandwidths[1] -refBandwidths(state::State) = state.belief.bandwidths -getTopologyKind(state::State) = state.belief.topologykind - # we can also do somthing like this: function getComponent(state::State, i) return ( diff --git a/src/entities/equality.jl b/src/entities/equality.jl index 72f38f31..5864dfd6 100644 --- a/src/entities/equality.jl +++ b/src/entities/equality.jl @@ -20,7 +20,8 @@ implement compare if needed. const GeneratedCompareUnion = Union{ Agent, Graphroot, - StoredHomotopyBelief, + HomotopyDensityDFG, + HomotopyReprDFG, State, Blobentry, Bloblet, diff --git a/src/services/AbstractDFG.jl b/src/services/AbstractDFG.jl index a429d2ce..e3231f9c 100644 --- a/src/services/AbstractDFG.jl +++ b/src/services/AbstractDFG.jl @@ -24,22 +24,6 @@ function getGraph end """ getGraphLabel(dfg::AbstractDFG) = getLabel(getGraph(dfg)) -""" - $(SIGNATURES) - -!!! warning "Deprecated" - `getSolverParams(dfg)` is deprecated in DFG v0.29 Pass `SolverParams` directly - to `solveTree!()` as a keyword argument instead. -""" -function getSolverParams(dfg::AbstractDFG) - Base.depwarn( - "getSolverParams(dfg) is deprecated. SolverParams will be removed from the DFG object. " * - "Pass SolverParams directly to solveTree!() as a keyword argument instead.", - :getSolverParams, - ) - return dfg.solverParams -end - """ $(SIGNATURES) diff --git a/src/services/compare.jl b/src/services/compare.jl index b8b2d3be..bae84288 100644 --- a/src/services/compare.jl +++ b/src/services/compare.jl @@ -155,9 +155,16 @@ end #Compare State function compare(a::State, b::State) refPoints(a) != refPoints(b) && @debug("val is not equal") === nothing && return false - refBandwidths(a) != refBandwidths(b) && - @debug("bw is not equal") === nothing && + bwa = a.belief.trailing_forms + bwb = b.belief.trailing_forms + SparseArrays.nonzeroinds(bwa) != SparseArrays.nonzeroinds(bwb) && + @debug("bw indices not equal") === nothing && return false + for (va, vb) in zip(SparseArrays.nonzeros(bwa), SparseArrays.nonzeros(bwb)) + !isapprox(va, vb; rtol = 1e-8) && + @debug("bw is not equal") === nothing && + return false + end # a.BayesNetOutVertIDs != b.BayesNetOutVertIDs && # @debug("BayesNetOutVertIDs is not equal") === nothing && # return false diff --git a/src/services/print.jl b/src/services/print.jl index 9dc61a81..dc99213f 100644 --- a/src/services/print.jl +++ b/src/services/print.jl @@ -61,7 +61,7 @@ function printVariable( bws = refBandwidths(vnd) if !isempty(bws) print(ioc, " kde bandwidths: ") - println(ioc, round.(bws[1]; digits = 4)) + println(ioc, round.(first(bws); digits = 4)) end printstyled(ioc, " VNDs: "; bold = true) println(ioc, solk[smsk], 4 < lsolk ? "..." : "") diff --git a/test/compareTests.jl b/test/compareTests.jl index b9698b2c..2aa34790 100644 --- a/test/compareTests.jl +++ b/test/compareTests.jl @@ -6,6 +6,8 @@ using Dates # TestFunctorInferenceType1 # TestCCW1 +DistributedFactorGraphs.@usingDFG true + ## Generated compare functions # State vnd1 = State(:default, TestVariableType1()) diff --git a/test/testSerializingVariables.jl b/test/testSerializingVariables.jl index f4de87b5..cc702fc1 100644 --- a/test/testSerializingVariables.jl +++ b/test/testSerializingVariables.jl @@ -46,27 +46,25 @@ function make_test_variable() end @testset "Serializing Variables" begin - @testset "StoredHomotopyBelief round-trip" begin - bel = DFG.StoredHomotopyBelief(Pose{3}()) + @testset "HomotopyDensityDFG round-trip" begin + bel = DFG.HomotopyDensityDFG(Pose{3}()) push!(bel.points, DFG.getPointIdentity(Pose{3}())) G = DFG.getManifold(Pose{3}()) push!(bel.points, rand(G, ArrayPartition)) jstr = JSON.json(bel; pretty = true, style = DFG.DFGJSONStyle()) - parsed = JSON.parse(jstr, DFG.StoredHomotopyBelief; style = DFG.DFGJSONStyle()) + parsed = JSON.parse(jstr, DFG.HomotopyDensityDFG; style = DFG.DFGJSONStyle()) @test bel == parsed end - @testset "RootsOnlyTopology round-trip" begin + @testset "HomotopyDensityDFG round-trip with roots only" begin dim = DFG.getDimension(Pose{3}()) - bel = DFG.StoredHomotopyBelief( - DFG.RootsOnlyTopology(), - Pose{3}(); - means = [DFG.getPointIdentity(Pose{3}())], - shapes = [diagm(ones(dim))], + bel = DFG.HomotopyDensityDFG{typeof(Pose{3}()), DFG.getPointType(Pose{3}())}(; + principal_elements = [DFG.getPointIdentity(Pose{3}())], + principal_forms = [diagm(ones(dim))], ) jstr = JSON.json(bel; pretty = true, style = DFG.DFGJSONStyle()) - parsed = JSON.parse(jstr, DFG.StoredHomotopyBelief; style = DFG.DFGJSONStyle()) + parsed = JSON.parse(jstr, DFG.HomotopyDensityDFG; style = DFG.DFGJSONStyle()) @test bel == parsed end @@ -100,7 +98,7 @@ end @testset "VariableSummary from VariableDFG" begin v = make_test_variable() - vs = VariableSummary(v) + vs = DFG.VariableSummary(v) @test vs.label == :x1 @test vs.tags == v.tags @test vs.solvable[] == v.solvable[]