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.4359141301703327Now, 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.cpd — Function
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. Forsolver = :als, this usesTuckerInit; 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 whensolver = :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 descentrgd_fixed: Riemannian gradient descent with fixed step sizercg: Riemannian conjugate gradientals: 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 withManifolds.Segresummands.
verbose = true: Enables progress output.nonnegative::Bool = false: Nonnegative CPD option to be selected by the user. (same asnncpd)pullback_eps = 1e-8: Regularization parameter for pullback-style nonnegative geometries.
Notes
solver = :alsdoes not use manifold geometry. In that case:geometrymust be:canonicalgradient_modeis ignored except for validation
:squaring_metricand:softplus_metricrequirenonnegative = true.- When
nonnegative = true,cpd(...)routes tonncpd(...). In that route:- if
solver != :alsandgeometryis left at:canonical, the effective geometry becomes:softplus_metric - if
stepsizeis left at1.0, the effective default becomes0.01 - if
init = :tucker, the effective initializer becomes:alswarm
- if
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.4359141301703327TensorKitchen.nncpd — Function
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_metricDefault and usually the safest choice.geometry=:squaring_metricUses a regularized pullback-inspired geometry induced by the squaring chart.geometry=:canonicalPlain nonnegative CP coordinates without the pullback-style manifold geometry. This is the natural choice withsolver=: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.3765605093526155TensorKitchen.CPDResult — Type
CPDResult{T}Result of a Canonical Polyadic Decomposition.
Stores the decoded CP representation together with solver diagnostics:
components: rank-one tensor componentsweights: component weightsfactors: factor matricescost: final objective function value at the returned solutionrel_error: final relative reconstruction errorgrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is the Riemannian gradient normiterations: number of refinement iterationsconverged: whether the solver reported convergencesolver: solver optimization method used to produce the resultsolver_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), andnncp_pullback_eps(NNCP).
TensorKitchen.weights — Method
weights(r::CPDResult)Return the CP component weights stored in a CPD result.
TensorKitchen.factors — Method
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.
TensorKitchen.reconstruct — Method
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).