Skip to contents

nmfae.signed fits a three-layer non-negative matrix factorization autoencoder with a signed bottleneck, solving $$Y_1 \approx X_1 (C_{+} - C_{-}) X_2 Y_2, \quad X_1 \ge 0,\; C_{+} \ge 0,\; C_{-} \ge 0,\; X_2 \ge 0,$$ where \(\Theta = C_{+} - C_{-}\) is the signed bottleneck. The basis matrices \(X_1\) (columns sum to 1) and \(X_2\) (rows sum to 1) retain their non-negative "parts-based" interpretability, while \(\Theta\) can express anti-correlations (e.g., refractive index up vs. Abbe number down).

The algorithm uses Direct Multiplicative Updates derived from Ding et al. (2010) sign-splitting technique, applied block-wise to the four non-negative blocks \((C_{+}, C_{-}, X_1, X_2)\). Each block update monotonically decreases the true objective \(\|Y_1 - X_1(C_{+} - C_{-})X_2 Y_2\|_F^2\) (Lee-Seung auxiliary function method).

Relation to nmfae: When \(\Theta \ge 0\) suffices (the nmfae case), nmfae.signed reduces to nmfae up to the \(C_{+} - C_{-}\) parameterization. Use nmfae.signed when the data exhibit negative cross-property correlations that tri-NMF-AE cannot express (e.g., high refractive index <-> low Abbe number trade-off).

Usage

nmfae.signed(
  Y1,
  Y2 = Y1,
  rank = 2,
  rank.encoder = rank,
  epsilon = 1e-04,
  maxit = 5000,
  verbose = FALSE,
  ...
)

Arguments

Y1

Output matrix \(Y_1\) (P1 x N). Negative entries allowed (Y.signed = TRUE is auto-detected).

Y2

Input matrix \(Y_2\) (P2 x N). Must be non-negative. Default Y1 (autoencoder).

rank

Integer. Decoder rank Q. Default 2. Alias Q accepted via ....

rank.encoder

Integer. Encoder rank R. Default rank. Alias R accepted via ....

epsilon

Relative convergence tolerance on the objective. Default 1e-4.

maxit

Maximum iterations. Default 5000.

verbose

Logical. Print progress. Default FALSE.

...

Additional arguments:

warm.start

One of TRUE (default, hybrid: warm-start \(X_1, X_2\) from nmfae but initialize \(C_{+}, C_{-}\) randomly), "full" (warm-start everything including \(C_{+} = C_{\mathrm{tri}}\), \(C_{-} = \delta\)), or FALSE (random for all blocks). The hybrid default avoids the \(C_{-} = 0\) local-minimum trap inherited from tri-NMF-AE while still benefiting from good \(X_1, X_2\) initialization. Ignored when \(Y_1\) has negative entries.

nstart

Integer, default 1 (cf. kmeans). Number of random restarts for \(C_{+}, C_{-}\). Each restart uses seed seed + 7919 * (s-1). Returns the best run by final objective. Signed models have more local minima than non-negative ones because the bottleneck \(\Theta = C_{+} - C_{-}\) can take both positive and negative values; during exploration a larger nstart (e.g., 10-50) reduces the chance of being trapped at an inferior stationary point (particularly the \(C_{-} = 0\) trap from warm-start from non-negative tri-NMF-AE). Use the default 1 for fast development and raise for publication-grade runs.

Y1.weights

Optional non-negative weight matrix (P1 x N) or vector (length N) for \(Y_1\), analogous to the weights argument of lm. Loss becomes \(\sum W_{ij} \, (Y_{1,ij} - \hat Y_{1,ij})^2\) (lm()-style, linear in \(W\)). Logical matrices (TRUE / FALSE) are also accepted. Used by nmfae.signed.ecv to hold out test elements via a binary mask \(W \in \{0,1\}\); real-valued weights for importance weighting are also supported. Default: if Y1 has NA, a binary mask is auto-generated (0 for NA, 1 elsewhere).

Cp.init, Cn.init

Explicit \(Q \times R\) non-negative matrices for initialization. Overrides warm.start.

C.init

Explicit signed \(Q \times R\) matrix, internally split into \((C_{+}, C_{-})\).

X1.init, X2.init

Explicit basis matrices.

seed

RNG seed. Default 123.

print.trace

Logical. Print iteration trace. Default FALSE.

prefix.dec, prefix.enc

Label prefixes for decoder/encoder factors. Default "Dec", "Enc".

Value

An object of class c("nmfae.signed", "nmfae", "nmf") with:

X1

Decoder basis (P1 x Q), column sum 1.

Cp, Cn

Non-negative parts of \(\Theta\) (each Q x R).

C

Signed bottleneck \(\Theta = C_{+} - C_{-}\) (Q x R).

X2

Encoder basis (R x P2), row sum 1.

Y1hat

Fitted values \(X_1 (C_{+} - C_{-}) X_2 Y_2\).

H

Encoding \((C_{+} - C_{-}) X_2 Y_2\) (Q x N, signed).

rank

c(Q = Q, R = R).

dims

c(P1, P2, N).

objfunc, objfunc.iter

Final and per-iteration objective values.

r.squared, sigma, mae

Goodness of fit statistics.

niter, runtime

Iterations and elapsed seconds.

Y.signed

Logical; whether \(Y_1\) contained negative entries.

call

Matched call.

Lifecycle

This function is experimental; interface may change.

References

Ding, C.H.Q., Li, T., and Jordan, M.I. (2010). Convex and Semi-Nonnegative Matrix Factorizations. IEEE TPAMI, 32(1), 45-55.

Satoh, K. (2026). Signed-Bottleneck NMF-AE: Signed-Bottleneck 3-Layer NMF (research memo, 2026-04-18).

Ding, C. H. Q., Li, T., & Jordan, M. I. (2010). Convex and semi-nonnegative matrix factorizations. IEEE Transactions on Pattern Analysis and Machine Intelligence, 32(1), 45–55.

Examples

# \donttest{
set.seed(1)
Y1 <- matrix(abs(rnorm(12)), 3, 4)
Y2 <- matrix(abs(rnorm(20)), 5, 4)
res <- nmfae.signed(Y1, Y2, rank = 2, rank.encoder = 2, maxit = 500)
summary(res)
#> 
#> Call:
#> nmfae.signed(Y1 = Y1, Y2 = Y2, rank = 2, rank.encoder = 2, maxit = 500)
#> 
#> Signed-Bottleneck NMF-AE (Direct MU; signed bottleneck Theta = Cp - Cn)
#> 
#> Dimensions:
#>   P1 (output rows): 3
#>   P2 (input rows):  5
#>   N  (samples):     4
#>   Q (decoder rank): 2
#>   R (encoder rank): 2
#>   Parameters:       24
#> 
#> Convergence:
#>   Iterations:       16
#>   Runtime (secs):   0.03
#>   Final objfunc:    2.128
#> 
#> Goodness of fit:
#>   R-squared:        0.04789
#>   Sigma (RMSE):     0.4211
#>   MAE:              0.344
#> 
#> Structure (range / sparsity / negative mass):
#>   X1 (P1 x Q):     range [6.232e-139, 0.9987]   sparsity  16.7%
#>   Cp (Q x R):      range [0, 2.459]   sparsity  75.0%
#>   Cn (Q x R):      range [0, 0.01288]   sparsity  50.0%
#>   C=Cp-Cn (Q x R): range [-0.01288, 2.459]   sparsity  25.0%   neg-mass   0.6%
#>   X2 (R x P2):     range [1.027e-30, 0.8354]   sparsity  30.0%
#>   Encoding H (Q x N):                                   neg-mass   0.1%
#> 
# }