A Join Decomposition of a vector $x\in\mathbb R^N$ is a decomposition of the form $x = x_1+\cdots+x_r$, where $x_i\in M_i$ and $M_i\subset \mathbb R^N$ is a given embedded manifold.
For instance, we can approximate a point $p = (1.2, 0.4)\in\mathbb R^2$ by $x=x_1+x_2$, where $x_1,x_2\in S^1$ are points on the circle:
julia> using Manifolds
-julia> p = [1.2, 0.4]
-julia> S = Sphere(1)
-julia> join_res = approx((S, S), p)
-ApproxResult{Float64}
- Components: 2
- Rel. error: 0.0007114699550529457
We access the decomposition as follows.
components(join_res)
-reconstruct(join_res)
<br>
approx(...) is the main frontend for join decomposition. It works in two stages:
- build an initial point
- refine it with the selected solver
approx(model; kwargs...) Solve an already constructed JoinModel.approx(manifolds, target; kwargs...) Build a join from a tuple/vector of component manifolds.approx(M::ProductManifold, target; kwargs...) Use the product-manifold factors as join components.approx(base, r, target; kwargs...) Repeat a single base manifold r times to build a join.approx(base, target; kwargs...) Build a single-component join.
Examples:
julia> target = [1.2, 0.4, -0.3]
-julia> model = JoinModel(Manifolds.Sphere(2), target)
-julia> approx(model; maxiter = 50, verbose = false)
-ApproxResult{Float64}
- Components: 1
- Rel. error: 0.23076923076923075
julia> target = randn(2, 3)
-julia> approx((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)
-CPDResult{Float64}
julia> target = [1.2, 0.4, -0.3]
-julia> approx(Manifolds.Sphere(2), 2, target; verbose = false)
-ApproxResult{Float64}
With dispatch = :auto:
- uniform
Manifolds.Segre components route to cpd(...) - uniform
Manifolds.Tucker components route to btd(...) - otherwise the generic
JoinModel(...) path is used and ApproxResult is returned
You can also force the route explicitly:
dispatch = :genericdispatch = :cpddispatch = :btd
Forced routing is validated:
dispatch = :cpd requires all components to be Manifolds.Segre with identical factor_dimsdispatch = :btd requires all components to be Manifolds.Tucker with identical factor_dims and compatible multilinear rank
For the generic join path:
init = :random Default initializer. Built-in initializer support depends on the component manifolds.solver = :rgd Supported generic-join solver options are:
Other common options:
p0 = nothingmaxiter = 500stepsize = 1.0tol = 1e-6gradient_mode = :riemannianverbose = truevector_transport_method = nothing
Built-in init support by component family:
Sphere: :random, :deterministic, :targetSegre: :random, :deterministicTucker: :random, :tucker, :tucker_diag, :sthosvd- other manifolds:
:random
- Generic joins require every component manifold to embed into the same flattened ambient length as
target. solver = :als is not available for a truly generic JoinModel. ALS may still be available when approx(...) auto-routes to cpd(...) or btd(...).
TensorKitchen.approx — Function
Generic Join Approximation
approx(...) is the main frontend for join decomposition, which works in two stages:
- build an initial point
- refine it with the selected solver
Supported Forms
approx(model; kwargs...) : It is for an already constructed join model (JoinModel(...)) and routes to the generic join solver. Model can be a JoinModel of a tuple of manifolds, a ProductManifold, or a single manifold.- model means an already constructed JoinModel.
- It fully fixes the decomposition structure and target.
approx(model; ...) just solves that model.
Example: If you want to approximate a point on the sphere, you can build a JoinModel and then use approx to refine it.
target = [1.2, 0.4, -0.3]
-model = JoinModel(Manifolds.Sphere(2), target)
-approx(model; maxiter = 100, verbose = false)
-# returns an ApproxResult
approx(manifolds, target; kwargs...) : Builds a generic join model from a tuple of existing manifolds and routes to according to dispatch. Example:
target = randn(2, 3)
-approx((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)
-# This builds a join with 2 copies of Manifolds.Segre((2, 3))".
approx(M::ProductManifold, target; kwargs...) : Uses the factors of a product manifold and route to CPD, BTD, or the generic join solver according to dispatch. - M::ProductManifold means that you already have a product manifold whose factors are the join components.
- approx(M, target; ...) uses those factors directly.
- It is more explicit than
base, because the components are already listed.
Example:
# Example 1
-target = randn(2, 3)
-M = ProductManifold(Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3)))
-approx(M, target; verbose = false)
-# returns an CPDResult
-
-# Example 2
-target = randn(4, 3, 2)
-M = ProductManifold(
- Manifolds.Tucker((4, 3, 2), (2, 2, 2)),
- Manifolds.Tucker((4, 3, 2), (2, 2, 2)),
-)
-approx(M, target; verbose = false)
-# returns an BTDResult
approx(base, r, target; kwargs...) : builds a rank-r Segre join and routes to CPD when base isa Manifolds.Segre and BTD when base isa Manifolds.Tucker unless generic
dispatch is explicitly requested. - base means one manifold template, not yet a full join. - approx(base, r, target; ...) repeats that same manifold r times to build a join. - approx(base, target; ...) builds a one-component join.
target = randn(2, 3)
-approx(Manifolds.Segre((2, 3)), 2, target; verbose = false)
-# returns an CPDResult
approx(base, target; kwargs...) : Builds a single-component generic join and route to the generic join solver. Example:
target = [1.2, 0.4, -0.3]
-approx(Manifolds.Sphere(2), target; verbose = false)
-# returns an ApproxResult
- By default,
approx auto-routes by manifold family:- uniform
Manifolds.Segre summands calls cpd(...) - uniform
Manifolds.Tucker summands calls btd(...) - otherwise calls
JoinModel(...) and returns a ApproxResult
Return Types
- Depending on the manifold family,
approx(...) may return:ApproxResult for the generic join pathCPDResult when auto-routed to cpd(...)BTDResult when auto-routed to btd(...)
Main Options
For the generic join path:
init = :random: Sets the algorithm to find the initial point.solver = :rgd: Sets the algorithm for refinement. Possible options are:rgd (default): Riemannian gradient descentrgd_fixed: Riemannian gradient descent with fixed step sizercg: Riemannian conjugate gradientlbfgs: Limited-memory quasi-Newton
##Notes##
:als is not a solver option for approx(...). However, if approx(...) auto-routes to cpd(...) or btd(...), then those specialized pipelines may support ALS separately.warm_steps and warm_init are not part of the generic approx(...) path. Generic joins start from the selected initializer and then use manifold solvers for refinement.- For generic mixed joins, use manifold solvers such as
:rgd, :rcg, or :lbfgs.
sourceapprox(M::ProductManifold, target; dispatch=:auto, kwargs...)
Use the factors of a product manifold as join components and route to CPD, BTD, or the generic join solver according to dispatch.
sourceapprox(base::Manifolds.Segre, r, target; dispatch=:auto, kwargs...)
Build a rank-r Segre join and route to the CPD pipeline unless generic dispatch is explicitly requested.
sourceapprox(base::Manifolds.Tucker, r, target; dispatch=:auto, kwargs...)
Build a r-block Tucker join and route to the BTD pipeline unless generic dispatch is explicitly requested.
sourceapprox(base::AbstractManifold, r, target; dispatch=:auto, kwargs...)
Fallback rank-r join constructor for non-specialized manifolds. Forced CPD or BTD dispatch is rejected because the base manifold family is not known.
sourceapprox(base::AbstractManifold, target; dispatch=:auto, kwargs...)
Single-component generic approximation fallback. Use this when no CPD/BTD family-specific routing is intended.
sourceTensorKitchen.ApproxResult — Type
ApproxResult{T}
Generic result of approx(manifolds, target) (generic join decomposition).
point: final point on the join manifoldcomponents: extracted component descriptionscost: the value of the optimization objective at the final returned point, that is typically the least-squares objective.rel_error: relative error of the decomposition which is the ratio of the cost to the target norm. is scale-normalized and easier to compare across problemsgrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is typically the Riemannian gradient normiterations: number of iterations usedconverged: whether the decomposition convergedsolver: solver used for the decompositionsolver_info: solver-specific diagnostics/metadata (NamedTuple)
sourceTensorKitchen.reconstruct — Method
reconstruct(res::ApproxResult)
-reconstruct(res::BTDResult)
sourceTensorKitchen.join_product — Function
join_product(base, r) constructs a ProductManifold
Construct rank-r join parameter manifold as a plain ProductManifold. For Manifolds.Segre, uses flattened (Euclidean(1), Sphere, ..., Sphere) factors per component to preserve current CP parameter layout.
sourceTensorKitchen.SegreProduct — Function
SegreProduct(dims, r)
Product manifold Manifolds.Segre(dims) × ... × Manifolds.Segre(dims) (r factors). Each component point uses the Manifolds.Segre layout [[λ], x₁, …, x_d].
source