CPD

Here is how to approximate a tensor A by a CPD of rank r.

julia> using TensorKitchen
julia> A = randn(20, 15, 10)
julia> r = 35
julia> res = cpd(A, r)
CPDResult{Float64}
  Order:        3
  Dimensions:   (20, 15, 10)
  Rank:         35
  Rel. error:   0.4359141301703327

Now, res contains a CP approximation of the 3-way tensor A,

\[\hat A = \sum_{i=1}^r \lambda_i\, a_i \otimes b_i \otimes c_i\]

It approximates A with relative error about 0.436.

We access the decomposition as follows.

λ = weights(res)
U = factors(res)

Here, U is a triple of matrices $(A,B,C)$, where the columns of $A$ are the $a_i$ and so on. These are called factor matrices.

We get the whole reconstructed tensor by

 = reconstruct(res)

CPD Docs

TensorKitchen.cpdFunction
cpd(A, r; kwargs...)

Computes a rank-r CP approximation of A in two steps: (1) the first step finds an initial point; (2) the second step refines the initial point. Returns a CPDResult. If r is omitted, uses the smallest tensor mode as a heuristic rank.

Main Options

  • init = :auto: Sets the algorithm to find the initial point. Possible options are:
    • :auto: Uses a default CPD initializer. For solver = :als, this uses TuckerInit; otherwise, it uses an ALS warm start.
    • :alswarm: Runs ALS first and uses the result as the initial point for refinement.
    • customized initial point:
      • :tucker (default when solver = :als): Uses a default Tucker initializer.
      • :random: Uses a random initial point.
      • :hosvd: Uses a HOSVD 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
    • als: Alternating Least Squares

Extended Options

  • p0 = nothing: Explicit initial point. If provided, it overrides the default initial point.
  • :alswarm: ALS warm start option.
    • warm_init = TuckerInit(): Before finding the warm start initial point, this sets the good starting point for ALS.
    • warm_steps = 500: Once finding the best initial point from warm_init, it runs this many ALS iterations to refine the initial point.
  • maxiter = 500: Maximum number of Riemannian gradient descent iterations.
  • stepsize = 1.0: Initial step size for line search in Riemannian gradient descent.
  • tol = 1e-6: Convergence tolerance.
  • gradient_mode = :riemannian: Gradient rule for manifold solvers.
    • If the model has a direct rgrad, it uses that.
    • Otherwise it computes egrad and projects it to the tangent space.
    • This behavior is in src/solvers/abstract.jl (line 289).
  • geometry = :canonical: Sets the geometry of the manifold. Possible options are:
    • :canonical: Standard CPD parameterization with the usual Euclidean factors and canonical Riemannian gradient handling. Best default for general unconstrained CPD.
    • :squaring_metric: Nonnegative geometry based on squared latent coordinates. Enforces nonnegativity indirectly, but can become ill-conditioned near zero.
    • :softplus_metric: Nonnegative geometry uses a regularized pullback-inspired geometry induced by the softplus chart. Smoother and usually more stable near zero than :squaring_metric.
    • :native: Native CP manifold geometry using the model’s intrinsic CP/Segre representation not for nonnegative=true. Best for structured join layouts with Manifolds.Segre summands.
  • verbose = true: Enables progress output.
  • nonnegative::Bool = false: Nonnegative CPD option to be selected by the user. (same as nncpd)
  • pullback_eps = 1e-8: Regularization parameter for pullback-style nonnegative geometries.

Notes

  • solver = :als does not use manifold geometry. In that case:
    • geometry must be :canonical
    • gradient_mode is ignored except for validation
  • :squaring_metric and :softplus_metric require nonnegative = true.
  • When nonnegative = true, cpd(...) routes to nncpd(...). In that route:
    • if solver != :als and geometry is left at :canonical, the effective geometry becomes :softplus_metric
    • if stepsize is left at 1.0, the effective default becomes 0.01
    • if init = :tucker, the effective initializer becomes :alswarm

Example

julia> A = randn(20, 15, 10); r = 35
julia> res = cpd(A, r)
CPDResult{Float64}
  Order:        3
  Dimensions:   (20, 15, 10)
  Rank:         35
  Rel. error:   0.4359141301703327
source
TensorKitchen.nncpdFunction
 nncpd(A, r; kwargs...)

Computes a nonnegative rank-r CP approximation of A in two steps: (1) the first step finds an initial point; (2) the second step refines the initial point. Returns a CPDResult. If r is omitted, uses the smallest tensor mode as a heuristic rank. cpd(A, r; nonnegative=true, ...) routes here and adopts the same effective defaults.

Options

The options are the same as for cpd.

Geometry guide:

  • geometry=:softplus_metric Default and usually the safest choice.
  • geometry=:squaring_metric Uses a regularized pullback-inspired geometry induced by the squaring chart.
  • geometry=:canonical Plain nonnegative CP coordinates without the pullback-style manifold geometry. This is the natural choice with solver=:als.

Example

julia> A = randn(20, 15, 10); r = 35;
julia> B = abs.(A)
julia> nncpd(B, r)
CPDResult{Float64}
  Order:        3
  Dimensions:   (20, 15, 10)
  Rank:         35
  Rel. error:   0.3765605093526155
source
TensorKitchen.CPDResultType
CPDResult{T}

Result of a Canonical Polyadic Decomposition.

Stores the decoded CP representation together with solver diagnostics:

  • components: rank-one tensor components
  • weights: component weights
  • factors: factor matrices
  • cost: final objective function value at the returned solution
  • rel_error: final relative reconstruction error
  • grad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is the Riemannian gradient norm
  • iterations: number of refinement iterations
  • converged: whether the solver reported convergence
  • solver: solver optimization method used to produce the result
  • solver_info: solver-specific diagnostics/metadata (NamedTuple). Typical keys include:
    • initial_stepsize_eff (RGD), memory_size (LBFGS), cautious_update (LBFGS),
    • initial_scale, linesearch, has_preconditioner (LBFGS), and nncp_pullback_eps (NNCP).
source
TensorKitchen.factorsMethod
factors(res::CPDResult)

Return the CP factor matrices of res as a vector [U₁, U₂, ..., U_N], where each U_m has size size(A, m) × rank.

source
TensorKitchen.reconstructMethod
reconstruct(res::CPDResult)

Reconstruct the dense tensor represented by a CP decomposition result.

For a rank-R CPD result, this returns sum(weights(res)[k] * u_1k ⊗ ... ⊗ u_Nk for k = 1:R).

source