Join Decomposition

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>

Generic Join Approximation

approx(...) is the main frontend for join decomposition. It works in two stages:

  1. build an initial point
  2. refine it with the selected solver

Supported Forms

  • 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}

Routing

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 = :generic
  • dispatch = :cpd
  • dispatch = :btd

Forced routing is validated:

  • dispatch = :cpd requires all components to be Manifolds.Segre with identical factor_dims
  • dispatch = :btd requires all components to be Manifolds.Tucker with identical factor_dims and compatible multilinear rank

Generic Join Options

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:
    • :rgd
    • :rgd_fixed
    • :rcg
    • :lbfgs

Other common options:

  • p0 = nothing
  • maxiter = 500
  • stepsize = 1.0
  • tol = 1e-6
  • gradient_mode = :riemannian
  • verbose = true
  • vector_transport_method = nothing

Built-in init support by component family:

  • Sphere: :random, :deterministic, :target
  • Segre: :random, :deterministic
  • Tucker: :random, :tucker, :tucker_diag, :sthosvd
  • other manifolds: :random

Notes

  • 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(...).

Join Decomposition Docs

TensorKitchen.approxFunction

Generic Join Approximation

approx(...) is the main frontend for join decomposition, which works in two stages:

  1. build an initial point
  2. 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 path
    • CPDResult 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 descent
    • rgd_fixed: Riemannian gradient descent with fixed step size
    • rcg: Riemannian conjugate gradient
    • lbfgs: 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 random initial point and then use manifold solvers for refinement.
  • For generic mixed joins, use manifold solvers such as :rgd, :rcg, or :lbfgs.
source
approx(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.

source
approx(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.

source
approx(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.

source
approx(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.

source
approx(base::AbstractManifold, target; dispatch=:auto, kwargs...)

Single-component generic approximation fallback. Use this when no CPD/BTD family-specific routing is intended.

source
TensorKitchen.ApproxResultType
ApproxResult{T}

Generic result of approx(manifolds, target) (generic join decomposition).

  • point: final point on the join manifold
  • components: extracted component descriptions
  • cost: 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 problems
  • grad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is typically the Riemannian gradient norm
  • iterations: number of iterations used
  • converged: whether the decomposition converged
  • solver: solver used for the decomposition
  • solver_info: solver-specific diagnostics/metadata (NamedTuple)
source
TensorKitchen.reconstructMethod
reconstruct(res::ApproxResult)

Reconstruct the dense ambient object represented by a generic join approximation result by summing its component tensors.

source
TensorKitchen.join_productFunction
join_product(base, r)

Decides how to expand base into r components:

  • Manifolds.Segre → flattened (Euclidean(1), Sphere, ...) × r (one λ + spheres per rank-1).
  • Manifolds.Tucker / ProductManifold → repeat each factor r times.
  • Generic manifold → ProductManifold(base, base, ..., base).
source
TensorKitchen.SegreProductFunction
SegreProduct(dims, r)
  • Product manifold Manifolds.Segre(dims) × ... × Manifolds.Segre(dims) (r factors).
  • Each component point uses the Manifolds.Segre layout [[λ], x₁, …, x_d].
  • SegreProduct is used to build a join model for CPD.
source