diff --git a/README.md b/README.md
index bedb15a..f8a04cb 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
---
-The package is currently at a pre-alpha stage.
+The package is currently an early version and will be updated frequently in the near future.
The implementation is based on combining algebraic algorithms like ALS (see, e.g., the [textbook by Kolda and Ballard](https://users.wfu.edu/ballard/pdfs/tensor_textbook.pdf)) and Riemannian optimization from [Manopt.jl](https://manoptjl.org/stable/).
@@ -30,9 +30,9 @@ See [PIPELINE.md](docs/src/PIPELINE.md) for the current execution flow.
The next updates will include
+- Handling of swamps/plateaus in the optimization step
- Documentation
- Improved User Interface
-- ProgressMeter
- GPU Support
- LL1 Decomposition (3-way specialized BTD)
- Symmetric CP / Waring Decomposition
@@ -77,6 +77,12 @@ We get the whole reconstructed tensor by
 = reconstruct(res)
```
+Per default, `cpd` finds the initial point by running ALS and then refines it using Riemannian optimization. To use ALS-only one can set the `:solver` flag:
+```julia
+cpd(A, r; solver = :als)
+````
+For a detailed overview on all the options see the documentation.
+
## Tucker Decomposition
@@ -194,34 +200,10 @@ reconstruct(join_res)
```
-## References
-
-#### General Tensor Decomposition
-
-- **Tensor decompositions (CP, Tucker):** T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," *SIAM Review*, vol. 51, no. 3, pp. 455–500, 2009.
-
-#### Tucker Methods
-
-- **HOSVD:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1253–1278, 2000.
-- **ST-HOSVD:** N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," *SIAM J. Sci. Comput.*, vol. 34, no. 2, pp. A1027–A1052, 2012.
-- **HOOI:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R_1,R_2,...,R_N) approximation of higher-order tensors," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1324–1342, 2000.
-
-#### Block and Structured Models (BTD / LL1)
-
-- **Block-term decomposition (BTD):** L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1022–1032, 2008.
-- L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1033–1066, 2008.
-- **BTD-ALS:** L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," *SIAM Journal on Matrix Analysis and Applications*, vol. 30, no. 3, pp. 1067–1083, 2008. [PDF](http://dimitri.nion.free.fr/Publications/Revues/DeLatNion_TensorBlock3.pdf).
-
-#### Join decompositions
-
-- **Conditioning of join decompositions:** P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," *SIAM Journal on Matrix Analysis and Applications*, vol. 39, no. 1, pp. 287–309, 2018. [arXiv:1611.08117 (PDF)](https://arxiv.org/pdf/1611.08117).
-
-#### Riemannian Optimization and Julia Ecosystem
-- **Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation:** P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," *SIAM Journal on Optimization*, vol. 28, no. 3, pp. 2435–2465, 2018. [arXiv:1709.00033 (PDF)](https://arxiv.org/pdf/1709.00033).
-- **Riemannian optimization:** P.-A. Absil, R. Mahony, and R. Sepulchre, *Optimization Algorithms on Matrix Manifolds*. Princeton University Press, 2008.
-- **Julia manifold optimization ecosystem:** R. Bergmann *et al.*, [ManifoldsBase.jl](https://github.com/JuliaManifolds/ManifoldsBase.jl), [Manifolds.jl](https://github.com/JuliaManifolds/Manifolds.jl), and [Manopt.jl](https://manoptjl.org).
+## Usage of AI
+AI coding assitants have been used to support the implementation process. All implemented functions have been designed and reviewed by humans. The documentation was written by humans.
[docs-img]: https://img.shields.io/badge/docs-online-blue.svg
diff --git a/docs/PIPELINE/index.html b/docs/PIPELINE/index.html
index 4b4c3ac..ab26047 100644
--- a/docs/PIPELINE/index.html
+++ b/docs/PIPELINE/index.html
@@ -1,2 +1,2 @@
-
A block term decomposition (BTD) with r blocks writes
\[\hat A = \sum_{i=1}^r A_i,\]
where each block $A_i$ is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.
To compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use
A block term decomposition (BTD) with r blocks writes
\[\hat A = \sum_{i=1}^r A_i,\]
where each block $A_i$ is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.
To compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use
Block-term decomposition (sum of Tucker blocks); default pipeline is BTDHOSVDMultistartInit(candidates=64, screening_steps=10, block_maxiter=12) followed by ALSwarm(500), Riemannian gradient descent, and a final BTD-ALS polish step.
Result of block-term decomposition (btd). Same shape as ApproxResult; each component is a Tucker block (.kind === :Tucker, .core, .factors, .tensor), with optional lightweight solver_info.
Blockwise tangent-subspace descent specialized to BTD. Each block update uses the projected Tucker-block tangent direction and accepts it only when a block Armijo decrease condition is satisfied.
Blockwise tangent-subspace descent specialized to BTD. Each block update uses the projected Tucker-block tangent direction and accepts it only when a block Armijo decrease condition is satisfied.
btd(A, blocks, ranks; kwargs...) returns a BTDResult
Computes a block-term decomposition of A with blocks Tucker blocks, each with multilinear rank ranks. The solver first finds an initial point, then refines it. Returns a BTDResult.
Main Options
init = :auto: Sets the algorithm to find the initial point. Possible options are:
:auto: Uses a default BTD initializer. For solver = :als, this uses BTDHOSVDMultistartInit; otherwise, it uses an ALS warm start.
:alswarm: Runs ALS first and uses the result as the initial point for manifold solver refinement.
custom initializer objects, e.g. BTDHOSVDMultistartInit(...).
solver = :rgd: Sets the algorithm for refinement. Possible options are:
:rgd (default): Riemannian gradient descent.
:als: Alternating least squares.
:rcg: Riemannian conjugate gradient.
:lbfgs: Limited-memory quasi-Newton refinement.
Extended Options
init_point = nothing: Explicit initial point. If provided, it overrides the default initial point.
warm_init = BTDHOSVDMultistartInit(...): Searches for initial points using HOSVD for :alswarm, optionally screens them with short ALS runs, and returns the lowest-cost candidate.
warm_steps = 200: Once finding the best initial point, it runs this many ALS iterations to refine the initial point.
warm_block_method = :hooi or :sthosvd: Block update method used during warm start.
warm_block_maxiter = 20: Maximum number of inner iterations for each block update during warm start.
warm_rel_error_gate = 5e-2: Skips manifold refinement if the warm-start error is above this threshold.
maxiter = 500: Maximum number of Riemannian gradient descent iterations.
stepsize = 0.01: Initial step size for line search in Riemannian gradient descent.
tol = 1e-6: Convergence tolerance.
gradient_mode = :riemannian: rgrad can be directly applied 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).
verbose = true: Enables progress output.
block_method = :hooi or :sthosvd: Block update method used for manifold solvers.
block_maxiter = 30: Maximum number of inner block-update iterations for manifold solvers.
btd_als_polish_maxiter = nothing: Number of final ALS polishing iterations. If nothing, an automatic budget is selected.
These settings are a robustness/quality feature for BTD-ALS. They are not used for manifold solvers.
max_stagnation_restarts = 1: More retries after a bad stagnated ALS pass. Higher values can improve solution quality, but increases runtime.
stagnation_rel_error = 1e-4: This is a “bad final error” cutoff, not an improvement threshold. Lower value means restarts trigger more easily.
restart_candidates = 24: It usually improves chance of finding a better basin, but cost grows roughly linearly.
restart_screening_steps = 5: It uses this many quick ALS steps to screen restart candidates.
restart_block_maxiter = 20: Inner block-update limit during restart screening for BTD-ALS.
restart_seed = nothing: Optional random seed for restart generation for BTD-ALS.
Example
julia> using Random
+julia> Random.seed!(0)
+julia> A = randn(20, 15, 10); blocks = 10; ranks = (5, 4, 3)
+julia> res = btd(A, blocks, ranks; verbose = false)
+BTDResult{Float64}
+ Blocks: 10
+ Rel. error: 0.2625821087015455
+ `);
+ }
+ }
+
+ //url param checking
+ function checkURLForSearch() {
+ const urlParams = new URLSearchParams(window.location.search);
+ const searchQuery = urlParams.get("q");
+ const filterParam = urlParams.get("filter");
+
+ // Set the selected filter if present in URL
+ if (filterParam) {
+ selected_filter = filterParam.toLowerCase();
+ }
+
+ // Trigger input event if there's a search query to perform the search
+ if (searchQuery) {
+ $(".documenter-search-input").val(searchQuery).trigger("input");
+ }
+ }
+ setTimeout(checkURLForSearch, 100);
+
+ /**
+ * Make the modal filter html
+ *
+ * @returns string
+ */
+ function make_modal_body_filters() {
+ let str = filters
+ .map((val) => {
+ if (selected_filter == val.toLowerCase()) {
+ return `${val}`;
+ } else {
+ return `${val}`;
+ }
+ })
+ .join("");
+
+ return `
+
+ Filters:
+ ${str}
+
`;
+ }
+}
+
+function waitUntilSearchIndexAvailable() {
+ // It is possible that the documenter.js script runs before the page
+ // has finished loading and documenterSearchIndex gets defined.
+ // So we need to wait until the search index actually loads before setting
+ // up all the search-related stuff.
+ if (
+ typeof documenterSearchIndex !== "undefined" &&
+ typeof $ !== "undefined"
+ ) {
+ runSearchMainCode();
+ } else {
+ console.warn("Search Index or jQuery not available, waiting");
+ setTimeout(waitUntilSearchIndexAvailable, 100);
+ }
+}
+
+// The actual entry point to the search code
+waitUntilSearchIndexAvailable();
+
+})
+////////////////////////////////////////////////////////////////////////////////
+require(['jquery'], function($) {
+
+// Modal settings dialog
+$(document).ready(function () {
+ var settings = $("#documenter-settings");
+ $("#documenter-settings-button").click(function () {
+ settings.toggleClass("is-active");
+ });
+ // Close the dialog if X is clicked
+ $("#documenter-settings button.delete").click(function () {
+ settings.removeClass("is-active");
+ });
+ // Close dialog if ESC is pressed
+ $(document).keyup(function (e) {
+ if (e.keyCode == 27) settings.removeClass("is-active");
+ });
+});
+
+})
+////////////////////////////////////////////////////////////////////////////////
+require(['jquery'], function($) {
+
+$(document).ready(function () {
+ let search_modal_header = `
+
+
A block term decomposition (BTD) with r blocks writes
\[\hat A = \sum_{i=1}^r A_i,\]
where each block $A_i$ is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.
To compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use
btd(A, blocks, ranks; kwargs...) returns a BTDResult
Computes a block-term decomposition of A with blocks Tucker blocks, each with multilinear rank ranks. The solver first finds an initial point, then refines it. Returns a BTDResult.
Main Options
init = :auto: Sets the algorithm to find the initial point. Possible options are:
:auto: Uses a default BTD initializer. For solver = :als, this uses BTDHOSVDMultistartInit; otherwise, it uses an ALS warm start.
:alswarm: Runs ALS first and uses the result as the initial point for manifold solver refinement.
custom initializer objects, e.g. BTDHOSVDMultistartInit(...).
solver = :rgd: Sets the algorithm for refinement. Possible options are:
:rgd (default): Riemannian gradient descent.
:als: Alternating least squares.
:rcg: Riemannian conjugate gradient.
:lbfgs: Limited-memory quasi-Newton refinement.
Extended Options
init_point = nothing: Explicit initial point. If provided, it overrides the default initial point.
warm_init = BTDHOSVDMultistartInit(...): Searches for initial points using HOSVD for :alswarm, optionally screens them with short ALS runs, and returns the lowest-cost candidate.
warm_steps = 200: Once finding the best initial point, it runs this many ALS iterations to refine the initial point.
warm_block_method = :hooi or :sthosvd: Block update method used during warm start.
warm_block_maxiter = 20: Maximum number of inner iterations for each block update during warm start.
warm_rel_error_gate = 5e-2: Skips manifold refinement if the warm-start error is above this threshold.
maxiter = 500: Maximum number of Riemannian gradient descent iterations.
stepsize = 0.01: Initial step size for line search in Riemannian gradient descent.
tol = 1e-6: Convergence tolerance.
gradient_mode = :riemannian: rgrad can be directly applied 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).
verbose = true: Enables progress output.
block_method = :hooi or :sthosvd: Block update method used for manifold solvers.
block_maxiter = 30: Maximum number of inner block-update iterations for manifold solvers.
btd_als_polish_maxiter = nothing: Number of final ALS polishing iterations. If nothing, an automatic budget is selected.
These settings are a robustness/quality feature for BTD-ALS. They are not used for manifold solvers.
max_stagnation_restarts = 1: More retries after a bad stagnated ALS pass. Higher values can improve solution quality, but increases runtime.
stagnation_rel_error = 1e-4: This is a “bad final error” cutoff, not an improvement threshold. Lower value means restarts trigger more easily.
restart_candidates = 24: It usually improves chance of finding a better basin, but cost grows roughly linearly.
restart_screening_steps = 5: It uses this many quick ALS steps to screen restart candidates.
restart_block_maxiter = 20: Inner block-update limit during restart screening for BTD-ALS.
restart_seed = nothing: Optional random seed for restart generation for BTD-ALS.
Example
julia> using Random
+julia> Random.seed!(0)
+julia> A = randn(20, 15, 10); blocks = 10; ranks = (5, 4, 3)
+julia> res = btd(A, blocks, ranks; verbose = false)
+BTDResult{Float64}
+ Blocks: 10
+ Rel. error: 0.2625821087015455
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
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.
The implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.
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
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.
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.
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.
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 the selected initializer and then use manifold solvers for refinement.
For generic mixed joins, use manifold solvers such as :rgd, :rcg, or :lbfgs.
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
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.
Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.
HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.
ST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.
HOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.
Block-term decomposition (BTD): L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.
L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.
BTD-ALS: L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.
Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).
Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).
Riemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.
This document was generated with Documenter.jl version 1.17.0 on Wednesday 6 May 2026. Using Julia version 1.12.0.
diff --git a/docs/build/search_index.js b/docs/build/search_index.js
new file mode 100644
index 0000000..4d9e21c
--- /dev/null
+++ b/docs/build/search_index.js
@@ -0,0 +1,3 @@
+var documenterSearchIndex = {"docs":
+[{"category":"section","location":"references/#References","page":"References","text":"","title":"References"},{"category":"section","location":"references/#General-Tensor-Decomposition","page":"References","text":"Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, \"Tensor decompositions and applications,\" SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.","title":"General Tensor Decomposition"},{"category":"section","location":"references/#Tucker-Methods","page":"References","text":"HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"A multilinear singular value decomposition,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.\nST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, \"A new truncation strategy for the higher-order singular value decomposition,\" SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.\nHOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.","title":"Tucker Methods"},{"category":"section","location":"references/#Block-and-Structured-Models-(BTD-/-LL1)","page":"References","text":"Block-term decomposition (BTD): L. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.\nL. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.\nBTD-ALS: L. De Lathauwer and D. Nion, \"Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms,\" SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.","title":"Block and Structured Models (BTD / LL1)"},{"category":"section","location":"references/#Join-decompositions","page":"References","text":"Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, \"The condition number of join decompositions,\" SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).","title":"Join decompositions"},{"category":"section","location":"references/#Riemannian-Optimization-and-Julia-Ecosystem","page":"References","text":"Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, \"A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem,\" SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).\nRiemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.\nJulia manifold optimization ecosystem: R. Bergmann et al., ManifoldsBase.jl, Manifolds.jl, and Manopt.jl.","title":"Riemannian Optimization and Julia Ecosystem"},{"category":"section","location":"PIPELINE/#TensorKitchen-Pipeline","page":"Pipeline","text":"This document explains how public APIs route into models, solvers, and result converters.","title":"TensorKitchen Pipeline"},{"category":"section","location":"PIPELINE/#Public-entry-points","page":"Pipeline","text":"cpd(A, r; ...) -> CPDResult\nnncpd(A, r; ...) -> CPDResult\nbtd(A, blocks, ranks; ...) -> BTDResult\ntucker(A, ranks; method=...) -> TuckerResult\napprox(...) -> ApproxResult or auto-routed CPDResult/BTDResult","title":"Public entry points"},{"category":"section","location":"PIPELINE/#Default-behavior-(quick-reference)","page":"Pipeline","text":"cpd(A, r):\ninit = :alswarm\nsolver = :rgd\nnncpd(A, r):\ninit = :alswarm\nsolver = :rgd\nbtd(A, blocks, ranks):\ninit = :alswarm\nwarm_steps = 200\nwarm_init = BTDHOSVDMultistartInit(candidates=64, screening_steps=10, block_maxiter=12)\nwarm_rel_error_gate = nothing (run manifold refinement by default; set e.g. 5e-2 to short-circuit on poor warm starts)\nsolver = :rgd\nfinal BTD-ALS polish enabled by default for non-ALS solvers\nmax_stagnation_restarts = 1 (retry with stronger multistart when ALS fit-change stalls at high rel-error)\ntucker(A, ranks):\nmethod = :sthosvd\napprox(model::JoinModel):\ninit = :alswarm\nwarm_steps = 500\nsolver = :rgd","title":"Default behavior (quick reference)"},{"category":"section","location":"PIPELINE/#Core-execution-architecture","page":"Pipeline","text":"Most optimization APIs share this core pattern:\n\nBuild a model (JoinModel + backend)\nCall _solve_model(...)\nConvert to a public result struct\n\n_solve_model lives in src/solvers/solve_dispatch.jl and is the common symbol-to-solver dispatch layer (:rgd, :rcg, :lbfgs, :als, :btd_tsd).","title":"Core execution architecture"},{"category":"section","location":"PIPELINE/#API-flows","page":"Pipeline","text":"","title":"API flows"},{"category":"section","location":"PIPELINE/#CPD-(cpd,-nncpd)","page":"Pipeline","text":"cpd(A, r; ...):\n\nBuild JoinModel(A, r; geometry=...) with CPDBackend\nNormalize/validate options (solver, geometry, gradient_mode, normalization policy)\nSolve through _solve_model(...)\nOptionally run nonnegative ALS polishing (for selected nonnegative paths)\nConvert to CPDResult\n\nNotes:\n\n:als means CP-ALS.\nManifold solvers (:rgd, :rgd_fixed, :rcg, :lbfgs) share dispatch with other pipelines.","title":"CPD (cpd, nncpd)"},{"category":"section","location":"PIPELINE/#BTD-(btd)","page":"Pipeline","text":"btd(A, blocks, ranks; ...):\n\nBuild a uniform Tucker family via TuckerJoin(...)\nWrap as JoinModel with BTDBackend\nChoose effective initializer:\nsolver == :als: use requested init directly (default multistart)\nsolver != :als: use BTDALSWarmStartInit(...) so first-order methods start from a good BTD-ALS warm point\nIf the warm-start rel-error exceeds warm_rel_error_gate, return the warm BTD-ALS result directly\nOtherwise solve through _solve_model(...)\nIf solver != :als, optionally polish with BTD-ALS (btd_als_polish_maxiter)\nConvert to BTDResult\n\nPolish step usefulness (brief):\n\nUsually helpful for a small final rel_error reduction after RGD converges near a good basin.\nMost useful for quality-focused runs (benchmarks, final fits).\nCan be skipped for speed-sensitive runs (btd_als_polish_maxiter=0) when small extra gains are not worth runtime.\n\nBTD-specific initialization options:\n\n:hosvd: sequential block initialization on residual\n:hosvd_multistart: HOSVD subspace split candidates, optional screening ALS, keep lowest-cost candidate\n:alswarm: short BTD-ALS warm-start wrapper around base initializer\n\nBTD-ALS stabilization behavior:\n\nTracks per-iteration fit change (|rel_t - rel_{t-1}|)\nDetects stagnation when fit change is tiny but rel_error remains high\nCan restart from fresh multistart pool (max_stagnation_restarts)\nReports true final Riemannian gradient norm (grad_norm) instead of a placeholder","title":"BTD (btd)"},{"category":"section","location":"PIPELINE/#Tucker-(tucker)","page":"Pipeline","text":"tucker(A, ranks; method=...) does not use _solve_model. It dispatches directly to decomposition routines:\n\n:sthosvd\n:hooi","title":"Tucker (tucker)"},{"category":"section","location":"PIPELINE/#Generic-approx(...)-routing","page":"Pipeline","text":"approx(manifolds, target; dispatch=:auto) routes by manifold family:\n\nuniform Manifolds.Segre -> cpd(...)\nuniform Manifolds.Tucker matching target shape/rank -> btd(...)\nmixed or non-uniform family -> generic JoinModel(...) path -> ApproxResult\n\ndispatch=:cpd, :btd, and :generic force behavior.","title":"Generic approx(...) routing"},{"category":"section","location":"PIPELINE/#Result-types-and-post-processing","page":"Pipeline","text":"CPDResult\nBTDResult\nTuckerResult\nApproxResult\n\nCommon utilities:\n\nreconstruct(result)\nrel_error(A, result)","title":"Result types and post-processing"},{"category":"section","location":"PIPELINE/#File-map","page":"Pipeline","text":"API entry points: src/api/approx.jl, src/api/cpd.jl, src/api/nncpd.jl, src/api/btd.jl\nRouting helpers: src/dispatch/approx_routing.jl\nSolver dispatch core: src/solvers/solve_dispatch.jl\nBTD backend/init details: src/btd/model.jl, src/solvers/btd_als.jl","title":"File map"},{"category":"section","location":"join/#Join-Decomposition","page":"Join","text":"A Join Decomposition of a vector xinmathbb R^N is a decomposition of the form x = x_1+cdots+x_r, where x_iin M_i and M_isubset mathbb R^N is a given embedded manifold. \n\nFor instance, we can approximate a point p = (12 04)inmathbb R^2 by x=x_1+x_2, where x_1x_2in S^1 are points on the circle:\n\njulia> using Manifolds\njulia> p = [1.2, 0.4]\njulia> S = Sphere(1)\njulia> join_res = approx((S, S), p)\nApproxResult{Float64}\n Components: 2\n Rel. error: 0.0007114699550529457\n\nWe access the decomposition as follows.\n\ncomponents(join_res)\nreconstruct(join_res)\n\n ","title":"Join Decomposition"},{"category":"section","location":"join/#Generic-Join-Approximation","page":"Join","text":"approx(...) is the main frontend for join decomposition. It works in two stages:\n\nbuild an initial point\nrefine it with the selected solver","title":"Generic Join Approximation"},{"category":"section","location":"join/#Supported-Forms","page":"Join","text":"approx(model; kwargs...) Solve an already constructed JoinModel.\napprox(manifolds, target; kwargs...) Build a join from a tuple/vector of component manifolds.\napprox(M::ProductManifold, target; kwargs...) Use the product-manifold factors as join components.\napprox(base, r, target; kwargs...) Repeat a single base manifold r times to build a join.\napprox(base, target; kwargs...) Build a single-component join.\n\nExamples:\n\njulia> target = [1.2, 0.4, -0.3]\njulia> model = JoinModel(Manifolds.Sphere(2), target)\njulia> approx(model; maxiter = 50, verbose = false)\nApproxResult{Float64}\n Components: 1\n Rel. error: 0.23076923076923075\n\njulia> target = randn(2, 3)\njulia> approx((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)\nCPDResult{Float64}\n\njulia> target = [1.2, 0.4, -0.3]\njulia> approx(Manifolds.Sphere(2), 2, target; verbose = false)\nApproxResult{Float64}","title":"Supported Forms"},{"category":"section","location":"join/#Routing","page":"Join","text":"With dispatch = :auto:\n\nuniform Manifolds.Segre components route to cpd(...)\nuniform Manifolds.Tucker components route to btd(...)\notherwise the generic JoinModel(...) path is used and ApproxResult is returned\n\nYou can also force the route explicitly:\n\ndispatch = :generic\ndispatch = :cpd\ndispatch = :btd\n\nForced routing is validated:\n\ndispatch = :cpd requires all components to be Manifolds.Segre with identical factor_dims\ndispatch = :btd requires all components to be Manifolds.Tucker with identical factor_dims and compatible multilinear rank","title":"Routing"},{"category":"section","location":"join/#Generic-Join-Options","page":"Join","text":"For the generic join path:\n\ninit = :random Default initializer. Built-in initializer support depends on the component manifolds.\nsolver = :rgd Supported generic-join solver options are:\n:rgd\n:rgd_fixed\n:rcg\n:lbfgs\n\nOther common options:\n\np0 = nothing\nmaxiter = 500\nstepsize = 1.0\ntol = 1e-6\ngradient_mode = :riemannian\nverbose = true\nvector_transport_method = nothing\n\nBuilt-in init support by component family:\n\nSphere: :random, :deterministic, :target\nSegre: :random, :deterministic\nTucker: :random, :tucker, :tucker_diag, :sthosvd\nother manifolds: :random","title":"Generic Join Options"},{"category":"section","location":"join/#Notes","page":"Join","text":"Generic joins require every component manifold to embed into the same flattened ambient length as target.\nsolver = :als is not available for a truly generic JoinModel. ALS may still be available when approx(...) auto-routes to cpd(...) or btd(...).","title":"Notes"},{"category":"section","location":"join/#Join-Decomposition-Docs","page":"Join","text":"","title":"Join Decomposition Docs"},{"category":"function","location":"join/#TensorKitchen.approx","page":"Join","text":"Generic Join Approximation\n\napprox(...) is the main frontend for join decomposition, which works in two stages:\n\nbuild an initial point\nrefine it with the selected solver\n\nSupported Forms\n\napprox(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.\nmodel means an already constructed JoinModel.\nIt fully fixes the decomposition structure and target.\napprox(model; ...) just solves that model.\nExample: If you want to approximate a point on the sphere, you can build a JoinModel and then use approx to refine it.\n\ntarget = [1.2, 0.4, -0.3]\nmodel = JoinModel(Manifolds.Sphere(2), target)\napprox(model; maxiter = 100, verbose = false)\n# returns an ApproxResult\n\napprox(manifolds, target; kwargs...) : Builds a generic join model from a tuple of existing manifolds and routes to according to dispatch. Example:\n\ntarget = randn(2, 3)\napprox((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)\n# This builds a join with 2 copies of Manifolds.Segre((2, 3))\".\n\napprox(M::ProductManifold, target; kwargs...) : Uses the factors of a product manifold and route to CPD, BTD, or the generic join solver according to dispatch. \nM::ProductManifold means that you already have a product manifold whose factors are the join components.\napprox(M, target; ...) uses those factors directly.\nIt is more explicit than base, because the components are already listed.\nExample:\n\n# Example 1\ntarget = randn(2, 3)\nM = ProductManifold(Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3)))\napprox(M, target; verbose = false)\n# returns an CPDResult\n\n# Example 2\ntarget = randn(4, 3, 2)\nM = ProductManifold(\n Manifolds.Tucker((4, 3, 2), (2, 2, 2)),\n Manifolds.Tucker((4, 3, 2), (2, 2, 2)),\n)\napprox(M, target; verbose = false)\n# returns an BTDResult\n\napprox(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\n\ndispatch 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.\n\ntarget = randn(2, 3)\napprox(Manifolds.Segre((2, 3)), 2, target; verbose = false)\n# returns an CPDResult\n\napprox(base, target; kwargs...) : Builds a single-component generic join and route to the generic join solver. Example:\n\ntarget = [1.2, 0.4, -0.3] \napprox(Manifolds.Sphere(2), target; verbose = false)\n# returns an ApproxResult\n\nBy default, approx auto-routes by manifold family:\nuniform Manifolds.Segre summands calls cpd(...)\nuniform Manifolds.Tucker summands calls btd(...)\notherwise calls JoinModel(...) and returns a ApproxResult\n\nReturn Types\n\nDepending on the manifold family, approx(...) may return:\nApproxResult for the generic join path\nCPDResult when auto-routed to cpd(...)\nBTDResult when auto-routed to btd(...)\n\nMain Options\n\nFor the generic join path:\n\ninit = :random: Sets the algorithm to find the initial point.\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\nrgd (default): Riemannian gradient descent\nrgd_fixed: Riemannian gradient descent with fixed step size\nrcg: Riemannian conjugate gradient\nlbfgs: Limited-memory quasi-Newton\n\n##Notes##\n\n: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.\nwarm_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.\nFor generic mixed joins, use manifold solvers such as :rgd, :rcg, or :lbfgs.\n\n\n\n\n\napprox(M::ProductManifold, target; dispatch=:auto, kwargs...)\n\nUse the factors of a product manifold as join components and route to CPD, BTD, or the generic join solver according to dispatch.\n\n\n\n\n\napprox(base::Manifolds.Segre, r, target; dispatch=:auto, kwargs...)\n\nBuild a rank-r Segre join and route to the CPD pipeline unless generic dispatch is explicitly requested.\n\n\n\n\n\napprox(base::Manifolds.Tucker, r, target; dispatch=:auto, kwargs...)\n\nBuild a r-block Tucker join and route to the BTD pipeline unless generic dispatch is explicitly requested.\n\n\n\n\n\napprox(base::AbstractManifold, r, target; dispatch=:auto, kwargs...)\n\nFallback rank-r join constructor for non-specialized manifolds. Forced CPD or BTD dispatch is rejected because the base manifold family is not known.\n\n\n\n\n\napprox(base::AbstractManifold, target; dispatch=:auto, kwargs...)\n\nSingle-component generic approximation fallback. Use this when no CPD/BTD family-specific routing is intended.\n\n\n\n\n\n","title":"TensorKitchen.approx"},{"category":"type","location":"join/#TensorKitchen.ApproxResult","page":"Join","text":"ApproxResult{T}\n\nGeneric result of approx(manifolds, target) (generic join decomposition).\n\npoint: final point on the join manifold\ncomponents: extracted component descriptions\ncost: the value of the optimization objective at the final returned point, that is typically the least-squares objective.\nrel_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\ngrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is typically the Riemannian gradient norm\niterations: number of iterations used\nconverged: whether the decomposition converged\nsolver: solver used for the decomposition\nsolver_info: solver-specific diagnostics/metadata (NamedTuple)\n\n\n\n\n\n","title":"TensorKitchen.ApproxResult"},{"category":"method","location":"join/#TensorKitchen.reconstruct-Tuple{ApproxResult}","page":"Join","text":"reconstruct(res::ApproxResult)\nreconstruct(res::BTDResult)\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"function","location":"join/#TensorKitchen.join_product","page":"Join","text":"join_product(base, r) constructs a ProductManifold\n\nConstruct 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.\n\n\n\n\n\n","title":"TensorKitchen.join_product"},{"category":"function","location":"join/#TensorKitchen.SegreProduct","page":"Join","text":"SegreProduct(dims, r)\n\nProduct manifold Manifolds.Segre(dims) × ... × Manifolds.Segre(dims) (r factors). Each component point uses the Manifolds.Segre layout [[λ], x₁, …, x_d].\n\n\n\n\n\n","title":"TensorKitchen.SegreProduct"},{"category":"section","location":"btd/#Block-Term-Decomposition","page":"BTD","text":"A block term decomposition (BTD) with r blocks writes\n\nhat A = sum_i=1^r A_i\n\nwhere each block A_i is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.\n\nTo compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use\n\njulia> r = 10\njulia> mlrank = (5, 4, 3)\njulia> btd_res = btd(A, r, mlrank)\nBTDResult{Float64}\n Blocks: 10\n Rel. error: 0.2551559591470521\n\nThe blocks of btd_res can be obtained as follows:\n\nblocks = blocks(btd_res)\n\nEach block is represented as a Tucker decomposition, so we can access its core and factor matrices via:\n\nblk = blocks[1]\ncore(blk)\nfactors(blk)","title":"Block Term Decomposition"},{"category":"section","location":"btd/#BTD-Docs","page":"BTD","text":"","title":"BTD Docs"},{"category":"function","location":"btd/#TensorKitchen.btd","page":"BTD","text":"btd(A, blocks, ranks; kwargs...) returns a BTDResult\n\nComputes a block-term decomposition of A with blocks Tucker blocks, each with multilinear rank ranks. The solver first finds an initial point, then refines it. Returns a BTDResult.\n\nMain Options\n\ninit = :auto: Sets the algorithm to find the initial point. Possible options are:\n:auto: Uses a default BTD initializer. For solver = :als, this uses BTDHOSVDMultistartInit; otherwise, it uses an ALS warm start.\n:alswarm: Runs ALS first and uses the result as the initial point for manifold solver refinement.\ncustom initializer objects, e.g. BTDHOSVDMultistartInit(...).\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\n:rgd (default): Riemannian gradient descent.\n:als: Alternating least squares.\n:rcg: Riemannian conjugate gradient.\n:lbfgs: Limited-memory quasi-Newton refinement.\n\nExtended Options\n\ninit_point = nothing: Explicit initial point. If provided, it overrides the default initial point.\nwarm_init = BTDHOSVDMultistartInit(...): Searches for initial points using HOSVD for :alswarm, optionally screens them with short ALS runs, and returns the lowest-cost candidate.\nwarm_steps = 200: Once finding the best initial point, it runs this many ALS iterations to refine the initial point.\nwarm_block_method = :hooi or :sthosvd: Block update method used during warm start.\nwarm_block_maxiter = 20: Maximum number of inner iterations for each block update during warm start.\nwarm_rel_error_gate = 5e-2: Skips manifold refinement if the warm-start error is above this threshold.\nmaxiter = 500: Maximum number of Riemannian gradient descent iterations.\nstepsize = 0.01: Initial step size for line search in Riemannian gradient descent.\ntol = 1e-6: Convergence tolerance.\ngradient_mode = :riemannian: rgrad can be directly applied for manifold solvers. \nIf the model has a direct rgrad, it uses that.\nOtherwise it computes egrad and projects it to the tangent space.\nThis behavior is in src/solvers/abstract.jl (line 289).\nverbose = true: Enables progress output.\nblock_method = :hooi or :sthosvd: Block update method used for manifold solvers.\nblock_maxiter = 30: Maximum number of inner block-update iterations for manifold solvers.\nbtd_als_polish_maxiter = nothing: Number of final ALS polishing iterations. If nothing, an automatic budget is selected.\nThese settings are a robustness/quality feature for BTD-ALS. They are not used for manifold solvers.\nmax_stagnation_restarts = 1: More retries after a bad stagnated ALS pass. Higher values can improve solution quality, but increases runtime.\nstagnation_rel_error = 1e-4: This is a “bad final error” cutoff, not an improvement threshold. Lower value means restarts trigger more easily.\nrestart_candidates = 24: It usually improves chance of finding a better basin, but cost grows roughly linearly.\nrestart_screening_steps = 5: It uses this many quick ALS steps to screen restart candidates.\nrestart_block_maxiter = 20: Inner block-update limit during restart screening for BTD-ALS.\nrestart_seed = nothing: Optional random seed for restart generation for BTD-ALS.\n\nExample\n\njulia> using Random\njulia> Random.seed!(0)\njulia> A = randn(20, 15, 10); blocks = 10; ranks = (5, 4, 3)\njulia> res = btd(A, blocks, ranks; verbose = false)\nBTDResult{Float64}\n Blocks: 10\n Rel. error: 0.2625821087015455\n\n\n\n\n\n","title":"TensorKitchen.btd"},{"category":"type","location":"btd/#TensorKitchen.BTDResult","page":"BTD","text":"BTDResult{T}\n\nResult of block-term decomposition (btd); block components expose Tucker structure through accessors like core(blk), factors(blk), and blk.tensor.\n\nsolver_info: solver-specific diagnostics/metadata (NamedTuple). Typical keys include BTD-ALS restart diagnostics (total_iterations, stagnation_restarts, restart_rel_error_history) and BTD-TSD run settings (schedule, block_repeats, block_count, stepsize).\n\n\n\n\n\n","title":"TensorKitchen.BTDResult"},{"category":"function","location":"btd/#TensorKitchen.blocks","page":"BTD","text":"blocks(r::BTDResult)\n\nReturn the Tucker block components of a block-term decomposition result.\n\n\n\n\n\n","title":"TensorKitchen.blocks"},{"category":"method","location":"btd/#TensorKitchen.reconstruct-Tuple{BTDResult}","page":"BTD","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"tucker/#Tucker-Decomposition","page":"Tucker","text":"Approximating A by a Tucker decomposition\n\nhat A = C times_1 U times_2 V times_3 W\n\nwith multilinear rank mlrank can be computed as follows.\n\njulia> mlrank = (5, 4, 3)\njulia> tucker_res = tucker(A, mlrank)\nTuckerResult{Float64, 3}\n Original size: (20, 15, 10)\n Core size: (5, 4, 3)\n Multilinear rank: (5, 4, 3)\n Compression: 12.0x\n\nThe core C and the factor matrices (U V W) of the decomposition can be accessed as follows.\n\ncore(tucker_res)\nfactors(tucker_res)","title":"Tucker Decomposition"},{"category":"section","location":"tucker/#Tucker-Docs","page":"Tucker","text":"","title":"Tucker Docs"},{"category":"function","location":"tucker/#TensorKitchen.tucker","page":"Tucker","text":"tucker(A, ranks; method = :sthosvd, kwargs...) returns a TuckerResult\n\nComputes a Tucker decomposition of A with multilinear rank ranks.\n\nMain Options\n\nmethod = :sthosvd: Sets the Tucker decomposition algorithm. Possible options are:\n:sthosvd (default): Sequentially Truncated HOSVD. A direct one-pass decomposition, mainly used as a fast standalone Tucker approximation or as the default initializer for :hooiFast, deterministic, and usually a good initial point.\n:hooi: High-Order Orthogonal Iteration. Iteratively refines the Tucker factors, initialized by ST-HOSVD by default.\n\nExtended Options\n\nFor method = :hooi:\n\nmaxiter = 50: Maximum number of HOOI iterations.\ntol = 1e-8: Convergence tolerance based on change in relative reconstruction error.\ninit = :sthosvd\n:sthosvd: Uses ST-HOSVD to initialize the Tucker factors.\nTuckerResult: Uses an existing Tucker decomposition as the initial point.\n\nExample\n\njulia> using Random\njulia> Random.seed!(0)\njulia> A = randn(20, 15, 10); ranks = (5, 4, 3)\njulia> res = tucker(A, ranks; verbose = false)\nTuckerResult{Float64, 3}\n Original size: (20, 15, 10)\n Core size: (5, 4, 3)\n Multilinear rank: (5, 4, 3)\n Compression: 12.0x\n\n\nThe core tensor and factor matrices can be accessed by\n\ncore(res)\nfactors(res)\n\nA tensor approximation can be reconstructed by\n\nreconstruct(res)\n\nor equivalently\n\nreconstruct_tucker(core(res), factors(res))\n\nNotes\n\n:sthosvd is not an iterative solver. It directly returns a TuckerResult and does not expose solver-style outputs such as iteration counts or convergence diagnostics.\n:hooi is the iterative refinement method in the current Tucker implementation.\nImportant distinction: For the current Tucker implementation, do not use solver = :rgd, init = :auto, or manifold solvers like RGD, RCG, LBFGS, etc.\n\n\n\n\n\n","title":"TensorKitchen.tucker"},{"category":"type","location":"tucker/#TensorKitchen.TuckerResult","page":"Tucker","text":"TuckerResult{T, N}\n\nStores a Tucker decomposition: core tensor and factor matrices.\n\ncore::Array{T,N} — core tensor\nfactors::Vector{Matrix{T}} — orthonormal factor matrices\nprocessing_order::Vector{Int} — order modes were processed\nsingular_values::Vector{Vector{T}} — singular values per truncation\n\n\n\n\n\n","title":"TensorKitchen.TuckerResult"},{"category":"method","location":"tucker/#TensorKitchen.core-Tuple{TuckerResult}","page":"Tucker","text":"core(td::TuckerResult)\n\nReturn the Tucker core tensor for a Tucker result.\n\n\n\n\n\n","title":"TensorKitchen.core"},{"category":"method","location":"tucker/#TensorKitchen.factors-Tuple{TuckerResult}","page":"Tucker","text":"factors(td::TuckerResult)\n\nReturn the Tucker factor matrices.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"tucker/#TensorKitchen.multilinear_rank-Tuple{TuckerResult}","page":"Tucker","text":"multilinear_rank(td::TuckerResult)\n\nReturn the Tucker multilinear rank tuple, i.e. the size of the core tensor.\n\n\n\n\n\n","title":"TensorKitchen.multilinear_rank"},{"category":"method","location":"tucker/#TensorKitchen.factor_dims-Tuple{TuckerResult}","page":"Tucker","text":"factor_dims(td::TuckerResult)\n\nReturn the original mode dimensions represented by the Tucker factor matrices.\n\n\n\n\n\n","title":"TensorKitchen.factor_dims"},{"category":"method","location":"tucker/#TensorKitchen.reconstruct-Tuple{TuckerResult}","page":"Tucker","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"cpd/#CPD","page":"CPD","text":"Here is how to approximate a tensor A by a CPD of rank r.\n\njulia> using TensorKitchen\njulia> A = randn(20, 15, 10)\njulia> r = 35\njulia> res = cpd(A, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.4359141301703327\n\nNow, res contains a CP approximation of the 3-way tensor A,\n\nhat A = sum_i=1^r lambda_i a_i otimes b_i otimes c_i\n\nIt approximates A with relative error about 0.436.\n\nWe access the decomposition as follows.\n\nλ = weights(res)\nU = factors(res)\n\nHere, U is a triple of matrices (ABC), where the columns of A are the a_i and so on. These are called factor matrices.\n\nWe get the whole reconstructed tensor by \n\n = reconstruct(res)","title":"CPD"},{"category":"section","location":"cpd/#CPD-Docs","page":"CPD","text":"","title":"CPD Docs"},{"category":"function","location":"cpd/#TensorKitchen.cpd","page":"CPD","text":"cpd(A, r; kwargs...)\n\nComputes 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.\n\nMain Options\n\ninit = :auto: Sets the algorithm to find the initial point. Possible options are:\n:auto: Uses a default CPD initializer. For solver = :als, this uses TuckerInit; otherwise, it uses an ALS warm start.\n:alswarm: Runs ALS first and uses the result as the initial point for refinement.\ncustomized initial point:\n:tucker (default when solver = :als): Uses a default Tucker initializer.\n:random: Uses a random initial point.\n:hosvd: Uses a HOSVD initial point.\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\nrgd (default): Riemannian gradient descent\nrgd_fixed: Riemannian gradient descent with fixed step size\nrcg: Riemannian conjugate gradient\nals: Alternating Least Squares\n\nExtended Options\n\np0 = nothing: Explicit initial point. If provided, it overrides the default initial point.\n:alswarm: ALS warm start option.\nwarm_init = TuckerInit(): Before finding the warm start initial point, this sets the good starting point for ALS.\nwarm_steps = 500: Once finding the best initial point from warm_init, it runs this many ALS iterations to refine the initial point.\nmaxiter = 500: Maximum number of Riemannian gradient descent iterations.\nstepsize = 1.0: Initial step size for line search in Riemannian gradient descent.\ntol = 1e-6: Convergence tolerance.\ngradient_mode = :riemannian: Gradient rule for manifold solvers. \nIf the model has a direct rgrad, it uses that.\nOtherwise it computes egrad and projects it to the tangent space.\nThis behavior is in src/solvers/abstract.jl (line 289).\ngeometry = :canonical: Sets the geometry of the manifold. Possible options are:\n:canonical: Standard CPD parameterization with the usual Euclidean factors and canonical Riemannian gradient handling. Best default for general unconstrained CPD.\n:squaring_metric: Nonnegative geometry based on squared latent coordinates. Enforces nonnegativity indirectly, but can become ill-conditioned near zero.\n: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.\n: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.\nverbose = true: Enables progress output.\nnonnegative::Bool = false: Nonnegative CPD option to be selected by the user. (same as nncpd)\npullback_eps = 1e-8: Regularization parameter for pullback-style nonnegative geometries.\n\nNotes\n\nsolver = :als does not use manifold geometry. In that case:\ngeometry must be :canonical\ngradient_mode is ignored except for validation\n:squaring_metric and :softplus_metric require nonnegative = true.\nWhen nonnegative = true, cpd(...) routes to nncpd(...). In that route:\nif solver != :als and geometry is left at :canonical, the effective geometry becomes :softplus_metric\nif stepsize is left at 1.0, the effective default becomes 0.01\nif init = :tucker, the effective initializer becomes :alswarm\n\nExample\n\njulia> A = randn(20, 15, 10); r = 35\njulia> res = cpd(A, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.4359141301703327\n\n\n\n\n\n","title":"TensorKitchen.cpd"},{"category":"function","location":"cpd/#TensorKitchen.nncpd","page":"CPD","text":" nncpd(A, r; kwargs...)\n\nComputes 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.\n\nOptions\n\nThe options are the same as for cpd.\n\nGeometry guide:\n\ngeometry=:softplus_metric Default and usually the safest choice. \ngeometry=:squaring_metric Uses a regularized pullback-inspired geometry induced by the squaring chart.\ngeometry=:canonical Plain nonnegative CP coordinates without the pullback-style manifold geometry. This is the natural choice with solver=:als.\n\nExample\n\njulia> A = randn(20, 15, 10); r = 35;\njulia> B = abs.(A)\njulia> nncpd(B, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.3765605093526155\n\n\n\n\n\n","title":"TensorKitchen.nncpd"},{"category":"type","location":"cpd/#TensorKitchen.CPDResult","page":"CPD","text":"CPDResult{T}\n\nResult of a Canonical Polyadic Decomposition.\n\nStores the decoded CP representation together with solver diagnostics:\n\ncomponents: rank-one tensor components\nweights: component weights\nfactors: factor matrices\ncost: final objective function value at the returned solution\nrel_error: final relative reconstruction error\ngrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is the Riemannian gradient norm\niterations: number of refinement iterations\nconverged: whether the solver reported convergence\nsolver: solver optimization method used to produce the result\nsolver_info: solver-specific diagnostics/metadata (NamedTuple). Typical keys include:\ninitial_stepsize_eff (RGD), memory_size (LBFGS), cautious_update (LBFGS),\ninitial_scale, linesearch, has_preconditioner (LBFGS), and nncp_pullback_eps (NNCP).\n\n\n\n\n\n","title":"TensorKitchen.CPDResult"},{"category":"method","location":"cpd/#TensorKitchen.weights-Tuple{CPDResult}","page":"CPD","text":"weights(r::CPDResult)\n\nReturn the CP component weights stored in a CPD result.\n\n\n\n\n\n","title":"TensorKitchen.weights"},{"category":"method","location":"cpd/#TensorKitchen.factors-Tuple{CPDResult}","page":"CPD","text":"factors(res::CPDResult)\n\nReturn the CP factor matrices of res as a vector [U₁, U₂, ..., U_N], where each U_m has size size(A, m) × rank.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"cpd/#TensorKitchen.reconstruct-Tuple{CPDResult}","page":"CPD","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"#TensorKitchen.jl-Documentation","page":"Home","text":"TensorKitchen.jl is a Julia package for tensor decompositions.\n\nCPD documentation\nTucker documentation\nBTD documentation\nJoin decomposition documentation\nUtilities\nPipeline\nReferences","title":"TensorKitchen.jl Documentation"},{"category":"section","location":"#Notes","page":"Home","text":"The package is currently at a pre-alpha stage. \n\nThe implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.\n\nWhat currently works is \n\nCanonical Polyadic Decomposition (CPD)\nTucker Decomposition\nNonnegative Canonical Polyadic Decomposition (NNCPD)\nBlock Term Decomposition (BTD)\nJoin Decompositions\n\n\n\nThe next updates will include \n\nHandling of swamps/plateaus in the optimization step\nDocumentation\nImproved User Interface\nGPU Support \nLL1 Decomposition (3-way specialized BTD)\nSymmetric CP / Waring Decomposition\nPartially Symmetric CP\nTensor Trains","title":"Notes"},{"category":"section","location":"utils/#General-Utilities","page":"Utilities","text":"","title":"General Utilities"},{"category":"function","location":"utils/#TensorKitchen.save_result","page":"Utilities","text":"save_result(path::AbstractString, result)\n\nSave a result to a file.\n\n\n\n\n\n","title":"TensorKitchen.save_result"},{"category":"function","location":"utils/#TensorKitchen.load_result","page":"Utilities","text":"load_result(path::AbstractString)\n\nLoad a result from a file\n\n\n\n\n\n","title":"TensorKitchen.load_result"}]
+}
diff --git a/docs/build/tucker/index.html b/docs/build/tucker/index.html
new file mode 100644
index 0000000..cd7b853
--- /dev/null
+++ b/docs/build/tucker/index.html
@@ -0,0 +1,21 @@
+
+Tucker · Documentation
tucker(A, ranks; method = :sthosvd, kwargs...) returns a TuckerResult
Computes a Tucker decomposition of A with multilinear rank ranks.
Main Options
method = :sthosvd: Sets the Tucker decomposition algorithm. Possible options are:
:sthosvd (default): Sequentially Truncated HOSVD. A direct one-pass decomposition, mainly used as a fast standalone Tucker approximation or as the default initializer for :hooiFast, deterministic, and usually a good initial point.
:hooi: High-Order Orthogonal Iteration. Iteratively refines the Tucker factors, initialized by ST-HOSVD by default.
Extended Options
For method = :hooi:
maxiter = 50: Maximum number of HOOI iterations.
tol = 1e-8: Convergence tolerance based on change in relative reconstruction error.
init = :sthosvd
:sthosvd: Uses ST-HOSVD to initialize the Tucker factors.
TuckerResult: Uses an existing Tucker decomposition as the initial point.
Example
julia> using Random
+julia> Random.seed!(0)
+julia> A = randn(20, 15, 10); ranks = (5, 4, 3)
+julia> res = tucker(A, ranks; verbose = false)
+TuckerResult{Float64, 3}
+ Original size: (20, 15, 10)
+ Core size: (5, 4, 3)
+ Multilinear rank: (5, 4, 3)
+ Compression: 12.0x
+
The core tensor and factor matrices can be accessed by
core(res)
+factors(res)
A tensor approximation can be reconstructed by
reconstruct(res)
or equivalently
reconstruct_tucker(core(res), factors(res))
Notes
:sthosvd is not an iterative solver. It directly returns a TuckerResult and does not expose solver-style outputs such as iteration counts or convergence diagnostics.
:hooi is the iterative refinement method in the current Tucker implementation.
Important distinction: For the current Tucker implementation, do not use solver = :rgd, init = :auto, or manifold solvers like RGD, RCG, LBFGS, etc.
This is the preferred backend entry point for normalization, since it is independent of solver/manifold layout. To normalize an actual optimization iterate, combine it with cpd_point and pack_cpd_point, or use post_step!.
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
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.
The implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.
Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.
HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.
ST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.
HOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.
Block-term decomposition (BTD): L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.
L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.
BTD-ALS: L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.
Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).
Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).
Riemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.
The implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.
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:
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
Convenience overloads for generic join approximation from manifold specifications:
Tuple/Vector of manifolds
ProductManifold (its factors become join components)
single base::AbstractManifold (one component)
By default, approx auto-routes by manifold family:
uniform Manifolds.Segre summands -> cpd(...)
uniform Manifolds.Tucker summands -> btd(...)
otherwise -> generic JoinModel(...) and ApproxResult
For generic mixed joins, components only need to agree on the flattened ambient length of target; each manifold may still use its own native ambient shape.
Use dispatch=:generic to force the generic join path, dispatch=:cpd to require CPD routing, or dispatch=:btd to require BTD routing.
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.
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.
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.
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.
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 the selected initializer and then use manifold solvers for refinement.
For generic mixed joins, use manifold solvers such as :rgd, :rcg, or :lbfgs.
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
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.
Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.
HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.
ST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.
HOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors," SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.
Block-term decomposition (BTD): L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.
L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.
BTD-ALS: L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.
Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).
Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).
Riemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.
This document was generated with Documenter.jl version 1.17.0 on Wednesday 6 May 2026. Using Julia version 1.12.0.
diff --git a/docs/search_index.js b/docs/search_index.js
index eba3180..4d9e21c 100644
--- a/docs/search_index.js
+++ b/docs/search_index.js
@@ -1,3 +1,3 @@
var documenterSearchIndex = {"docs":
-[{"category":"section","location":"PIPELINE/#TensorKitchen-Pipeline","page":"Pipeline","text":"This document explains how public APIs route into models, solvers, and result converters.","title":"TensorKitchen Pipeline"},{"category":"section","location":"PIPELINE/#Public-entry-points","page":"Pipeline","text":"cpd(A, r; ...) -> CPDResult\nnncpd(A, r; ...) -> CPDResult\nbtd(A, blocks, ranks; ...) -> BTDResult\ntucker(A, ranks; method=...) -> TuckerResult\napprox(...) -> ApproxResult or auto-routed CPDResult/BTDResult","title":"Public entry points"},{"category":"section","location":"PIPELINE/#Default-behavior-(quick-reference)","page":"Pipeline","text":"cpd(A, r):\ninit = :alswarm\nsolver = :rgd\nnncpd(A, r):\ninit = :alswarm\nsolver = :rgd\nbtd(A, blocks, ranks):\ninit = :alswarm\nwarm_steps = 500\nwarm_init = BTDHOSVDMultistartInit(candidates=64, screening_steps=10, block_maxiter=12)\nsolver = :rgd\nfinal BTD-ALS polish enabled by default for non-ALS solvers\nmax_stagnation_restarts = 1 (retry with stronger multistart when ALS fit-change stalls at high rel-error)\ntucker(A, ranks):\nmethod = :sthosvd\napprox(model::JoinModel):\ninit = :alswarm\nwarm_steps = 500\nsolver = :rgd","title":"Default behavior (quick reference)"},{"category":"section","location":"PIPELINE/#Core-execution-architecture","page":"Pipeline","text":"Most optimization APIs share this core pattern:\n\nBuild a model (JoinModel + backend)\nCall _solve_model(...)\nConvert to a public result struct\n\n_solve_model lives in src/solvers/solve_dispatch.jl and is the common symbol-to-solver dispatch layer (:rgd, :rcg, :lbfgs, :als, :btd_tsd).","title":"Core execution architecture"},{"category":"section","location":"PIPELINE/#API-flows","page":"Pipeline","text":"","title":"API flows"},{"category":"section","location":"PIPELINE/#CPD-(cpd,-nncpd)","page":"Pipeline","text":"cpd(A, r; ...):\n\nBuild JoinModel(A, r; geometry=...) with CPDBackend\nNormalize/validate options (solver, geometry, gradient_mode, normalization policy)\nSolve through _solve_model(...)\nOptionally run nonnegative ALS polishing (for selected nonnegative paths)\nConvert to CPDResult\n\nNotes:\n\n:als means CP-ALS.\nManifold solvers (:rgd, :rgd_fixed, :rcg, :lbfgs) share dispatch with other pipelines.","title":"CPD (cpd, nncpd)"},{"category":"section","location":"PIPELINE/#BTD-(btd)","page":"Pipeline","text":"btd(A, blocks, ranks; ...):\n\nBuild a uniform Tucker family via TuckerJoin(...)\nWrap as JoinModel with BTDBackend\nChoose effective initializer:\nsolver == :als: use requested init directly (default multistart)\nsolver != :als: use BTDALSWarmStartInit(...) so first-order methods start from a good BTD-ALS warm point\nSolve through _solve_model(...)\nIf solver != :als, optionally polish with BTD-ALS (btd_als_polish_maxiter)\nConvert to BTDResult\n\nBTD-specific initialization options:\n\n:hosvd: sequential block initialization on residual\n:hosvd_multistart: HOSVD subspace split candidates, optional screening ALS, keep lowest-cost candidate\n:alswarm: short BTD-ALS warm-start wrapper around base initializer\n\nBTD-ALS stabilization behavior:\n\nTracks per-iteration fit change (|rel_t - rel_{t-1}|)\nDetects stagnation when fit change is tiny but rel_error remains high\nCan restart from fresh multistart pool (max_stagnation_restarts)\nReports true final Riemannian gradient norm (grad_norm) instead of a placeholder","title":"BTD (btd)"},{"category":"section","location":"PIPELINE/#Tucker-(tucker)","page":"Pipeline","text":"tucker(A, ranks; method=...) does not use _solve_model. It dispatches directly to decomposition routines:\n\n:sthosvd\n:thosvd / :hosvd\n:hooi","title":"Tucker (tucker)"},{"category":"section","location":"PIPELINE/#Generic-approx(...)-routing","page":"Pipeline","text":"approx(manifolds, target; dispatch=:auto) routes by manifold family:\n\nuniform Manifolds.Segre -> cpd(...)\nuniform Manifolds.Tucker matching target shape/rank -> btd(...)\nmixed or non-uniform family -> generic JoinModel(...) path -> ApproxResult\n\ndispatch=:cpd, :btd, and :generic force behavior.","title":"Generic approx(...) routing"},{"category":"section","location":"PIPELINE/#Result-types-and-post-processing","page":"Pipeline","text":"CPDResult\nBTDResult\nTuckerResult\nApproxResult\n\nCommon utilities:\n\nreconstruct(result)\nrel_error(A, result)","title":"Result types and post-processing"},{"category":"section","location":"PIPELINE/#File-map","page":"Pipeline","text":"API entry points: src/api/approx.jl, src/api/cpd.jl, src/api/nncpd.jl, src/api/btd.jl\nRouting helpers: src/dispatch/approx_routing.jl\nSolver dispatch core: src/solvers/solve_dispatch.jl\nBTD backend/init details: src/btd/model.jl, src/solvers/btd_als.jl","title":"File map"},{"category":"section","location":"join/#Join-Decomposition","page":"Join","text":"A Join Decomposition of a vector xinmathbb R^N is a decomposition of the form x = x_1+cdots+x_r, where x_iin M_i and M_isubset mathbb R^N is a given embedded manifold. \n\nFor instance, we can approximate a point p = (12 04)inmathbb R^2 by x=x_1+x_2, where x_1x_2in S^1 are points on the circle:\n\njulia> using Manifolds\njulia> p = [1.2, 0.4]\njulia> S = Sphere(1)\njulia> join_res = approx((S, S), p)\nApproxResult{Float64}\n Components: 2\n Rel. error: 0.0007114699550529457\n\nWe access the decomposition as follows.\n\ncomponents(join_res)\nreconstruct(join_res)\n\n ","title":"Join Decomposition"},{"category":"section","location":"join/#Join-Decomposition-Docs","page":"Join","text":"","title":"Join Decomposition Docs"},{"category":"function","location":"join/#TensorKitchen.approx","page":"Join","text":"approx(model; init=:alswarm, solver=:rgd, ...) -> ApproxResult\n\nCore approximation entry-point. Solves any model that implements the common manifold hooks.\n\n\n\n\n\napprox(manifolds, target; dispatch=:auto, kwargs...) -> Union{ApproxResult,CPDResult,BTDResult}\napprox(base, r, target; dispatch=:auto, kwargs...) -> Union{ApproxResult,CPDResult,BTDResult}\napprox(base, target; kwargs...) -> ApproxResult\n\nConvenience overloads for generic join approximation from manifold specifications:\n\nTuple/Vector of manifolds\nProductManifold (its factors become join components)\nsingle base::AbstractManifold (one component)\n\nBy default, approx auto-routes by manifold family:\n\nuniform Manifolds.Segre summands -> cpd(...)\nuniform Manifolds.Tucker summands -> btd(...)\notherwise -> generic JoinModel(...) and ApproxResult\n\nFor generic mixed joins, components only need to agree on the flattened ambient length of target; each manifold may still use its own native ambient shape.\n\nUse dispatch=:generic to force the generic join path, dispatch=:cpd to require CPD routing, or dispatch=:btd to require BTD routing.\n\n\n\n\n\n","title":"TensorKitchen.approx"},{"category":"type","location":"join/#TensorKitchen.ApproxResult","page":"Join","text":"ApproxResult{T}\n\nGeneric result of approx(manifolds, target) (generic join).\n\npoint: final point on product manifold\ncomponents: extracted component descriptions\ncost, rel_error, grad_norm, iterations, converged, solver: optimization metadata\nsolver_info: lightweight solver diagnostics\n\n\n\n\n\n","title":"TensorKitchen.ApproxResult"},{"category":"method","location":"join/#TensorKitchen.reconstruct-Tuple{ApproxResult}","page":"Join","text":"reconstruct(r::ApproxResult)\nreconstruct(r::BTDResult)\n\nReconstruct by summing component.tensor for all components.\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"function","location":"join/#TensorKitchen.join_product","page":"Join","text":"join_product(base, r) -> ProductManifold\n\nConstruct 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.\n\n\n\n\n\n","title":"TensorKitchen.join_product"},{"category":"function","location":"join/#TensorKitchen.SegreProduct","page":"Join","text":"SegreProduct(dims, r)\n\nProduct manifold Segre(dims) × ... × Segre(dims) (r factors). Each component point uses native Segre layout [[λ], x₁, …, x_d].\n\n\n\n\n\n","title":"TensorKitchen.SegreProduct"},{"category":"type","location":"join/#TensorKitchen.SqEuclidean","page":"Join","text":"SqEuclidean(n; ε=1e-8)\n\nEuclidean space ℝⁿ with regularized pullback metric from squaring map p ↦ p².\n\ninner(M, p, X, Y) = X' G(p) Y with G(p) = diag(4*p.² + ε)\nRiemannian gradient: grad = G(p)^{-1} * egrad (well-conditioned at p=0; exact pullback when 4*p.² ≫ ε)\n\nCompatible with Manopt: uses inner for norm, delegates retract/project to Euclidean.\n\n\n\n\n\n","title":"TensorKitchen.SqEuclidean"},{"category":"function","location":"join/#TensorKitchen.pullback_metric_inverse","page":"Join","text":"pullback_metric_inverse(M, p)\n\nApply G(p)^{-1} to vector X. For diagonal G, (G^{-1} X)i = Xi / (4*p_i² + ε). Used to convert Euclidean gradient to Riemannian gradient.\n\n\n\n\n\n","title":"TensorKitchen.pullback_metric_inverse"},{"category":"function","location":"join/#TensorKitchen.sm_cost_nn_quadratic","page":"Join","text":"sm_cost_nn_quadratic(p; a=1, b=1)\n\nf(x,y) = 0.5*((x^2-a)^2 + (y^2-b)^2)\n\n\n\n\n\n","title":"TensorKitchen.sm_cost_nn_quadratic"},{"category":"function","location":"join/#TensorKitchen.sm_egrad_nn_quadratic","page":"Join","text":"sm_egrad_nn_quadratic(p; a=1, b=1)\n\nEuclidean gradient of sm_cost_nn_quadratic.\n\n\n\n\n\n","title":"TensorKitchen.sm_egrad_nn_quadratic"},{"category":"function","location":"join/#TensorKitchen.sm_cost_nn_rank1","page":"Join","text":"sm_cost_nn_rank1(p; A=1)\n\nf(x,y) = 0.5(A - x^2y^2)^2\n\n\n\n\n\n","title":"TensorKitchen.sm_cost_nn_rank1"},{"category":"function","location":"join/#TensorKitchen.sm_egrad_nn_rank1","page":"Join","text":"sm_egrad_nn_rank1(p; A=1)\n\nEuclidean gradient of sm_cost_nn_rank1.\n\n\n\n\n\n","title":"TensorKitchen.sm_egrad_nn_rank1"},{"category":"function","location":"join/#TensorKitchen.sm_cost_logbarrier","page":"Join","text":"sm_cost_logbarrier(p; a=1, b=1, eps_barrier=1e-2)\n\nf(x,y)=0.5(x-a)^2 + 0.5(y-b)^2 - epsbarrier*log(x) - epsbarrier*log(y), x,y>0.\n\n\n\n\n\n","title":"TensorKitchen.sm_cost_logbarrier"},{"category":"function","location":"join/#TensorKitchen.sm_egrad_logbarrier","page":"Join","text":"sm_egrad_logbarrier(p; a=1, b=1, eps_barrier=1e-2)\n\nEuclidean gradient of sm_cost_logbarrier.\n\n\n\n\n\n","title":"TensorKitchen.sm_egrad_logbarrier"},{"category":"function","location":"join/#TensorKitchen.sm_cost_double_circle","page":"Join","text":"sm_cost_double_circle(p; c1=(5,5), c2=(-1.5,5), r1=1, r2=1)\n\nf(x,y) = C1*C2 with circle level sets C1=0, C2=0.\n\n\n\n\n\n","title":"TensorKitchen.sm_cost_double_circle"},{"category":"function","location":"join/#TensorKitchen.sm_egrad_double_circle","page":"Join","text":"sm_egrad_double_circle(p; c1=(5,5), c2=(-1.5,5), r1=1, r2=1)\n\nEuclidean gradient of sm_cost_double_circle.\n\n\n\n\n\n","title":"TensorKitchen.sm_egrad_double_circle"},{"category":"function","location":"join/#TensorKitchen.sm_double_circle_at_zero","page":"Join","text":"sm_double_circle_at_zero(; c1=(5,5), c2=(-1.5,5), r1=1, r2=1)\n\nReturn value and gradient of the double-circle objective at (0,0).\n\n\n\n\n\n","title":"TensorKitchen.sm_double_circle_at_zero"},{"category":"function","location":"join/#TensorKitchen.egrad_to_rgrad","page":"Join","text":"egrad_to_rgrad(M, p, egrad)\n\nConvert Euclidean/ambient gradient egrad at p to Riemannian tangent on M.\n\nEmbedded manifolds: project(M, p, egrad).\nSqEuclidean: pullback metric inverse.\nFallback: orthonormal basis projection when project is unavailable.\n\n\n\n\n\n","title":"TensorKitchen.egrad_to_rgrad"},{"category":"function","location":"join/#TensorKitchen.egrad_to_rgrad!","page":"Join","text":"egrad_to_rgrad!(M, X, p, egrad)\n\nIn-place variant. Uses project! when available, else egrad_to_rgrad + copyto!.\n\n\n\n\n\n","title":"TensorKitchen.egrad_to_rgrad!"},{"category":"section","location":"btd/#Block-Term-Decomposition","page":"BTD","text":"A block term decomposition (BTD) with r blocks writes\n\nhat A = sum_i=1^r A_i\n\nwhere each block A_i is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.\n\nTo compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use\n\njulia> r = 10\njulia> mlrank = (5, 4, 3)\njulia> btd_res = btd(A, r, mlrank)\nBTDResult{Float64}\n Blocks: 10\n Rel. error: 0.2551559591470521\n\nThe blocks of btd_res can be obtained as follows:\n\nblocks = blocks(btd_res)\n\nEach block is represented as a Tucker decomposition, so we can access its core and factor matrices via:\n\nblk = blocks[1]\ncore(blk)\nfactors(blk)","title":"Block Term Decomposition"},{"category":"section","location":"btd/#BTD-Docs","page":"BTD","text":"","title":"BTD Docs"},{"category":"function","location":"btd/#TensorKitchen.btd","page":"BTD","text":"btd(A, blocks, ranks; kwargs...) -> BTDResult\n\nBlock-term decomposition (sum of Tucker blocks); default pipeline is BTDHOSVDMultistartInit(candidates=64, screening_steps=10, block_maxiter=12) followed by ALSwarm(500), Riemannian gradient descent, and a final BTD-ALS polish step.\n\n\n\n\n\n","title":"TensorKitchen.btd"},{"category":"type","location":"btd/#TensorKitchen.BTDResult","page":"BTD","text":"BTDResult{T}\n\nResult of block-term decomposition (btd). Same shape as ApproxResult; each component is a Tucker block (.kind === :Tucker, .core, .factors, .tensor), with optional lightweight solver_info.\n\n\n\n\n\n","title":"TensorKitchen.BTDResult"},{"category":"function","location":"btd/#TensorKitchen.blocks","page":"BTD","text":"blocks(r::BTDResult) -> Vector{DecompositionComponent}\n\nReturn the Tucker block components of a BTDResult. Each block blk supports core(blk) and factors(blk).\n\n\n\n\n\n","title":"TensorKitchen.blocks"},{"category":"method","location":"btd/#TensorKitchen.reconstruct-Tuple{BTDResult}","page":"BTD","text":"reconstruct(td::TuckerResult) -> Array\n\nReconstruct the full tensor from a Tucker decomposition: A ≈ S ×₁ U₁ ×₂ U₂ ⋯ ×d Ud\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"type","location":"btd/#TensorKitchen.BTDTSDSolver","page":"BTD","text":"BTDTSDSolver(; stepsize=1.0, schedule=:cyclic, block_repeats=1,\n armijo_contraction=0.5, armijo_sufficient_decrease=1e-4, armijo_alpha_min=1e-12)\n\nBlockwise tangent-subspace descent specialized to BTD. Each block update uses the projected Tucker-block tangent direction and accepts it only when a block Armijo decrease condition is satisfied.\n\n\n\n\n\n","title":"TensorKitchen.BTDTSDSolver"},{"category":"type","location":"btd/#TensorKitchen.TSDSolver","page":"BTD","text":"BTDTSDSolver(; stepsize=1.0, schedule=:cyclic, block_repeats=1,\n armijo_contraction=0.5, armijo_sufficient_decrease=1e-4, armijo_alpha_min=1e-12)\n\nBlockwise tangent-subspace descent specialized to BTD. Each block update uses the projected Tucker-block tangent direction and accepts it only when a block Armijo decrease condition is satisfied.\n\n\n\n\n\n","title":"TensorKitchen.TSDSolver"},{"category":"section","location":"tucker/#Tucker-Decomposition","page":"Tucker","text":"Approximating A by a Tucker decomposition\n\nhat A = C times_1 U times_2 V times_3 W\n\nwith multilinear rank mlrank can be computed as follows.\n\njulia> mlrank = (5, 4, 3)\njulia> tucker_res = tucker(A, mlrank)\nTuckerResult{Float64, 3}\n Original size: (20, 15, 10)\n Core size: (5, 4, 3)\n Multilinear rank: (5, 4, 3)\n Compression: 12.0x\n\nThe core C and the factor matrices (U V W) of the decomposition can be accessed as follows.\n\ncore(tucker_res)\nfactors(tucker_res)","title":"Tucker Decomposition"},{"category":"section","location":"tucker/#Tucker-Docs","page":"Tucker","text":"","title":"Tucker Docs"},{"category":"function","location":"tucker/#TensorKitchen.tucker","page":"Tucker","text":"tucker(A, ranks; method=:sthosvd, kwargs...) -> TuckerResult\n\nTucker decomposition. method: :sthosvd (default), :thosvd/:hosvd, or :hooi.\n\n\n\n\n\n","title":"TensorKitchen.tucker"},{"category":"type","location":"tucker/#TensorKitchen.TuckerResult","page":"Tucker","text":"TuckerResult{T, N}\n\nStores a Tucker decomposition: core tensor and factor matrices.\n\ncore::Array{T,N} — core tensor\nfactors::Vector{Matrix{T}} — orthonormal factor matrices\nprocessing_order::Vector{Int} — order modes were processed\nsingular_values::Vector{Vector{T}} — singular values per truncation\n\n\n\n\n\n","title":"TensorKitchen.TuckerResult"},{"category":"method","location":"tucker/#TensorKitchen.core-Tuple{TuckerResult}","page":"Tucker","text":"core(td::TuckerResult) -> Array{T,N}\n\nReturn the core tensor C of a Tucker decomposition  = (U₁ ⊗ U₂ ⊗ … ⊗ U_N) · C.\n\n\n\n\n\n","title":"TensorKitchen.core"},{"category":"method","location":"tucker/#TensorKitchen.factors-Tuple{TuckerResult}","page":"Tucker","text":"factors(td::TuckerResult) -> Vector{Matrix{T}}\n\nReturn the factor matrices (U₁, U₂, …, U_N) of a Tucker decomposition.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"tucker/#TensorKitchen.multilinear_rank-Tuple{TuckerResult}","page":"Tucker","text":"multilinear_rank(td::TuckerResult) -> NTuple{N,Int}\n\nReturn the multilinear rank, i.e. the tuple size(core(td)).\n\n\n\n\n\n","title":"TensorKitchen.multilinear_rank"},{"category":"method","location":"tucker/#TensorKitchen.factor_dims-Tuple{TuckerResult}","page":"Tucker","text":"factor_dims(td::TuckerResult) -> NTuple{N,Int}\n\nReturn the ambient mode sizes, i.e. size(U_m, 1) for each factor.\n\n\n\n\n\n","title":"TensorKitchen.factor_dims"},{"category":"method","location":"tucker/#TensorKitchen.reconstruct-Tuple{TuckerResult}","page":"Tucker","text":"reconstruct(td::TuckerResult) -> Array\n\nReconstruct the full tensor from a Tucker decomposition: A ≈ S ×₁ U₁ ×₂ U₂ ⋯ ×d Ud\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"function","location":"tucker/#TensorKitchen.sthosvd","page":"Tucker","text":"sthosvd(A, ranks; [processing_order], [verbose]) -> TuckerResult\n\nCompute the rank-(r₁,…,r_d) Sequentially Truncated HOSVD (ST-HOSVD).\n\nAlgorithm (Definition 6.1, Algorithm 1)\n\nGiven tensor A ∈ ℝ^{n₁×⋯×nd} and target multilinear rank (r₁,…,rd):\n\nSet Ŝ₀ = A\nFor k = 1,…,d (in processing order p): a. Compute the mode-p[k] unfolding of Ŝ{k-1} b. Compute the compact SVD; truncate to rank r{p[k]} c. Set Û{p[k]} = first r{p[k]} left singular vectors d. Ŝₖ = Ŝ{k-1} ×{p[k]} Û_{p[k]}ᵀ (project and shrink)\nReturn core Ŝd and factor matrices Û₁,…,Ûd\n\nArguments\n\nA::Array{T,N}: input tensor\nranks::NTuple{N,Int}: target multilinear rank (r₁,…,r_d)\n\nKeyword arguments\n\nprocessing_order::Vector{Int}: order in which to process modes (default: heuristic)\nverbose::Bool: print progress information (default: false)\n\nReturns\n\nTuckerResult with core tensor and factor matrices.\n\nExample\n\nA = randn(50, 40, 30)\ntd = sthosvd(A, (5, 4, 3))\nrel_err = relative_error(A, td)\n\n\n\n\n\nsthosvd(A, tol; [processing_order], [verbose]) -> TuckerResult\n\nTolerance-based ST-HOSVD: automatically determine ranks to achieve ‖A - Â‖F ≤ tol · ‖A‖F\n\nUses the error bound from Theorem 6.4: the squared error decomposes as a sum of squared errors from each truncation step. By distributing the error budget equally across modes (ϵₖ = ϵ/√d for each mode), the final error is bounded by ϵ.\n\nArguments\n\nA::Array{T,N}: input tensor\ntol::Float64: relative error tolerance\n\nKeyword arguments\n\nprocessing_order::Vector{Int}: order in which to process modes (default: smallest first)\nverbose::Bool: print progress information (default: false)\n\n\n\n\n\n","title":"TensorKitchen.sthosvd"},{"category":"function","location":"tucker/#TensorKitchen.thosvd","page":"Tucker","text":"thosvd(A, ranks; [verbose]) -> TuckerResult\n\nClassical Truncated HOSVD (T-HOSVD).\n\nComputes all factor matrices from the original tensor (no sequential truncation), then computes the core tensor at the end. This is the standard approach from De Lathauwer et al. (2000).\n\nThe ST-HOSVD is generally preferred: it requires fewer operations and typically yields a better approximation.\n\n\n\n\n\n","title":"TensorKitchen.thosvd"},{"category":"function","location":"tucker/#TensorKitchen.optimal_mode_order","page":"Tucker","text":"optimal_mode_order(dims::Tuple, ranks::Tuple) -> Vector{Int}\n\nHeuristic for choosing a good processing order for ST-HOSVD.\n\nFollowing Section 6.3 of Vannieuwenhoven et al., a good heuristic is to process modes in increasing order of nₖ / rₖ (i.e., process the most compressible modes first). When ranks are not known a priori, processing from smallest to largest dimension is a reasonable default.\n\n\n\n\n\n","title":"TensorKitchen.optimal_mode_order"},{"category":"function","location":"tucker/#TensorKitchen.error_bound","page":"Tucker","text":"error_bound(td::TuckerResult) -> Float64\n\nCompute the a posteriori error bound from Theorem 6.4: ‖A - Â‖²F = Σₖ Σ{j > rₖ} σ²{k,j} where σ{k,j} are the singular values at step k of the ST-HOSVD.\n\nThis is exact (not just a bound) for the ST-HOSVD.\n\n\n\n\n\n","title":"TensorKitchen.error_bound"},{"category":"function","location":"tucker/#TensorKitchen.tucker_hosvd","page":"Tucker","text":"tucker_hosvd(A, ranks) -> (core, factors)\n\nCompute Tucker decomposition via Higher-Order SVD (HOSVD).\n\nFor each mode, computes SVD of the unfolded tensor and takes the top ranks[mode] left singular vectors as the factor matrix. The core tensor is computed by projecting the original tensor onto these factor spaces.\n\nArguments\n\nA: Input tensor\nranks: Tuple of target ranks for each mode\n\nReturns\n\ncore: Core tensor\nfactors: Vector of factor matrices (one per mode)\n\n\n\n\n\n","title":"TensorKitchen.tucker_hosvd"},{"category":"function","location":"tucker/#TensorKitchen.reconstruct_tucker","page":"Tucker","text":"reconstruct_tucker(core, factors) -> Array\n\nReconstruct the tensor from Tucker decomposition: core ×₁ factors[1] ×₂ factors[2] ⋯\n\n\n\n\n\n","title":"TensorKitchen.reconstruct_tucker"},{"category":"section","location":"cpd/#CPD","page":"CPD","text":"Here is how to approximate a tensor A by a CPD of rank r.\n\njulia> using TensorKitchen\njulia> A = randn(20, 15, 10)\njulia> r = 35\njulia> res = cpd(A, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.4359141301703327\n\nNow, res contains a CP approximation of the 3-way tensor A,\n\nhat A = sum_i=1^r lambda_i a_i otimes b_i otimes c_i\n\nIt approximates A with relative error about 0.436.\n\nWe access the decomposition as follows.\n\nλ = weights(res)\nU = factors(res)\n\nHere, U is a triple of matrices (ABC), where the columns of A are the a_i and so on. These are called factor matrices.\n\nWe get the whole reconstructed tensor by \n\n = reconstruct(res)","title":"CPD"},{"category":"section","location":"cpd/#CPD-Docs","page":"CPD","text":"","title":"CPD Docs"},{"category":"function","location":"cpd/#TensorKitchen.cpd","page":"CPD","text":"cpd(A, r, opts::CPDOpts)\ncpd(A, r; kwargs...)\n\nRank-r CP decomposition of A; :als converges linearly with rate ≈ 0.83 (~200 iters to machine precision on exact low-rank CP).\n\n\n\n\n\n","title":"TensorKitchen.cpd"},{"category":"function","location":"cpd/#TensorKitchen.nncpd","page":"CPD","text":"nncpd(A; r=nothing, kwargs...) -> CPDResult\n\nNonnegative CP decomposition. If r is omitted, uses the same rank heuristic as cpd(A; r=nothing, ...).\n\n\n\n\n\nnncpd(A, r; kwargs...) -> CPDResult\n\nNonnegative CP decomposition with its own frontend defaults.\n\n\n\n\n\n","title":"TensorKitchen.nncpd"},{"category":"type","location":"cpd/#TensorKitchen.CPDResult","page":"CPD","text":"CPDResult{T}\n\nResult of a Canonical Polyadic Decomposition.\n\ncomponents: vector of RankOneTensor\ncost, rel_error, grad_norm, iterations, converged, solver: metadata\nsolver_info: lightweight solver diagnostics (for example line-search/evaluation counts)\n\n\n\n\n\n","title":"TensorKitchen.CPDResult"},{"category":"type","location":"cpd/#TensorKitchen.RankOneTensor","page":"CPD","text":"RankOneTensor{T}\n\nRepresentation of a rank-one tensor λ · u₁ ⊗ u₂ ⊗ … ⊗ u_d.\n\nλ: scalar weight\nvectors: length-d vector of factor vectors (one per mode)\n\nNot a manifold object; used for CP representation and contractions.\n\n\n\n\n\n","title":"TensorKitchen.RankOneTensor"},{"category":"method","location":"cpd/#TensorKitchen.weights-Tuple{CPDResult}","page":"CPD","text":"weights(r::CPDResult) -> Vector{T}\n\nReturn the per-component scalar weights [λ_1, …, λ_r].\n\n\n\n\n\n","title":"TensorKitchen.weights"},{"category":"method","location":"cpd/#TensorKitchen.comp_weight-Tuple{CPDResult}","page":"CPD","text":"comp_weight(r::CPDResult)\n\nReturn component weights from solver_info(r) when available, otherwise fall back to weights.\n\n\n\n\n\n","title":"TensorKitchen.comp_weight"},{"category":"method","location":"cpd/#TensorKitchen.factors-Tuple{CPDResult}","page":"CPD","text":"factors(r::CPDResult) -> Vector{Matrix{T}}\n\nReturn the mode-wise factor matrices (A, B, C, …); column k of mode m is the m-th factor vector of the k-th rank-one component.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"cpd/#TensorKitchen.reconstruct-Tuple{CPDResult}","page":"CPD","text":"reconstruct(r::CPDResult)\n\nReconstruct the tensor: ∑k λk · (U1[:,k] ⊗ U2[:,k] ⊗ …).\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"function","location":"cpd/#TensorKitchen.reconstruct_cp_rank1","page":"CPD","text":"Rank-1 CP: λ · u₁ ⊗ u₂ ⊗ ... ⊗ u_d via ncon.\n\n\n\n\n\n","title":"TensorKitchen.reconstruct_cp_rank1"},{"category":"function","location":"cpd/#TensorKitchen.reconstruct_cpd_rankr","page":"CPD","text":"Rank-r CP from (λ, U). Batched broadcast + sum over r.\n\n\n\n\n\nRank-r CP from components. Validates structure, delegates to (λ,U).\n\n\n\n\n\n","title":"TensorKitchen.reconstruct_cpd_rankr"},{"category":"function","location":"cpd/#TensorKitchen.normalize_components!","page":"CPD","text":"normalize_components!(factors, λ, policy=SeparateLambdaNormalization())\n\nNormalize CP factor matrices and weights in place according to policy, preserving the represented CP tensor.\n\n\n\n\n\nnormalize_components!(point::CPDPoint, policy=SeparateLambdaNormalization())\n\nNormalize a canonical CPDPoint in place.\n\nThis is the preferred backend entry point for normalization, since it is independent of solver/manifold layout. To normalize an actual optimization iterate, combine it with cpd_point and pack_cpd_point, or use post_step!.\n\n\n\n\n\n","title":"TensorKitchen.normalize_components!"},{"category":"function","location":"cpd/#TensorKitchen.normalize_components","page":"CPD","text":"normalize_components(point::CPDPoint, policy=SeparateLambdaNormalization())\n\nReturn a normalized copy of a canonical CPDPoint.\n\n\n\n\n\n","title":"TensorKitchen.normalize_components"},{"category":"section","location":"#TensorKitchen.jl-Documentation","page":"Home","text":"TensorKitchen.jl is a Julia package for tensor decompositions.\n\nCPD documentation\nTucker documentation\nBTD documentation\nJoin decomposition documentation\nUtilities\nPipeline","title":"TensorKitchen.jl Documentation"},{"category":"section","location":"#Notes","page":"Home","text":"The package is currently at a pre-alpha stage. \n\nThe implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.\n\nWhat currently works is \n\nCanonical Polyadic Decomposition (CPD)\nTucker Decomposition\nNonnegative Canonical Polyadic Decomposition (NNCPD)\nBlock Term Decomposition (BTD)\nJoin Decompositions\n\n\n\nThe next updates will include \n\nDocumentation\nImproved User Interface\nProgressMeter\nGPU Support \nLL1 Decomposition (3-way specialized BTD)\nSymmetric CP / Waring Decomposition\nPartially Symmetric CP\nTensor Trains","title":"Notes"},{"category":"section","location":"#References","page":"Home","text":"","title":"References"},{"category":"section","location":"#General-Tensor-Decomposition","page":"Home","text":"Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, \"Tensor decompositions and applications,\" SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.","title":"General Tensor Decomposition"},{"category":"section","location":"#Tucker-Methods","page":"Home","text":"HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"A multilinear singular value decomposition,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.\nST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, \"A new truncation strategy for the higher-order singular value decomposition,\" SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.\nHOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.","title":"Tucker Methods"},{"category":"section","location":"#Block-and-Structured-Models-(BTD-/-LL1)","page":"Home","text":"Block-term decomposition (BTD): L. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.\nL. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.\nBTD-ALS: L. De Lathauwer and D. Nion, \"Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms,\" SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.","title":"Block and Structured Models (BTD / LL1)"},{"category":"section","location":"#Join-decompositions","page":"Home","text":"Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, \"The condition number of join decompositions,\" SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).","title":"Join decompositions"},{"category":"section","location":"#Riemannian-Optimization-and-Julia-Ecosystem","page":"Home","text":"Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, \"A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem,\" SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).\nRiemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.\nJulia manifold optimization ecosystem: R. Bergmann et al., ManifoldsBase.jl, Manifolds.jl, and Manopt.jl.","title":"Riemannian Optimization and Julia Ecosystem"},{"category":"section","location":"utils/#General-Utilities","page":"Utilities","text":"","title":"General Utilities"},{"category":"function","location":"utils/#TensorKitchen.save_result","page":"Utilities","text":"save_result(path, result)\n\nSerialize a result to disk (Julia Serialization).\n\n\n\n\n\n","title":"TensorKitchen.save_result"},{"category":"function","location":"utils/#TensorKitchen.load_result","page":"Utilities","text":"load_result(path)\n\nLoad a result serialized by save_result.\n\n\n\n\n\n","title":"TensorKitchen.load_result"}]
+[{"category":"section","location":"references/#References","page":"References","text":"","title":"References"},{"category":"section","location":"references/#General-Tensor-Decomposition","page":"References","text":"Tensor decompositions (CP, Tucker): T. G. Kolda and B. W. Bader, \"Tensor decompositions and applications,\" SIAM Review, vol. 51, no. 3, pp. 455–500, 2009.","title":"General Tensor Decomposition"},{"category":"section","location":"references/#Tucker-Methods","page":"References","text":"HOSVD: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"A multilinear singular value decomposition,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1253–1278, 2000.\nST-HOSVD: N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, \"A new truncation strategy for the higher-order singular value decomposition,\" SIAM J. Sci. Comput., vol. 34, no. 2, pp. A1027–A1052, 2012.\nHOOI: L. De Lathauwer, B. De Moor, and J. Vandewalle, \"On the best rank-1 and rank-(R1,R2,...,R_N) approximation of higher-order tensors,\" SIAM J. Matrix Anal. Appl., vol. 21, no. 4, pp. 1324–1342, 2000.","title":"Tucker Methods"},{"category":"section","location":"references/#Block-and-Structured-Models-(BTD-/-LL1)","page":"References","text":"Block-term decomposition (BTD): L. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1022–1032, 2008.\nL. De Lathauwer, \"Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness,\" SIAM J. Matrix Anal. Appl., vol. 30, no. 3, pp. 1033–1066, 2008.\nBTD-ALS: L. De Lathauwer and D. Nion, \"Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms,\" SIAM Journal on Matrix Analysis and Applications, vol. 30, no. 3, pp. 1067–1083, 2008. PDF.","title":"Block and Structured Models (BTD / LL1)"},{"category":"section","location":"references/#Join-decompositions","page":"References","text":"Conditioning of join decompositions: P. Breiding and N. Vannieuwenhoven, \"The condition number of join decompositions,\" SIAM Journal on Matrix Analysis and Applications, vol. 39, no. 1, pp. 287–309, 2018. arXiv:1611.08117 (PDF).","title":"Join decompositions"},{"category":"section","location":"references/#Riemannian-Optimization-and-Julia-Ecosystem","page":"References","text":"Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation: P. Breiding and N. Vannieuwenhoven, \"A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem,\" SIAM Journal on Optimization, vol. 28, no. 3, pp. 2435–2465, 2018. arXiv:1709.00033 (PDF).\nRiemannian optimization: P.-A. Absil, R. Mahony, and R. Sepulchre, Optimization Algorithms on Matrix Manifolds. Princeton University Press, 2008.\nJulia manifold optimization ecosystem: R. Bergmann et al., ManifoldsBase.jl, Manifolds.jl, and Manopt.jl.","title":"Riemannian Optimization and Julia Ecosystem"},{"category":"section","location":"PIPELINE/#TensorKitchen-Pipeline","page":"Pipeline","text":"This document explains how public APIs route into models, solvers, and result converters.","title":"TensorKitchen Pipeline"},{"category":"section","location":"PIPELINE/#Public-entry-points","page":"Pipeline","text":"cpd(A, r; ...) -> CPDResult\nnncpd(A, r; ...) -> CPDResult\nbtd(A, blocks, ranks; ...) -> BTDResult\ntucker(A, ranks; method=...) -> TuckerResult\napprox(...) -> ApproxResult or auto-routed CPDResult/BTDResult","title":"Public entry points"},{"category":"section","location":"PIPELINE/#Default-behavior-(quick-reference)","page":"Pipeline","text":"cpd(A, r):\ninit = :alswarm\nsolver = :rgd\nnncpd(A, r):\ninit = :alswarm\nsolver = :rgd\nbtd(A, blocks, ranks):\ninit = :alswarm\nwarm_steps = 200\nwarm_init = BTDHOSVDMultistartInit(candidates=64, screening_steps=10, block_maxiter=12)\nwarm_rel_error_gate = nothing (run manifold refinement by default; set e.g. 5e-2 to short-circuit on poor warm starts)\nsolver = :rgd\nfinal BTD-ALS polish enabled by default for non-ALS solvers\nmax_stagnation_restarts = 1 (retry with stronger multistart when ALS fit-change stalls at high rel-error)\ntucker(A, ranks):\nmethod = :sthosvd\napprox(model::JoinModel):\ninit = :alswarm\nwarm_steps = 500\nsolver = :rgd","title":"Default behavior (quick reference)"},{"category":"section","location":"PIPELINE/#Core-execution-architecture","page":"Pipeline","text":"Most optimization APIs share this core pattern:\n\nBuild a model (JoinModel + backend)\nCall _solve_model(...)\nConvert to a public result struct\n\n_solve_model lives in src/solvers/solve_dispatch.jl and is the common symbol-to-solver dispatch layer (:rgd, :rcg, :lbfgs, :als, :btd_tsd).","title":"Core execution architecture"},{"category":"section","location":"PIPELINE/#API-flows","page":"Pipeline","text":"","title":"API flows"},{"category":"section","location":"PIPELINE/#CPD-(cpd,-nncpd)","page":"Pipeline","text":"cpd(A, r; ...):\n\nBuild JoinModel(A, r; geometry=...) with CPDBackend\nNormalize/validate options (solver, geometry, gradient_mode, normalization policy)\nSolve through _solve_model(...)\nOptionally run nonnegative ALS polishing (for selected nonnegative paths)\nConvert to CPDResult\n\nNotes:\n\n:als means CP-ALS.\nManifold solvers (:rgd, :rgd_fixed, :rcg, :lbfgs) share dispatch with other pipelines.","title":"CPD (cpd, nncpd)"},{"category":"section","location":"PIPELINE/#BTD-(btd)","page":"Pipeline","text":"btd(A, blocks, ranks; ...):\n\nBuild a uniform Tucker family via TuckerJoin(...)\nWrap as JoinModel with BTDBackend\nChoose effective initializer:\nsolver == :als: use requested init directly (default multistart)\nsolver != :als: use BTDALSWarmStartInit(...) so first-order methods start from a good BTD-ALS warm point\nIf the warm-start rel-error exceeds warm_rel_error_gate, return the warm BTD-ALS result directly\nOtherwise solve through _solve_model(...)\nIf solver != :als, optionally polish with BTD-ALS (btd_als_polish_maxiter)\nConvert to BTDResult\n\nPolish step usefulness (brief):\n\nUsually helpful for a small final rel_error reduction after RGD converges near a good basin.\nMost useful for quality-focused runs (benchmarks, final fits).\nCan be skipped for speed-sensitive runs (btd_als_polish_maxiter=0) when small extra gains are not worth runtime.\n\nBTD-specific initialization options:\n\n:hosvd: sequential block initialization on residual\n:hosvd_multistart: HOSVD subspace split candidates, optional screening ALS, keep lowest-cost candidate\n:alswarm: short BTD-ALS warm-start wrapper around base initializer\n\nBTD-ALS stabilization behavior:\n\nTracks per-iteration fit change (|rel_t - rel_{t-1}|)\nDetects stagnation when fit change is tiny but rel_error remains high\nCan restart from fresh multistart pool (max_stagnation_restarts)\nReports true final Riemannian gradient norm (grad_norm) instead of a placeholder","title":"BTD (btd)"},{"category":"section","location":"PIPELINE/#Tucker-(tucker)","page":"Pipeline","text":"tucker(A, ranks; method=...) does not use _solve_model. It dispatches directly to decomposition routines:\n\n:sthosvd\n:hooi","title":"Tucker (tucker)"},{"category":"section","location":"PIPELINE/#Generic-approx(...)-routing","page":"Pipeline","text":"approx(manifolds, target; dispatch=:auto) routes by manifold family:\n\nuniform Manifolds.Segre -> cpd(...)\nuniform Manifolds.Tucker matching target shape/rank -> btd(...)\nmixed or non-uniform family -> generic JoinModel(...) path -> ApproxResult\n\ndispatch=:cpd, :btd, and :generic force behavior.","title":"Generic approx(...) routing"},{"category":"section","location":"PIPELINE/#Result-types-and-post-processing","page":"Pipeline","text":"CPDResult\nBTDResult\nTuckerResult\nApproxResult\n\nCommon utilities:\n\nreconstruct(result)\nrel_error(A, result)","title":"Result types and post-processing"},{"category":"section","location":"PIPELINE/#File-map","page":"Pipeline","text":"API entry points: src/api/approx.jl, src/api/cpd.jl, src/api/nncpd.jl, src/api/btd.jl\nRouting helpers: src/dispatch/approx_routing.jl\nSolver dispatch core: src/solvers/solve_dispatch.jl\nBTD backend/init details: src/btd/model.jl, src/solvers/btd_als.jl","title":"File map"},{"category":"section","location":"join/#Join-Decomposition","page":"Join","text":"A Join Decomposition of a vector xinmathbb R^N is a decomposition of the form x = x_1+cdots+x_r, where x_iin M_i and M_isubset mathbb R^N is a given embedded manifold. \n\nFor instance, we can approximate a point p = (12 04)inmathbb R^2 by x=x_1+x_2, where x_1x_2in S^1 are points on the circle:\n\njulia> using Manifolds\njulia> p = [1.2, 0.4]\njulia> S = Sphere(1)\njulia> join_res = approx((S, S), p)\nApproxResult{Float64}\n Components: 2\n Rel. error: 0.0007114699550529457\n\nWe access the decomposition as follows.\n\ncomponents(join_res)\nreconstruct(join_res)\n\n ","title":"Join Decomposition"},{"category":"section","location":"join/#Generic-Join-Approximation","page":"Join","text":"approx(...) is the main frontend for join decomposition. It works in two stages:\n\nbuild an initial point\nrefine it with the selected solver","title":"Generic Join Approximation"},{"category":"section","location":"join/#Supported-Forms","page":"Join","text":"approx(model; kwargs...) Solve an already constructed JoinModel.\napprox(manifolds, target; kwargs...) Build a join from a tuple/vector of component manifolds.\napprox(M::ProductManifold, target; kwargs...) Use the product-manifold factors as join components.\napprox(base, r, target; kwargs...) Repeat a single base manifold r times to build a join.\napprox(base, target; kwargs...) Build a single-component join.\n\nExamples:\n\njulia> target = [1.2, 0.4, -0.3]\njulia> model = JoinModel(Manifolds.Sphere(2), target)\njulia> approx(model; maxiter = 50, verbose = false)\nApproxResult{Float64}\n Components: 1\n Rel. error: 0.23076923076923075\n\njulia> target = randn(2, 3)\njulia> approx((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)\nCPDResult{Float64}\n\njulia> target = [1.2, 0.4, -0.3]\njulia> approx(Manifolds.Sphere(2), 2, target; verbose = false)\nApproxResult{Float64}","title":"Supported Forms"},{"category":"section","location":"join/#Routing","page":"Join","text":"With dispatch = :auto:\n\nuniform Manifolds.Segre components route to cpd(...)\nuniform Manifolds.Tucker components route to btd(...)\notherwise the generic JoinModel(...) path is used and ApproxResult is returned\n\nYou can also force the route explicitly:\n\ndispatch = :generic\ndispatch = :cpd\ndispatch = :btd\n\nForced routing is validated:\n\ndispatch = :cpd requires all components to be Manifolds.Segre with identical factor_dims\ndispatch = :btd requires all components to be Manifolds.Tucker with identical factor_dims and compatible multilinear rank","title":"Routing"},{"category":"section","location":"join/#Generic-Join-Options","page":"Join","text":"For the generic join path:\n\ninit = :random Default initializer. Built-in initializer support depends on the component manifolds.\nsolver = :rgd Supported generic-join solver options are:\n:rgd\n:rgd_fixed\n:rcg\n:lbfgs\n\nOther common options:\n\np0 = nothing\nmaxiter = 500\nstepsize = 1.0\ntol = 1e-6\ngradient_mode = :riemannian\nverbose = true\nvector_transport_method = nothing\n\nBuilt-in init support by component family:\n\nSphere: :random, :deterministic, :target\nSegre: :random, :deterministic\nTucker: :random, :tucker, :tucker_diag, :sthosvd\nother manifolds: :random","title":"Generic Join Options"},{"category":"section","location":"join/#Notes","page":"Join","text":"Generic joins require every component manifold to embed into the same flattened ambient length as target.\nsolver = :als is not available for a truly generic JoinModel. ALS may still be available when approx(...) auto-routes to cpd(...) or btd(...).","title":"Notes"},{"category":"section","location":"join/#Join-Decomposition-Docs","page":"Join","text":"","title":"Join Decomposition Docs"},{"category":"function","location":"join/#TensorKitchen.approx","page":"Join","text":"Generic Join Approximation\n\napprox(...) is the main frontend for join decomposition, which works in two stages:\n\nbuild an initial point\nrefine it with the selected solver\n\nSupported Forms\n\napprox(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.\nmodel means an already constructed JoinModel.\nIt fully fixes the decomposition structure and target.\napprox(model; ...) just solves that model.\nExample: If you want to approximate a point on the sphere, you can build a JoinModel and then use approx to refine it.\n\ntarget = [1.2, 0.4, -0.3]\nmodel = JoinModel(Manifolds.Sphere(2), target)\napprox(model; maxiter = 100, verbose = false)\n# returns an ApproxResult\n\napprox(manifolds, target; kwargs...) : Builds a generic join model from a tuple of existing manifolds and routes to according to dispatch. Example:\n\ntarget = randn(2, 3)\napprox((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)\n# This builds a join with 2 copies of Manifolds.Segre((2, 3))\".\n\napprox(M::ProductManifold, target; kwargs...) : Uses the factors of a product manifold and route to CPD, BTD, or the generic join solver according to dispatch. \nM::ProductManifold means that you already have a product manifold whose factors are the join components.\napprox(M, target; ...) uses those factors directly.\nIt is more explicit than base, because the components are already listed.\nExample:\n\n# Example 1\ntarget = randn(2, 3)\nM = ProductManifold(Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3)))\napprox(M, target; verbose = false)\n# returns an CPDResult\n\n# Example 2\ntarget = randn(4, 3, 2)\nM = ProductManifold(\n Manifolds.Tucker((4, 3, 2), (2, 2, 2)),\n Manifolds.Tucker((4, 3, 2), (2, 2, 2)),\n)\napprox(M, target; verbose = false)\n# returns an BTDResult\n\napprox(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\n\ndispatch 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.\n\ntarget = randn(2, 3)\napprox(Manifolds.Segre((2, 3)), 2, target; verbose = false)\n# returns an CPDResult\n\napprox(base, target; kwargs...) : Builds a single-component generic join and route to the generic join solver. Example:\n\ntarget = [1.2, 0.4, -0.3] \napprox(Manifolds.Sphere(2), target; verbose = false)\n# returns an ApproxResult\n\nBy default, approx auto-routes by manifold family:\nuniform Manifolds.Segre summands calls cpd(...)\nuniform Manifolds.Tucker summands calls btd(...)\notherwise calls JoinModel(...) and returns a ApproxResult\n\nReturn Types\n\nDepending on the manifold family, approx(...) may return:\nApproxResult for the generic join path\nCPDResult when auto-routed to cpd(...)\nBTDResult when auto-routed to btd(...)\n\nMain Options\n\nFor the generic join path:\n\ninit = :random: Sets the algorithm to find the initial point.\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\nrgd (default): Riemannian gradient descent\nrgd_fixed: Riemannian gradient descent with fixed step size\nrcg: Riemannian conjugate gradient\nlbfgs: Limited-memory quasi-Newton\n\n##Notes##\n\n: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.\nwarm_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.\nFor generic mixed joins, use manifold solvers such as :rgd, :rcg, or :lbfgs.\n\n\n\n\n\napprox(M::ProductManifold, target; dispatch=:auto, kwargs...)\n\nUse the factors of a product manifold as join components and route to CPD, BTD, or the generic join solver according to dispatch.\n\n\n\n\n\napprox(base::Manifolds.Segre, r, target; dispatch=:auto, kwargs...)\n\nBuild a rank-r Segre join and route to the CPD pipeline unless generic dispatch is explicitly requested.\n\n\n\n\n\napprox(base::Manifolds.Tucker, r, target; dispatch=:auto, kwargs...)\n\nBuild a r-block Tucker join and route to the BTD pipeline unless generic dispatch is explicitly requested.\n\n\n\n\n\napprox(base::AbstractManifold, r, target; dispatch=:auto, kwargs...)\n\nFallback rank-r join constructor for non-specialized manifolds. Forced CPD or BTD dispatch is rejected because the base manifold family is not known.\n\n\n\n\n\napprox(base::AbstractManifold, target; dispatch=:auto, kwargs...)\n\nSingle-component generic approximation fallback. Use this when no CPD/BTD family-specific routing is intended.\n\n\n\n\n\n","title":"TensorKitchen.approx"},{"category":"type","location":"join/#TensorKitchen.ApproxResult","page":"Join","text":"ApproxResult{T}\n\nGeneric result of approx(manifolds, target) (generic join decomposition).\n\npoint: final point on the join manifold\ncomponents: extracted component descriptions\ncost: the value of the optimization objective at the final returned point, that is typically the least-squares objective.\nrel_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\ngrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is typically the Riemannian gradient norm\niterations: number of iterations used\nconverged: whether the decomposition converged\nsolver: solver used for the decomposition\nsolver_info: solver-specific diagnostics/metadata (NamedTuple)\n\n\n\n\n\n","title":"TensorKitchen.ApproxResult"},{"category":"method","location":"join/#TensorKitchen.reconstruct-Tuple{ApproxResult}","page":"Join","text":"reconstruct(res::ApproxResult)\nreconstruct(res::BTDResult)\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"function","location":"join/#TensorKitchen.join_product","page":"Join","text":"join_product(base, r) constructs a ProductManifold\n\nConstruct 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.\n\n\n\n\n\n","title":"TensorKitchen.join_product"},{"category":"function","location":"join/#TensorKitchen.SegreProduct","page":"Join","text":"SegreProduct(dims, r)\n\nProduct manifold Manifolds.Segre(dims) × ... × Manifolds.Segre(dims) (r factors). Each component point uses the Manifolds.Segre layout [[λ], x₁, …, x_d].\n\n\n\n\n\n","title":"TensorKitchen.SegreProduct"},{"category":"section","location":"btd/#Block-Term-Decomposition","page":"BTD","text":"A block term decomposition (BTD) with r blocks writes\n\nhat A = sum_i=1^r A_i\n\nwhere each block A_i is represented as a Tucker decomposition. At present, only homogeneous BTDs are supported, that is, all blocks must have the same multilinear rank.\n\nTo compute a block term decomposition of A with 10 blocks, each of multilinear rank (5, 4, 3), use\n\njulia> r = 10\njulia> mlrank = (5, 4, 3)\njulia> btd_res = btd(A, r, mlrank)\nBTDResult{Float64}\n Blocks: 10\n Rel. error: 0.2551559591470521\n\nThe blocks of btd_res can be obtained as follows:\n\nblocks = blocks(btd_res)\n\nEach block is represented as a Tucker decomposition, so we can access its core and factor matrices via:\n\nblk = blocks[1]\ncore(blk)\nfactors(blk)","title":"Block Term Decomposition"},{"category":"section","location":"btd/#BTD-Docs","page":"BTD","text":"","title":"BTD Docs"},{"category":"function","location":"btd/#TensorKitchen.btd","page":"BTD","text":"btd(A, blocks, ranks; kwargs...) returns a BTDResult\n\nComputes a block-term decomposition of A with blocks Tucker blocks, each with multilinear rank ranks. The solver first finds an initial point, then refines it. Returns a BTDResult.\n\nMain Options\n\ninit = :auto: Sets the algorithm to find the initial point. Possible options are:\n:auto: Uses a default BTD initializer. For solver = :als, this uses BTDHOSVDMultistartInit; otherwise, it uses an ALS warm start.\n:alswarm: Runs ALS first and uses the result as the initial point for manifold solver refinement.\ncustom initializer objects, e.g. BTDHOSVDMultistartInit(...).\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\n:rgd (default): Riemannian gradient descent.\n:als: Alternating least squares.\n:rcg: Riemannian conjugate gradient.\n:lbfgs: Limited-memory quasi-Newton refinement.\n\nExtended Options\n\ninit_point = nothing: Explicit initial point. If provided, it overrides the default initial point.\nwarm_init = BTDHOSVDMultistartInit(...): Searches for initial points using HOSVD for :alswarm, optionally screens them with short ALS runs, and returns the lowest-cost candidate.\nwarm_steps = 200: Once finding the best initial point, it runs this many ALS iterations to refine the initial point.\nwarm_block_method = :hooi or :sthosvd: Block update method used during warm start.\nwarm_block_maxiter = 20: Maximum number of inner iterations for each block update during warm start.\nwarm_rel_error_gate = 5e-2: Skips manifold refinement if the warm-start error is above this threshold.\nmaxiter = 500: Maximum number of Riemannian gradient descent iterations.\nstepsize = 0.01: Initial step size for line search in Riemannian gradient descent.\ntol = 1e-6: Convergence tolerance.\ngradient_mode = :riemannian: rgrad can be directly applied for manifold solvers. \nIf the model has a direct rgrad, it uses that.\nOtherwise it computes egrad and projects it to the tangent space.\nThis behavior is in src/solvers/abstract.jl (line 289).\nverbose = true: Enables progress output.\nblock_method = :hooi or :sthosvd: Block update method used for manifold solvers.\nblock_maxiter = 30: Maximum number of inner block-update iterations for manifold solvers.\nbtd_als_polish_maxiter = nothing: Number of final ALS polishing iterations. If nothing, an automatic budget is selected.\nThese settings are a robustness/quality feature for BTD-ALS. They are not used for manifold solvers.\nmax_stagnation_restarts = 1: More retries after a bad stagnated ALS pass. Higher values can improve solution quality, but increases runtime.\nstagnation_rel_error = 1e-4: This is a “bad final error” cutoff, not an improvement threshold. Lower value means restarts trigger more easily.\nrestart_candidates = 24: It usually improves chance of finding a better basin, but cost grows roughly linearly.\nrestart_screening_steps = 5: It uses this many quick ALS steps to screen restart candidates.\nrestart_block_maxiter = 20: Inner block-update limit during restart screening for BTD-ALS.\nrestart_seed = nothing: Optional random seed for restart generation for BTD-ALS.\n\nExample\n\njulia> using Random\njulia> Random.seed!(0)\njulia> A = randn(20, 15, 10); blocks = 10; ranks = (5, 4, 3)\njulia> res = btd(A, blocks, ranks; verbose = false)\nBTDResult{Float64}\n Blocks: 10\n Rel. error: 0.2625821087015455\n\n\n\n\n\n","title":"TensorKitchen.btd"},{"category":"type","location":"btd/#TensorKitchen.BTDResult","page":"BTD","text":"BTDResult{T}\n\nResult of block-term decomposition (btd); block components expose Tucker structure through accessors like core(blk), factors(blk), and blk.tensor.\n\nsolver_info: solver-specific diagnostics/metadata (NamedTuple). Typical keys include BTD-ALS restart diagnostics (total_iterations, stagnation_restarts, restart_rel_error_history) and BTD-TSD run settings (schedule, block_repeats, block_count, stepsize).\n\n\n\n\n\n","title":"TensorKitchen.BTDResult"},{"category":"function","location":"btd/#TensorKitchen.blocks","page":"BTD","text":"blocks(r::BTDResult)\n\nReturn the Tucker block components of a block-term decomposition result.\n\n\n\n\n\n","title":"TensorKitchen.blocks"},{"category":"method","location":"btd/#TensorKitchen.reconstruct-Tuple{BTDResult}","page":"BTD","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"tucker/#Tucker-Decomposition","page":"Tucker","text":"Approximating A by a Tucker decomposition\n\nhat A = C times_1 U times_2 V times_3 W\n\nwith multilinear rank mlrank can be computed as follows.\n\njulia> mlrank = (5, 4, 3)\njulia> tucker_res = tucker(A, mlrank)\nTuckerResult{Float64, 3}\n Original size: (20, 15, 10)\n Core size: (5, 4, 3)\n Multilinear rank: (5, 4, 3)\n Compression: 12.0x\n\nThe core C and the factor matrices (U V W) of the decomposition can be accessed as follows.\n\ncore(tucker_res)\nfactors(tucker_res)","title":"Tucker Decomposition"},{"category":"section","location":"tucker/#Tucker-Docs","page":"Tucker","text":"","title":"Tucker Docs"},{"category":"function","location":"tucker/#TensorKitchen.tucker","page":"Tucker","text":"tucker(A, ranks; method = :sthosvd, kwargs...) returns a TuckerResult\n\nComputes a Tucker decomposition of A with multilinear rank ranks.\n\nMain Options\n\nmethod = :sthosvd: Sets the Tucker decomposition algorithm. Possible options are:\n:sthosvd (default): Sequentially Truncated HOSVD. A direct one-pass decomposition, mainly used as a fast standalone Tucker approximation or as the default initializer for :hooiFast, deterministic, and usually a good initial point.\n:hooi: High-Order Orthogonal Iteration. Iteratively refines the Tucker factors, initialized by ST-HOSVD by default.\n\nExtended Options\n\nFor method = :hooi:\n\nmaxiter = 50: Maximum number of HOOI iterations.\ntol = 1e-8: Convergence tolerance based on change in relative reconstruction error.\ninit = :sthosvd\n:sthosvd: Uses ST-HOSVD to initialize the Tucker factors.\nTuckerResult: Uses an existing Tucker decomposition as the initial point.\n\nExample\n\njulia> using Random\njulia> Random.seed!(0)\njulia> A = randn(20, 15, 10); ranks = (5, 4, 3)\njulia> res = tucker(A, ranks; verbose = false)\nTuckerResult{Float64, 3}\n Original size: (20, 15, 10)\n Core size: (5, 4, 3)\n Multilinear rank: (5, 4, 3)\n Compression: 12.0x\n\n\nThe core tensor and factor matrices can be accessed by\n\ncore(res)\nfactors(res)\n\nA tensor approximation can be reconstructed by\n\nreconstruct(res)\n\nor equivalently\n\nreconstruct_tucker(core(res), factors(res))\n\nNotes\n\n:sthosvd is not an iterative solver. It directly returns a TuckerResult and does not expose solver-style outputs such as iteration counts or convergence diagnostics.\n:hooi is the iterative refinement method in the current Tucker implementation.\nImportant distinction: For the current Tucker implementation, do not use solver = :rgd, init = :auto, or manifold solvers like RGD, RCG, LBFGS, etc.\n\n\n\n\n\n","title":"TensorKitchen.tucker"},{"category":"type","location":"tucker/#TensorKitchen.TuckerResult","page":"Tucker","text":"TuckerResult{T, N}\n\nStores a Tucker decomposition: core tensor and factor matrices.\n\ncore::Array{T,N} — core tensor\nfactors::Vector{Matrix{T}} — orthonormal factor matrices\nprocessing_order::Vector{Int} — order modes were processed\nsingular_values::Vector{Vector{T}} — singular values per truncation\n\n\n\n\n\n","title":"TensorKitchen.TuckerResult"},{"category":"method","location":"tucker/#TensorKitchen.core-Tuple{TuckerResult}","page":"Tucker","text":"core(td::TuckerResult)\n\nReturn the Tucker core tensor for a Tucker result.\n\n\n\n\n\n","title":"TensorKitchen.core"},{"category":"method","location":"tucker/#TensorKitchen.factors-Tuple{TuckerResult}","page":"Tucker","text":"factors(td::TuckerResult)\n\nReturn the Tucker factor matrices.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"tucker/#TensorKitchen.multilinear_rank-Tuple{TuckerResult}","page":"Tucker","text":"multilinear_rank(td::TuckerResult)\n\nReturn the Tucker multilinear rank tuple, i.e. the size of the core tensor.\n\n\n\n\n\n","title":"TensorKitchen.multilinear_rank"},{"category":"method","location":"tucker/#TensorKitchen.factor_dims-Tuple{TuckerResult}","page":"Tucker","text":"factor_dims(td::TuckerResult)\n\nReturn the original mode dimensions represented by the Tucker factor matrices.\n\n\n\n\n\n","title":"TensorKitchen.factor_dims"},{"category":"method","location":"tucker/#TensorKitchen.reconstruct-Tuple{TuckerResult}","page":"Tucker","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"cpd/#CPD","page":"CPD","text":"Here is how to approximate a tensor A by a CPD of rank r.\n\njulia> using TensorKitchen\njulia> A = randn(20, 15, 10)\njulia> r = 35\njulia> res = cpd(A, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.4359141301703327\n\nNow, res contains a CP approximation of the 3-way tensor A,\n\nhat A = sum_i=1^r lambda_i a_i otimes b_i otimes c_i\n\nIt approximates A with relative error about 0.436.\n\nWe access the decomposition as follows.\n\nλ = weights(res)\nU = factors(res)\n\nHere, U is a triple of matrices (ABC), where the columns of A are the a_i and so on. These are called factor matrices.\n\nWe get the whole reconstructed tensor by \n\n = reconstruct(res)","title":"CPD"},{"category":"section","location":"cpd/#CPD-Docs","page":"CPD","text":"","title":"CPD Docs"},{"category":"function","location":"cpd/#TensorKitchen.cpd","page":"CPD","text":"cpd(A, r; kwargs...)\n\nComputes 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.\n\nMain Options\n\ninit = :auto: Sets the algorithm to find the initial point. Possible options are:\n:auto: Uses a default CPD initializer. For solver = :als, this uses TuckerInit; otherwise, it uses an ALS warm start.\n:alswarm: Runs ALS first and uses the result as the initial point for refinement.\ncustomized initial point:\n:tucker (default when solver = :als): Uses a default Tucker initializer.\n:random: Uses a random initial point.\n:hosvd: Uses a HOSVD initial point.\nsolver = :rgd: Sets the algorithm for refinement. Possible options are:\nrgd (default): Riemannian gradient descent\nrgd_fixed: Riemannian gradient descent with fixed step size\nrcg: Riemannian conjugate gradient\nals: Alternating Least Squares\n\nExtended Options\n\np0 = nothing: Explicit initial point. If provided, it overrides the default initial point.\n:alswarm: ALS warm start option.\nwarm_init = TuckerInit(): Before finding the warm start initial point, this sets the good starting point for ALS.\nwarm_steps = 500: Once finding the best initial point from warm_init, it runs this many ALS iterations to refine the initial point.\nmaxiter = 500: Maximum number of Riemannian gradient descent iterations.\nstepsize = 1.0: Initial step size for line search in Riemannian gradient descent.\ntol = 1e-6: Convergence tolerance.\ngradient_mode = :riemannian: Gradient rule for manifold solvers. \nIf the model has a direct rgrad, it uses that.\nOtherwise it computes egrad and projects it to the tangent space.\nThis behavior is in src/solvers/abstract.jl (line 289).\ngeometry = :canonical: Sets the geometry of the manifold. Possible options are:\n:canonical: Standard CPD parameterization with the usual Euclidean factors and canonical Riemannian gradient handling. Best default for general unconstrained CPD.\n:squaring_metric: Nonnegative geometry based on squared latent coordinates. Enforces nonnegativity indirectly, but can become ill-conditioned near zero.\n: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.\n: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.\nverbose = true: Enables progress output.\nnonnegative::Bool = false: Nonnegative CPD option to be selected by the user. (same as nncpd)\npullback_eps = 1e-8: Regularization parameter for pullback-style nonnegative geometries.\n\nNotes\n\nsolver = :als does not use manifold geometry. In that case:\ngeometry must be :canonical\ngradient_mode is ignored except for validation\n:squaring_metric and :softplus_metric require nonnegative = true.\nWhen nonnegative = true, cpd(...) routes to nncpd(...). In that route:\nif solver != :als and geometry is left at :canonical, the effective geometry becomes :softplus_metric\nif stepsize is left at 1.0, the effective default becomes 0.01\nif init = :tucker, the effective initializer becomes :alswarm\n\nExample\n\njulia> A = randn(20, 15, 10); r = 35\njulia> res = cpd(A, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.4359141301703327\n\n\n\n\n\n","title":"TensorKitchen.cpd"},{"category":"function","location":"cpd/#TensorKitchen.nncpd","page":"CPD","text":" nncpd(A, r; kwargs...)\n\nComputes 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.\n\nOptions\n\nThe options are the same as for cpd.\n\nGeometry guide:\n\ngeometry=:softplus_metric Default and usually the safest choice. \ngeometry=:squaring_metric Uses a regularized pullback-inspired geometry induced by the squaring chart.\ngeometry=:canonical Plain nonnegative CP coordinates without the pullback-style manifold geometry. This is the natural choice with solver=:als.\n\nExample\n\njulia> A = randn(20, 15, 10); r = 35;\njulia> B = abs.(A)\njulia> nncpd(B, r)\nCPDResult{Float64}\n Order: 3\n Dimensions: (20, 15, 10)\n Rank: 35\n Rel. error: 0.3765605093526155\n\n\n\n\n\n","title":"TensorKitchen.nncpd"},{"category":"type","location":"cpd/#TensorKitchen.CPDResult","page":"CPD","text":"CPDResult{T}\n\nResult of a Canonical Polyadic Decomposition.\n\nStores the decoded CP representation together with solver diagnostics:\n\ncomponents: rank-one tensor components\nweights: component weights\nfactors: factor matrices\ncost: final objective function value at the returned solution\nrel_error: final relative reconstruction error\ngrad_norm: norm of the final optimization gradient reported by the solver; for manifold solvers this is the Riemannian gradient norm\niterations: number of refinement iterations\nconverged: whether the solver reported convergence\nsolver: solver optimization method used to produce the result\nsolver_info: solver-specific diagnostics/metadata (NamedTuple). Typical keys include:\ninitial_stepsize_eff (RGD), memory_size (LBFGS), cautious_update (LBFGS),\ninitial_scale, linesearch, has_preconditioner (LBFGS), and nncp_pullback_eps (NNCP).\n\n\n\n\n\n","title":"TensorKitchen.CPDResult"},{"category":"method","location":"cpd/#TensorKitchen.weights-Tuple{CPDResult}","page":"CPD","text":"weights(r::CPDResult)\n\nReturn the CP component weights stored in a CPD result.\n\n\n\n\n\n","title":"TensorKitchen.weights"},{"category":"method","location":"cpd/#TensorKitchen.factors-Tuple{CPDResult}","page":"CPD","text":"factors(res::CPDResult)\n\nReturn the CP factor matrices of res as a vector [U₁, U₂, ..., U_N], where each U_m has size size(A, m) × rank.\n\n\n\n\n\n","title":"TensorKitchen.factors"},{"category":"method","location":"cpd/#TensorKitchen.reconstruct-Tuple{CPDResult}","page":"CPD","text":"reconstruct(td::TuckerResult) reconstructs the tensor from Tucker decomposition\n\nA = S ×₁ U₁ ×₂ U₂ ⋯ ×_d U_d\n\n\n\n\n\n","title":"TensorKitchen.reconstruct"},{"category":"section","location":"#TensorKitchen.jl-Documentation","page":"Home","text":"TensorKitchen.jl is a Julia package for tensor decompositions.\n\nCPD documentation\nTucker documentation\nBTD documentation\nJoin decomposition documentation\nUtilities\nPipeline\nReferences","title":"TensorKitchen.jl Documentation"},{"category":"section","location":"#Notes","page":"Home","text":"The package is currently at a pre-alpha stage. \n\nThe implementation is based on combining algebraic algorithms like ALS (see, e.g., the textbook by Kolda and Ballard) and Riemannian optimization from Manopt.jl.\n\nWhat currently works is \n\nCanonical Polyadic Decomposition (CPD)\nTucker Decomposition\nNonnegative Canonical Polyadic Decomposition (NNCPD)\nBlock Term Decomposition (BTD)\nJoin Decompositions\n\n\n\nThe next updates will include \n\nHandling of swamps/plateaus in the optimization step\nDocumentation\nImproved User Interface\nGPU Support \nLL1 Decomposition (3-way specialized BTD)\nSymmetric CP / Waring Decomposition\nPartially Symmetric CP\nTensor Trains","title":"Notes"},{"category":"section","location":"utils/#General-Utilities","page":"Utilities","text":"","title":"General Utilities"},{"category":"function","location":"utils/#TensorKitchen.save_result","page":"Utilities","text":"save_result(path::AbstractString, result)\n\nSave a result to a file.\n\n\n\n\n\n","title":"TensorKitchen.save_result"},{"category":"function","location":"utils/#TensorKitchen.load_result","page":"Utilities","text":"load_result(path::AbstractString)\n\nLoad a result from a file\n\n\n\n\n\n","title":"TensorKitchen.load_result"}]
}
diff --git a/docs/src/PIPELINE.md b/docs/src/PIPELINE.md
index d507b69..7b1f74a 100644
--- a/docs/src/PIPELINE.md
+++ b/docs/src/PIPELINE.md
@@ -101,7 +101,6 @@ BTD-ALS stabilization behavior:
It dispatches directly to decomposition routines:
- `:sthosvd`
-- `:thosvd` / `:hosvd`
- `:hooi`
## Generic `approx(...)` routing
diff --git a/docs/src/btd.md b/docs/src/btd.md
index b054b80..8835558 100644
--- a/docs/src/btd.md
+++ b/docs/src/btd.md
@@ -38,6 +38,5 @@ btd
BTDResult
blocks
reconstruct(::BTDResult)
-BTDTSDSolver
-TSDSolver
+
```
diff --git a/docs/src/cpd.md b/docs/src/cpd.md
index 0eca095..bdbb537 100644
--- a/docs/src/cpd.md
+++ b/docs/src/cpd.md
@@ -41,13 +41,7 @@ Â = reconstruct(res)
cpd
nncpd
CPDResult
-RankOneTensor
weights(::CPDResult)
-comp_weight(::CPDResult)
factors(::CPDResult)
reconstruct(::CPDResult)
-reconstruct_cp_rank1
-reconstruct_cpd_rankr
-normalize_components!
-normalize_components
```
diff --git a/docs/src/index.md b/docs/src/index.md
index e483d3e..5404928 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -9,6 +9,7 @@
* [Join decomposition documentation](join.md)
* [Utilities](utils.md)
* [Pipeline](PIPELINE.md)
+* [References](references.md)
## Notes
@@ -27,40 +28,11 @@ What currently works is
The next updates will include
+- Handling of swamps/plateaus in the optimization step
- Documentation
- Improved User Interface
-- ProgressMeter
- GPU Support
- LL1 Decomposition (3-way specialized BTD)
- Symmetric CP / Waring Decomposition
- Partially Symmetric CP
- Tensor Trains
-
-
-## References
-
-### General Tensor Decomposition
-
-- **Tensor decompositions (CP, Tucker):** T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," *SIAM Review*, vol. 51, no. 3, pp. 455–500, 2009.
-
-### Tucker Methods
-
-- **HOSVD:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1253–1278, 2000.
-- **ST-HOSVD:** N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," *SIAM J. Sci. Comput.*, vol. 34, no. 2, pp. A1027–A1052, 2012.
-- **HOOI:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R_1,R_2,...,R_N) approximation of higher-order tensors," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1324–1342, 2000.
-
-### Block and Structured Models (BTD / LL1)
-
-- **Block-term decomposition (BTD):** L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1022–1032, 2008.
-- L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1033–1066, 2008.
-- **BTD-ALS:** L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," *SIAM Journal on Matrix Analysis and Applications*, vol. 30, no. 3, pp. 1067–1083, 2008. [PDF](http://dimitri.nion.free.fr/Publications/Revues/DeLatNion_TensorBlock3.pdf).
-
-### Join decompositions
-
-- **Conditioning of join decompositions:** P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," *SIAM Journal on Matrix Analysis and Applications*, vol. 39, no. 1, pp. 287–309, 2018. [arXiv:1611.08117 (PDF)](https://arxiv.org/pdf/1611.08117).
-
-### Riemannian Optimization and Julia Ecosystem
-
-- **Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation:** P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," *SIAM Journal on Optimization*, vol. 28, no. 3, pp. 2435–2465, 2018. [arXiv:1709.00033 (PDF)](https://arxiv.org/pdf/1709.00033).
-- **Riemannian optimization:** P.-A. Absil, R. Mahony, and R. Sepulchre, *Optimization Algorithms on Matrix Manifolds*. Princeton University Press, 2008.
-- **Julia manifold optimization ecosystem:** R. Bergmann *et al.*, [ManifoldsBase.jl](https://github.com/JuliaManifolds/ManifoldsBase.jl), [Manifolds.jl](https://github.com/JuliaManifolds/Manifolds.jl), and [Manopt.jl](https://manoptjl.org).
diff --git a/docs/src/join.md b/docs/src/join.md
index c5669b7..a52b31f 100644
--- a/docs/src/join.md
+++ b/docs/src/join.md
@@ -22,6 +22,103 @@ reconstruct(join_res)
```
+## 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-repl
+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-repl
+julia> target = randn(2, 3)
+julia> approx((Manifolds.Segre((2, 3)), Manifolds.Segre((2, 3))), target; verbose = false)
+CPDResult{Float64}
+```
+
+```julia-repl
+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
```@docs
@@ -30,15 +127,5 @@ ApproxResult
reconstruct(::ApproxResult)
join_product
SegreProduct
-SqEuclidean
-pullback_metric_inverse
-sm_cost_nn_quadratic
-sm_egrad_nn_quadratic
-sm_cost_nn_rank1
-sm_egrad_nn_rank1
-sm_cost_double_circle
-sm_egrad_double_circle
-sm_double_circle_at_zero
-egrad_to_rgrad
-egrad_to_rgrad!
+
```
diff --git a/docs/src/references.md b/docs/src/references.md
new file mode 100644
index 0000000..9090801
--- /dev/null
+++ b/docs/src/references.md
@@ -0,0 +1,27 @@
+# References
+
+#### General Tensor Decomposition
+
+- **Tensor decompositions (CP, Tucker):** T. G. Kolda and B. W. Bader, "Tensor decompositions and applications," *SIAM Review*, vol. 51, no. 3, pp. 455–500, 2009.
+
+#### Tucker Methods
+
+- **HOSVD:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "A multilinear singular value decomposition," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1253–1278, 2000.
+- **ST-HOSVD:** N. Vannieuwenhoven, R. Vandebril, K. Meerbergen, "A new truncation strategy for the higher-order singular value decomposition," *SIAM J. Sci. Comput.*, vol. 34, no. 2, pp. A1027–A1052, 2012.
+- **HOOI:** L. De Lathauwer, B. De Moor, and J. Vandewalle, "On the best rank-1 and rank-(R_1,R_2,...,R_N) approximation of higher-order tensors," *SIAM J. Matrix Anal. Appl.*, vol. 21, no. 4, pp. 1324–1342, 2000.
+
+#### Block and Structured Models (BTD / LL1)
+
+- **Block-term decomposition (BTD):** L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part I: Lemmas for partitioned matrices," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1022–1032, 2008.
+- L. De Lathauwer, "Decompositions of a higher-order tensor in block terms—Part II: Definitions and uniqueness," *SIAM J. Matrix Anal. Appl.*, vol. 30, no. 3, pp. 1033–1066, 2008.
+- **BTD-ALS:** L. De Lathauwer and D. Nion, "Decompositions of a higher-order tensor in block terms—Part III: Alternating least squares algorithms," *SIAM Journal on Matrix Analysis and Applications*, vol. 30, no. 3, pp. 1067–1083, 2008. [PDF](http://dimitri.nion.free.fr/Publications/Revues/DeLatNion_TensorBlock3.pdf).
+
+#### Join decompositions
+
+- **Conditioning of join decompositions:** P. Breiding and N. Vannieuwenhoven, "The condition number of join decompositions," *SIAM Journal on Matrix Analysis and Applications*, vol. 39, no. 1, pp. 287–309, 2018. [arXiv:1611.08117 (PDF)](https://arxiv.org/pdf/1611.08117).
+
+#### Riemannian Optimization and Julia Ecosystem
+
+- **Riemannian trust-region / Gauss–Newton for canonical rank (CP) approximation:** P. Breiding and N. Vannieuwenhoven, "A Riemannian Trust Region Method for the Canonical Tensor Rank Approximation Problem," *SIAM Journal on Optimization*, vol. 28, no. 3, pp. 2435–2465, 2018. [arXiv:1709.00033 (PDF)](https://arxiv.org/pdf/1709.00033).
+- **Riemannian optimization:** P.-A. Absil, R. Mahony, and R. Sepulchre, *Optimization Algorithms on Matrix Manifolds*. Princeton University Press, 2008.
+- **Julia manifold optimization ecosystem:** R. Bergmann *et al.*, [ManifoldsBase.jl](https://github.com/JuliaManifolds/ManifoldsBase.jl), [Manifolds.jl](https://github.com/JuliaManifolds/Manifolds.jl), and [Manopt.jl](https://manoptjl.org).
\ No newline at end of file
diff --git a/docs/src/tucker.md b/docs/src/tucker.md
index c7bdfec..e191543 100644
--- a/docs/src/tucker.md
+++ b/docs/src/tucker.md
@@ -33,10 +33,4 @@ factors(::TuckerResult)
multilinear_rank(::TuckerResult)
factor_dims(::TuckerResult)
reconstruct(::TuckerResult)
-sthosvd
-thosvd
-optimal_mode_order
-error_bound
-tucker_hosvd
-reconstruct_tucker
```
diff --git a/docs/tucker/index.html b/docs/tucker/index.html
index 3334a6d..cd7b853 100644
--- a/docs/tucker/index.html
+++ b/docs/tucker/index.html
@@ -1,11 +1,21 @@
-Tucker · Documentation
Compute the rank-(r₁,…,r_d) Sequentially Truncated HOSVD (ST-HOSVD).
Algorithm (Definition 6.1, Algorithm 1)
Given tensor A ∈ ℝ^{n₁×⋯×nd} and target multilinear rank (r₁,…,rd):
Set Ŝ₀ = A
For k = 1,…,d (in processing order p): a. Compute the mode-p[k] unfolding of Ŝ{k-1} b. Compute the compact SVD; truncate to rank r{p[k]} c. Set Û{p[k]} = first r{p[k]} left singular vectors d. Ŝₖ = Ŝ{k-1} ×{p[k]} Û_{p[k]}ᵀ (project and shrink)
Tolerance-based ST-HOSVD: automatically determine ranks to achieve ‖A - Â‖F ≤ tol · ‖A‖F
Uses the error bound from Theorem 6.4: the squared error decomposes as a sum of squared errors from each truncation step. By distributing the error budget equally across modes (ϵₖ = ϵ/√d for each mode), the final error is bounded by ϵ.
Arguments
A::Array{T,N}: input tensor
tol::Float64: relative error tolerance
Keyword arguments
processing_order::Vector{Int}: order in which to process modes (default: smallest first)
verbose::Bool: print progress information (default: false)
Computes all factor matrices from the original tensor (no sequential truncation), then computes the core tensor at the end. This is the standard approach from De Lathauwer et al. (2000).
The ST-HOSVD is generally preferred: it requires fewer operations and typically yields a better approximation.
Heuristic for choosing a good processing order for ST-HOSVD.
Following Section 6.3 of Vannieuwenhoven et al., a good heuristic is to process modes in increasing order of nₖ / rₖ (i.e., process the most compressible modes first). When ranks are not known a priori, processing from smallest to largest dimension is a reasonable default.
Compute the a posteriori error bound from Theorem 6.4: ‖A - Â‖²F = Σₖ Σ{j > rₖ} σ²{k,j} where σ{k,j} are the singular values at step k of the ST-HOSVD.
This is exact (not just a bound) for the ST-HOSVD.
Compute Tucker decomposition via Higher-Order SVD (HOSVD).
For each mode, computes SVD of the unfolded tensor and takes the top ranks[mode] left singular vectors as the factor matrix. The core tensor is computed by projecting the original tensor onto these factor spaces.
tucker(A, ranks; method = :sthosvd, kwargs...) returns a TuckerResult
Computes a Tucker decomposition of A with multilinear rank ranks.
Main Options
method = :sthosvd: Sets the Tucker decomposition algorithm. Possible options are:
:sthosvd (default): Sequentially Truncated HOSVD. A direct one-pass decomposition, mainly used as a fast standalone Tucker approximation or as the default initializer for :hooiFast, deterministic, and usually a good initial point.
:hooi: High-Order Orthogonal Iteration. Iteratively refines the Tucker factors, initialized by ST-HOSVD by default.
Extended Options
For method = :hooi:
maxiter = 50: Maximum number of HOOI iterations.
tol = 1e-8: Convergence tolerance based on change in relative reconstruction error.
init = :sthosvd
:sthosvd: Uses ST-HOSVD to initialize the Tucker factors.
TuckerResult: Uses an existing Tucker decomposition as the initial point.
Example
julia> using Random
+julia> Random.seed!(0)
+julia> A = randn(20, 15, 10); ranks = (5, 4, 3)
+julia> res = tucker(A, ranks; verbose = false)
+TuckerResult{Float64, 3}
+ Original size: (20, 15, 10)
+ Core size: (5, 4, 3)
+ Multilinear rank: (5, 4, 3)
+ Compression: 12.0x
+
The core tensor and factor matrices can be accessed by
core(res)
+factors(res)
A tensor approximation can be reconstructed by
reconstruct(res)
or equivalently
reconstruct_tucker(core(res), factors(res))
Notes
:sthosvd is not an iterative solver. It directly returns a TuckerResult and does not expose solver-style outputs such as iteration counts or convergence diagnostics.
:hooi is the iterative refinement method in the current Tucker implementation.
Important distinction: For the current Tucker implementation, do not use solver = :rgd, init = :auto, or manifold solvers like RGD, RCG, LBFGS, etc.