Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ uuid = "3630a16b-0f2f-4d88-afbf-c7d59eccf553"
version = "0.1.0"

[deps]
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
ManifoldsBase = "3362f125-f0bb-47a3-aa74-596ffd7ef2fb"
Manopt = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"

[compat]
JuliaFormatter = "1.0.33"
Manifolds = "0.11.20"
ManifoldsBase = "2.3.5"
Manopt = "0.5.37"
Expand All @@ -25,7 +22,8 @@ TensorOperations = "5.6"
julia = "1.10"

[extras]
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Statistics", "Test"]
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Â = 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.

<br>
Expand Down Expand Up @@ -138,7 +138,7 @@ CPDResult{Float64}
or run

```julia
nncpd(B, r)
nn_res = nncpd(B, r)
```

We access the decomposition as follows.
Expand Down Expand Up @@ -172,13 +172,13 @@ BTDResult{Float64}
The blocks of `btd_res` can be obtained as follows:

```julia
blocks = blocks(btd_res)
btd_blocks = blocks(btd_res)
```

Each block is represented as a Tucker decomposition, so we can access its core and factor matrices via:

```julia
blk = blocks[1]
blk = btd_blocks[1]
core(blk)
factors(blk)
```
Expand Down
107 changes: 107 additions & 0 deletions test/basic_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2151,6 +2151,113 @@ end
@test res_btd_zero.rel_error ≥ 0
end

@testset "README workflow examples stay executable" begin
rng = MersenneTwister(5151)
A = randn(rng, 8, 6, 5)
r = 3

res = cpd(A, r; init = TuckerInit(), maxiter = 5, verbose = false)
@test res isa CPDResult
λ = weights(res)
U = factors(res)
@test length(λ) == r
@test length(U) == ndims(A)
 = reconstruct(res)
@test size(Â) == size(A)
@test cpd(A, r; solver = :als, maxiter = 3, verbose = false) isa CPDResult

mlrank = (4, 3, 2)
tucker_res = tucker(A, mlrank)
@test tucker_res isa TuckerResult
@test size(core(tucker_res)) == mlrank
@test length(factors(tucker_res)) == ndims(A)

B = abs.(A)
nn_res = nncpd(B, r; solver = :als, maxiter = 3, verbose = false)
@test nn_res isa CPDResult
@test length(weights(nn_res)) == r
@test length(factors(nn_res)) == ndims(B)
@test all(w -> w >= -1e-12, weights(nn_res))
@test all(F -> all(x -> x >= -1e-12, F), factors(nn_res))
@test cpd(B, r; nonnegative = true, solver = :als, maxiter = 3, verbose = false) isa
CPDResult

block_count = 2
block_rank = (3, 2, 2)
btd_res = btd(
A,
block_count,
block_rank;
solver = :als,
init = BTDHOSVDMultistartInit(2; screening_steps = 0, block_maxiter = 1),
maxiter = 2,
block_maxiter = 1,
verbose = false,
)
@test btd_res isa BTDResult
btd_blocks = blocks(btd_res)
@test length(btd_blocks) == block_count
blk = btd_blocks[1]
@test size(core(blk)) == block_rank
@test length(factors(blk)) == ndims(A)

p = [1.2, 0.4]
S = Manifolds.Sphere(1)
join_res = approx(
(S, S),
p;
init = :deterministic,
solver = :rgd,
maxiter = 60,
verbose = false,
)
@test join_res isa ApproxResult
@test length(components(join_res)) == 2
@test size(reconstruct(join_res)) == size(p)
end

@testset "result conversion consistency for CPD, NNCPD, and BTD" begin
rng = MersenneTwister(6262)
A = randn(rng, 6, 5, 4)
r = 2

res_cpd = cpd(A, r; solver = :als, init = TuckerInit(), maxiter = 4, verbose = false)
Ahat_cpd = reconstruct(res_cpd)
@test Ahat_cpd ≈ reconstruct_cpd_rankr(weights(res_cpd), factors(res_cpd))
@test rel_error(A, res_cpd) ≈ rel_error(A, Ahat_cpd)
@test length(components(res_cpd)) == r
@test factors(res_cpd) == TensorKitchen.factors_from_components(components(res_cpd))

B = abs.(A)
res_nn = nncpd(B, r; solver = :als, init = TuckerInit(), maxiter = 4, verbose = false)
Ahat_nn = reconstruct(res_nn)
@test Ahat_nn ≈ reconstruct_cpd_rankr(weights(res_nn), factors(res_nn))
@test rel_error(B, res_nn) ≈ rel_error(B, Ahat_nn)
@test length(components(res_nn)) == r
@test all(w -> w >= -1e-12, weights(res_nn))
@test all(F -> all(x -> x >= -1e-12, F), factors(res_nn))

res_btd = btd(
A,
2,
(2, 2, 2);
solver = :als,
init = BTDHOSVDMultistartInit(2; screening_steps = 0, block_maxiter = 1),
maxiter = 3,
block_maxiter = 1,
verbose = false,
)
Ahat_btd = reconstruct(res_btd)
block_sum = zero(A)
for blk in blocks(res_btd)
@test size(core(blk)) == (2, 2, 2)
@test length(factors(blk)) == ndims(A)
block_sum .+= tensor(blk)
end
@test Ahat_btd ≈ block_sum
@test rel_error(A, res_btd) ≈ rel_error(A, Ahat_btd)
end

# =========================================================================
# utils: pack_unpack, cp_init_tucker, tensor_contractions
# =========================================================================
Expand Down
Loading