|
223 | 223 | @test size(Ahat) == size(A) |
224 | 224 | @test rel_error(A, res) == TensorKitchen.relative_frobenius_error(A, Ahat) |
225 | 225 | @test rel_error(A, Ahat) == rel_error(A, res) |
| 226 | + |
| 227 | + model = JoinModel(A, r; geometry = :canonical) |
| 228 | + p = TensorKitchen.initial_point(model, :random) |
| 229 | + comps = TensorKitchen.extract_components(model, p) |
| 230 | + @test length(comps) == r |
| 231 | + @test comps[1] isa TensorKitchen.CPDComponent |
| 232 | + @test !(:tensor in fieldnames(typeof(comps[1]))) |
| 233 | + @test comps[1].point !== p |
| 234 | + @test comps[1].kind == :Segre |
| 235 | + @test size(comps[1].tensor) == size(A) |
| 236 | + Xparts = zero(A) |
| 237 | + for c in comps |
| 238 | + Xparts .+= c.tensor |
| 239 | + end |
| 240 | + @test TensorKitchen.cost(model, p) ≈ 0.5 * sum(abs2, A .- Xparts) |
226 | 241 | end |
227 | 242 |
|
228 | 243 | @testset "frontend defaults through public APIs" begin |
|
562 | 577 | @test all( |
563 | 578 | isapprox(norm(q_sep.factors[m][:, k]), 1; atol = 1e-10) for m = 1:3 for k = 1:r |
564 | 579 | ) |
| 580 | + U_sep, λ_sep = normalize_components(U, λ, SeparateLambdaNormalization()) |
| 581 | + @test U_sep isa Vector{Matrix{Float64}} |
| 582 | + @test λ_sep isa Vector{Float64} |
| 583 | + @test reconstruct_cpd_rankr(λ_sep, U_sep) ≈ A_ref |
565 | 584 |
|
566 | 585 | q_last = normalize_components(CPDPoint(λ, U), :last_mode) |
567 | 586 | @test reconstruct_cpd_rankr(q_last.lambda, q_last.factors) ≈ A_ref |
|
886 | 905 | ) |
887 | 906 | @test TensorKitchen.manifold(model_sm) isa ProductManifold |
888 | 907 | @test all(m -> m isa SqEuclidean, TensorKitchen.manifold(model_sm).manifolds) |
| 908 | + join_model_sm = JoinModel(A, r; nonnegative = true, geometry = :squaring_metric) |
| 909 | + @test all( |
| 910 | + m -> m isa SqEuclidean, |
| 911 | + TensorKitchen.manifold(TensorKitchen.cpd_model(join_model_sm)).manifolds, |
| 912 | + ) |
889 | 913 | @test getproperty(model_sm, :scale_by_lambda) == false |
890 | 914 | res_nn_sm = cpd( |
891 | 915 | A, |
@@ -1712,6 +1736,28 @@ end |
1712 | 1736 | length(res_btd_tsd.solver_info.line_search_trial_history) |
1713 | 1737 | @test isfinite(res_btd_tsd.rel_error) |
1714 | 1738 | @test res_btd_tsd.rel_error ≈ norm(A - reconstruct(res_btd_tsd)) / norm(A) |
| 1739 | + res_btd_tsd_object = btd( |
| 1740 | + A, |
| 1741 | + 2, |
| 1742 | + (2, 2, 2); |
| 1743 | + solver = BTDTSDSolver(stepsize = 1.0), |
| 1744 | + init = :hosvd_multistart, |
| 1745 | + maxiter = 1, |
| 1746 | + schedule = :cyclic, |
| 1747 | + btd_als_polish_maxiter = 0, |
| 1748 | + tol = 1e-6, |
| 1749 | + verbose = false, |
| 1750 | + ) |
| 1751 | + @test res_btd_tsd_object isa BTDResult |
| 1752 | + @test res_btd_tsd_object.solver == :btd_tsd |
| 1753 | + @test_throws ArgumentError btd( |
| 1754 | + A, |
| 1755 | + 2, |
| 1756 | + (2, 2, 2); |
| 1757 | + solver = :tsd, |
| 1758 | + maxiter = 1, |
| 1759 | + verbose = false, |
| 1760 | + ) |
1715 | 1761 |
|
1716 | 1762 | manifolds = TensorKitchen._as_join_manifold_tuple(TuckerJoin(size(A), (2, 2, 2), 2)) |
1717 | 1763 | backend = TensorKitchen._sum_backend_instance(TensorKitchen.BTDBackend, manifolds, A) |
|
0 commit comments