The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Introduction to mwTensor

Koki Tsuyuzaki

2026-05-07

Overview

mwTensor is an R package for Multi-Way Component Analysis (MWCA). It provides solvers for decomposing single or multiple matrices and tensors with flexible choices of matrix factorization algorithms (SVD, NMF, ICA, CX, etc.).

Key functions:

Function Purpose
MWCA Decompose a single tensor
CoupledMWCA Decompose multiple coupled tensors simultaneously
checkCoupledMWCA Validate parameters before optimization
initCoupledMWCA Generate reproducible initial values
refineFactor One-step decomposition of a factor (experimental)
MWCAProgram Declarative factorization program representation

1. Single Tensor Decomposition with MWCA

MWCA applies a matrix factorization method to each mode of a single tensor.

library(mwTensor)
#> Warning: no DISPLAY variable so Tk is not available

# Create a 3-way tensor (10 x 12 x 8)
set.seed(123)
X <- array(runif(10 * 12 * 8), dim = c(10, 12, 8))

# Default parameters: SVD on each mode, lower dim = 2
params <- defaultMWCAParams(X)
params@algorithms  # decomposition method per mode
#> [1] "mySVD" "mySVD" "mySVD"
params@dims        # target lower dimension per mode
#> [1] 2 2 2
out <- MWCA(params)

# Factor matrices: one per mode
# Each is dims[i] x dim(X)[i]
lapply(out@factors, dim)
#> [[1]]
#> [1]  2 10
#> 
#> [[2]]
#> [1]  2 12
#> 
#> [[3]]
#> [1] 2 8

# Core tensor
dim(out@core)
#> [1] 2 2 2

# Reconstruction error
out@rec_error
#> [1] 8.776407

Choosing different algorithms per mode

params2 <- new("MWCAParams",
    X = X,
    mask = NULL,
    pseudocount = 1e-10,
    algorithms = c("mySVD", "myNMF", "myICA"),
    dims = c(3L, 4L, 2L),
    transpose = FALSE,
    viz = FALSE,
    figdir = NULL)
out2 <- MWCA(params2)
lapply(out2@factors, dim)
#> [[1]]
#> [1]  3 10
#> 
#> [[2]]
#> [1]  4 12
#> 
#> [[3]]
#> [1] 2 8

Available algorithms: mySVD, myALS_SVD, myNMF, myICA, myCX.

2. Coupled Tensor Decomposition with CoupledMWCA

CoupledMWCA decomposes multiple tensors that share dimensions. Shared dimensions get common factor matrices; unshared dimensions get specific factor matrices.

Toy data

Xs <- toyModel("coupled_CP_Easy")
# X1: 20x30 matrix, X2: 30x30x30 tensor, X3: 30x25 matrix
lapply(Xs, dim)
#> $X1
#> [1] 20 30
#> 
#> $X2
#> [1] 30 30 30
#> 
#> $X3
#> [1] 30 25

The coupling structure:

I2 is shared between X1 and X2. I4 is shared between X2 and X3.

Setting up the model

# common_model maps: block -> (mode_name -> factor_name)
common_model <- list(
    X1 = list(I1 = "A1", I2 = "A2"),
    X2 = list(I2 = "A2", I3 = "A3", I4 = "A4"),
    X3 = list(I4 = "A4", I5 = "A5"))

# A2 appears in both X1 and X2 -> shared factor for dimension I2
# A4 appears in both X2 and X3 -> shared factor for dimension I4

Running with default parameters

params <- defaultCoupledMWCAParams(Xs, common_model)

# Inspect defaults
params@common_algorithms  # "mySVD" for all
#> $A1
#> [1] "mySVD"
#> 
#> $A2
#> [1] "mySVD"
#> 
#> $A3
#> [1] "mySVD"
#> 
#> $A4
#> [1] "mySVD"
#> 
#> $A5
#> [1] "mySVD"
params@common_dims        # 2 for all
#> $A1
#> [1] 2
#> 
#> $A2
#> [1] 2
#> 
#> $A3
#> [1] 2
#> 
#> $A4
#> [1] 2
#> 
#> $A5
#> [1] 2
params@common_coretype    # "Tucker"
#> [1] "Tucker"

out <- CoupledMWCA(params)

# Common factor matrices
lapply(out@common_factors, dim)
#> $A1
#> [1]  2 20
#> 
#> $A2
#> [1]  2 30
#> 
#> $A3
#> [1]  2 30
#> 
#> $A4
#> [1]  2 30
#> 
#> $A5
#> [1]  2 25

# Convergence
tail(out@rec_error)
#>       95       96       97       98       99      100 
#> 7913.081 7913.081 7913.081 7913.081 7913.081 7913.081

Customizing algorithms and dimensions

params@common_algorithms <- list(
    A1 = "mySVD", A2 = "myNMF", A3 = "myNMF",
    A4 = "mySVD", A5 = "myCX")
params@common_dims <- list(
    A1 = 3, A2 = 3, A3 = 5, A4 = 4, A5 = 4)
params@common_iteration <- list(
    A1 = 5, A2 = 5, A3 = 5, A4 = 5, A5 = 5)

out2 <- CoupledMWCA(params)
lapply(out2@common_factors, dim)
#> $A1
#> [1]  3 20
#> 
#> $A2
#> [1]  3 30
#> 
#> $A3
#> [1]  5 30
#> 
#> $A4
#> [1]  4 30
#> 
#> $A5
#> [1]  4 25

3. Validating Parameters with checkCoupledMWCA

Before running a potentially expensive optimization, validate the parameter object. Unlike CoupledMWCA which stops on the first error, checkCoupledMWCA collects all errors and warnings.

result <- checkCoupledMWCA(params)
result$ok
#> [1] TRUE
result$summary
#> [1] "Validation passed"
# Introduce multiple errors
bad_params <- params
bad_params@common_algorithms$A1 <- "nonExistentAlgo"
bad_params@common_iteration$A2 <- 1.5  # must be integer
bad_params@common_coretype <- "TUCKER"  # must be "Tucker" or "CP"

result <- checkCoupledMWCA(bad_params)
result$ok
#> [1] FALSE
result$errors
#> [1] "nonExistentAlgo is not defined in .GlovalEnv"                  
#> [2] "params@common_iteration must be specified as an integer vector"
#> [3] "params@common_coretype must be 'Tucker' or 'CP'"
result$summary
#> [1] "Validation failed: 3 error(s), 0 warning(s)"

4. Reproducible Initialization with initCoupledMWCA

initCoupledMWCA generates initial factor matrices without running optimization. This is useful for:

params_ok <- defaultCoupledMWCAParams(Xs, common_model)

# Random initialization with seed
init <- initCoupledMWCA(params_ok, seed = 42L)
init@init_policy
#> [1] "random"
lapply(init@common_factors, dim)
#> $A1
#> [1]  2 20
#> 
#> $A2
#> [1]  2 30
#> 
#> $A3
#> [1]  2 30
#> 
#> $A4
#> [1]  2 30
#> 
#> $A5
#> [1]  2 25

Initialization policies

# SVD-based initialization
init_svd <- initCoupledMWCA(params_ok, seed = 42L, init_policy = "svd")

# Non-negative random (safe for NMF)
init_nn <- initCoupledMWCA(params_ok, seed = 42L, init_policy = "nonneg_random")

# Verify non-negativity
all(init_nn@common_factors$A1 >= 0)
#> [1] TRUE

Using initialized values for optimization

A CoupledMWCAInit object can be passed directly to CoupledMWCA. The pre-computed factors are used as the starting point.

# Initialize, then optimize
init <- initCoupledMWCA(params_ok, seed = 42L, init_policy = "svd")
out <- CoupledMWCA(init)

# Same seed -> same result
out_a <- CoupledMWCA(initCoupledMWCA(params_ok, seed = 123L))
out_b <- CoupledMWCA(initCoupledMWCA(params_ok, seed = 123L))
identical(out_a@common_factors$A1, out_b@common_factors$A1)
#> [1] TRUE

5. Factor Refinement with refineFactor (Experimental)

refineFactor applies a one-step matrix factorization to a factor obtained from a previous decomposition. This is a proof-of-concept for “decomposition of a decomposition.”

Mathematical interpretation

Given a factor matrix A (k x n), the refinement decomposes t(A) (n x k):

t(A) ~ U * V

where U is (n x dim) and V is (dim x k). This gives:

set.seed(42)
X <- matrix(runif(20 * 30), nrow = 20, ncol = 30)
params_mat <- defaultMWCAParams(X)
params_mat@dims <- c(5L, 5L)
fit <- MWCA(params_mat)

# Factor 1 is 5 x 20. Refine it down to dim=2.
ref <- refineFactor(fit, 1L, algorithm = "mySVD", dim = 2L)
dim(ref@sub_factors)  # 2 x 20
#> [1]  2 20
dim(ref@coef)         # 5 x 2
#> [1] 5 2

# Approximation quality
max(abs(ref@source_factor - ref@coef %*% ref@sub_factors))
#> [1] 0.5069393

Refinement on CoupledMWCA results

params_c <- defaultCoupledMWCAParams(Xs, common_model)
params_c@common_dims <- list(A1=3, A2=3, A3=3, A4=3, A5=3)
fit_c <- CoupledMWCA(params_c)

# Refine common factor A2 (3 x 30) with NMF
ref_A2 <- refineFactor(fit_c, "A2", algorithm = "myNMF", dim = 2L)
dim(ref_A2@sub_factors)  # 2 x 30
#> [1]  2 30
dim(ref_A2@coef)         # 3 x 2
#> [1] 3 2

6. Factorization Programs with MWCAProgram (Experimental)

MWCAProgram provides a declarative way to describe factorization problems, including one-step factor refinements. This is an internal representation layer designed for future recursive decomposition support.

Defining a program

prog <- MWCAProgram(
    blocks = list(
        X1 = MWCAProgramBlock(
            modes = c("I1", "I2"),
            factor_map = c(I1 = "A1", I2 = "A2")),
        X2 = MWCAProgramBlock(
            modes = c("I2", "I3", "I4"),
            factor_map = c(I2 = "A2", I3 = "A3", I4 = "A4")),
        X3 = MWCAProgramBlock(
            modes = c("I4", "I5"),
            factor_map = c(I4 = "A4", I5 = "A5"))),
    factors = list(
        A1 = MWCAProgramFactor(mode = "I1", dim = 3),
        A2 = MWCAProgramFactor(mode = "I2", dim = 3),
        A3 = MWCAProgramFactor(mode = "I3", dim = 5, algorithm = "myNMF"),
        A4 = MWCAProgramFactor(mode = "I4", dim = 4),
        A5 = MWCAProgramFactor(mode = "I5", dim = 4)))
print(prog)
#> MWCAProgram
#>   Blocks:      3
#>   Factors:     5
#>   Refinements: 0
#>   Block details:
#>     X1 [common]: modes=I1,I2 -> factors={A1, A2}
#>     X2 [common]: modes=I2,I3,I4 -> factors={A2, A3, A4}
#>     X3 [common]: modes=I4,I5 -> factors={A4, A5}
#>   Factor details:
#>     A1 [common]: mode=I1, dim=3, status=decomposed, algo=mySVD
#>     A2 [common]: mode=I2, dim=3, status=decomposed, algo=mySVD
#>     A3 [common]: mode=I3, dim=5, status=decomposed, algo=myNMF
#>     A4 [common]: mode=I4, dim=4, status=decomposed, algo=mySVD
#>     A5 [common]: mode=I5, dim=4, status=decomposed, algo=mySVD

Validation

v <- validateMWCAProgram(prog)
v$ok
#> [1] TRUE
v$summary
#> [1] "Program is valid"

Compiling to solver parameters

compiled <- compileMWCAProgram(prog, Xs)
is(compiled, "CoupledMWCAParams")
#> [1] TRUE
compiled@common_dims
#> $A1
#> [1] 3
#> 
#> $A2
#> [1] 3
#> 
#> $A3
#> [1] 5
#> 
#> $A4
#> [1] 4
#> 
#> $A5
#> [1] 4
compiled@common_algorithms$A3  # "myNMF" as specified
#> [1] "myNMF"

Programs with refinement

prog_ref <- MWCAProgram(
    blocks = prog$blocks,
    factors = prog$factors,
    refinements = list(
        R1 = MWCAProgramRefinement(
            source_factor = "A2",
            algorithm = "mySVD",
            dim = 2)))

result <- executeMWCAProgram(prog_ref, Xs)
is(result$fit, "CoupledMWCAResult")
#> [1] TRUE
length(result$refinements)  # 1
#> [1] 1
dim(result$refinements$R1@sub_factors)
#> [1]  2 30

Factor status: decomposed, fixed, frozen

prog_status <- MWCAProgram(
    blocks = list(
        X1 = MWCAProgramBlock(
            modes = c("I1", "I2"),
            factor_map = c(I1 = "A1", I2 = "A2")),
        X2 = MWCAProgramBlock(
            modes = c("I2", "I3"),
            factor_map = c(I2 = "A2", I3 = "A3"))),
    factors = list(
        A1 = MWCAProgramFactor(mode = "I1", dim = 3, status = "decomposed"),
        A2 = MWCAProgramFactor(mode = "I2", dim = 3, status = "fixed"),
        A3 = MWCAProgramFactor(mode = "I3", dim = 3, status = "frozen")))
# decomposed: updated by solver
# fixed:      initialized but not updated (fix=TRUE)
# frozen:     identity-like, not decomposed (decomp=FALSE)
validateMWCAProgram(prog_status)$ok
#> [1] TRUE

Session Info

sessionInfo()
#> R version 4.4.3 (2025-02-28)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Rocky Linux 9.5 (Blue Onyx)
#> 
#> Matrix products: default
#> BLAS:   /opt/R/4.4.3/lib64/R/lib/libRblas.so 
#> LAPACK: /opt/R/4.4.3/lib64/R/lib/libRlapack.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=C              
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: Asia/Tokyo
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] mwTensor_1.2.2
#> 
#> loaded via a namespace (and not attached):
#>  [1] dotCall64_1.2       gtable_0.3.6        ellipse_0.5.0      
#>  [4] spam_2.11-3         xfun_0.57           bslib_0.10.0       
#>  [7] ggplot2_4.0.3       tagcloud_0.7.0      ggrepel_0.9.8      
#> [10] lattice_0.22-6      vctrs_0.7.3         tools_4.4.3        
#> [13] generics_0.1.4      parallel_4.4.3      tibble_3.3.1       
#> [16] rARPACK_0.11-0      pkgconfig_2.0.3     Matrix_1.7-5       
#> [19] RColorBrewer_1.1-3  S7_0.2.2            mixOmics_6.30.0    
#> [22] groupICA_0.1.1      lifecycle_1.0.5     compiler_4.4.3     
#> [25] farver_2.1.2        stringr_1.6.0       fields_17.3        
#> [28] iTensor_1.0.2       codetools_0.2-20    misc3d_0.9-2       
#> [31] einsum_0.1.2        htmltools_0.5.9     maps_3.4.3         
#> [34] sass_0.4.10         yaml_2.3.12         pillar_1.11.1      
#> [37] jquerylib_0.1.4     tidyr_1.3.2         MASS_7.3-65        
#> [40] BiocParallel_1.40.2 cachem_1.1.0        nlme_3.1-167       
#> [43] RSpectra_0.16-2     nnTensor_1.3.0      tidyselect_1.2.1   
#> [46] digest_0.6.39       stringi_1.8.7       dplyr_1.2.1        
#> [49] reshape2_1.4.4      purrr_1.2.2         splines_4.4.3      
#> [52] fastmap_1.2.0       grid_4.4.3          cli_3.6.6          
#> [55] rTensor_1.4.9       ccTensor_1.0.3      magrittr_2.0.5     
#> [58] corpcor_1.6.10      scales_1.4.0        plot3D_1.4.2       
#> [61] rmarkdown_2.31      matrixStats_1.4.1   igraph_2.1.4       
#> [64] otel_0.2.0          gridExtra_2.3       evaluate_1.0.5     
#> [67] knitr_1.51          tcltk_4.4.3         viridisLite_0.4.3  
#> [70] mgcv_1.9-1          jointDiag_0.4       rlang_1.2.0        
#> [73] Rcpp_1.1.1-1.1      glue_1.8.1          geigen_2.3         
#> [76] jsonlite_2.0.0      R6_2.6.1            plyr_1.8.9

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.