| Title: | Minorization-Maximization via Assembly-Decomposition Technology |
|---|---|
| Description: | A formula-driven framework for maximizing target functions via the minorization-maximization (MM) algorithm. The package represents the target as a symbolic expression tree, infers its curvature via disciplined-convex-programming rules, and constructs a separable surrogate at each iterate using only Jensen's inequality and the supporting hyperplane. The driver maximizes the surrogate via block-coordinate Newton with line search, falling back to a multivariate step on any non-separable residue. A formula interface accepts standard R expressions (including `sum()` reductions and `X %*% theta` design-matrix products) so statistical models such as Poisson regression can be written in one line. |
| Authors: | Xifen Huang [aut], Jinfeng Xu [aut], Jiaqi Gu [aut, cre] |
| Maintainer: | Jiaqi Gu <[email protected]> |
| License: | GPL-3 |
| Version: | 3.0.0 |
| Built: | 2026-05-26 18:25:26 UTC |
| Source: | https://github.com/gujq5/mmad |
mmad_expr
Convert a formula or expression into an mmad_expr
as_mmad_expr(x, init = NULL, data = NULL)as_mmad_expr(x, init = NULL, data = NULL)
x |
A one-sided formula ( |
init |
Numeric vector of initial parameter values. If named,
the names supply the parameter vocabulary; otherwise parameters
must be referenced via |
data |
Optional list / data frame in which to evaluate theta-free sub-expressions. Default: the formula's environment. |
An mmad_expr.
expr <- as_mmad_expr(~ log(theta[1] + theta[2]), init = c(0, 0)) evaluate_expr(expr, c(1, 2))$valueexpr <- as_mmad_expr(~ log(theta[1] + theta[2]), init = c(0, 0)) evaluate_expr(expr, c(1, 2))$value
mmad_expr
Returns the DCP curvature of the expression as one of "affine",
"convex", "concave", or "unknown". "unknown" means the DCP
rule set could not prove a definite curvature; this is a refusal,
not a claim of non-convexity.
curvature(expr)curvature(expr)
expr |
An |
A length-1 character.
curvature(log(mmad_var(1) + 1)) # "concave" curvature(exp(mmad_var(1))) # "convex" curvature(2 * mmad_var(1) + 3 * mmad_var(2)) # "affine" curvature(exp(mmad_var(1)) - exp(mmad_var(2))) # "unknown"curvature(log(mmad_var(1) + 1)) # "concave" curvature(exp(mmad_var(1))) # "convex" curvature(2 * mmad_var(1) + 3 * mmad_var(2)) # "affine" curvature(exp(mmad_var(1)) - exp(mmad_var(2))) # "unknown"
mmad_expr at a parameter vectorComputes the value, gradient, and Hessian of the expression at theta,
all with respect to theta. The gradient is a numeric vector of length
length(theta); the Hessian is a length(theta) by length(theta)
numeric matrix.
evaluate_expr(expr, theta)evaluate_expr(expr, theta)
expr |
An object of class |
theta |
A numeric vector of parameter values. |
This is the Phase 1 replacement for Function_evaluation() – it works
on the new expression tree. The legacy function continues to work on
the old list representation; legacy_to_expr() bridges between them.
A list with components value, gradient, and hessian.
expr <- log(mmad_var(1) + mmad_var(2)) evaluate_expr(expr, c(1, 1))expr <- log(mmad_var(1) + mmad_var(2)) evaluate_expr(expr, c(1, 1))
mmad_expr (or formula) before optimizationRuns curvature inference, top-level summand decomposition, domain
check at init, and a dry-run of minorize_at(). Returns a
structured report with a print() method intended as a quick health
check before invoking mmad().
Function_check(expr, init, data = NULL, tree = FALSE)Function_check(expr, init, data = NULL, tree = FALSE)
expr |
A formula or |
init |
Numeric vector of initial parameter values (named or not). |
data |
Optional |
tree |
Logical; if |
The is_dcp flag reflects strict DCP composition rules and may be
FALSE even for expressions that are fully minorizable by MMAD's
extended rules (e.g. log(1 + exp(theta)) or
exp(log(theta1) + log(theta2))). The operationally relevant
criterion is is_separable: if that is TRUE, mmad() will
optimise the expression successfully.
The print() method renders a full expression tree in which every
node is annotated with [curvature | sign | minorizable?].
A list with class "mmad_check" and components
target_curvature, target_sign, is_dcp, is_minorizable,
summands, domain_ok, domain_message, is_separable,
non_separable_indices, expr_tree, show_tree.
chk <- Function_check(~ log(theta[1] + theta[2]) - theta[1], init = c(1, 1)) print(chk) print(chk, tree = TRUE) # show the expression treechk <- Function_check(~ log(theta[1] + theta[2]) - theta[1], init = c(1, 1)) print(chk) print(chk, tree = TRUE) # show the expression tree
Convenience predicate: TRUE exactly when curvature() is not
"unknown".
is_dcp(expr)is_dcp(expr)
expr |
An |
TRUE or FALSE.
is_dcp(log(mmad_var(1) + 1)) # TRUE is_dcp(exp(mmad_var(1)) - exp(mmad_var(2))) # FALSEis_dcp(log(mmad_var(1) + 1)) # TRUE is_dcp(exp(mmad_var(1)) - exp(mmad_var(2))) # FALSE
mmad_expr
log(), exp(), and sqrt() (the latter as (.)^(1/2)).
## S3 method for class 'mmad_expr' Math(x, ...)## S3 method for class 'mmad_expr' Math(x, ...)
x |
An |
... |
Unused. |
An mmad_expr.
mmad_expr at a current iterateGiven a target expr and a current iterate theta_0, construct a
surrogate S(theta | theta_0) that lower-bounds expr(theta) and is
tangent to it at theta_0. The surrogate is constructed to be as
separable across the coordinates of theta as the structure permits;
anything that resists clean minorization is preserved as-is in a
fallback bucket so that value/gradient/Hessian computations on the
full surrogate remain available.
minorize_at(expr, theta_0)minorize_at(expr, theta_0)
expr |
An |
theta_0 |
A numeric vector giving the current iterate. |
Construction uses only Jensen's inequality and the supporting hyperplane; no per-atom tight surrogates are applied.
A list with components
exprthe full surrogate as an mmad_expr
constantthe additive constant of the surrogate
per_coorda list of length length(theta_0) whose
j-th entry is the 1-d surrogate term in theta[j], or
NULL if no term touches that coordinate
non_separablean mmad_expr representing the
multi-coordinate residue we could not minorize, or NULL if
every leaf was minorized or extracted
is_separableTRUE iff non_separable is NULL
expr <- log(0.5 * mmad_var(1) + 0.5 * mmad_var(2)) surr <- minorize_at(expr, c(2, 1)) surr$is_separable evaluate_expr(surr$expr, c(2, 1))$value # tangent at theta_0expr <- log(0.5 * mmad_var(1) + 0.5 * mmad_var(2)) surr <- minorize_at(expr, c(2, 1)) surr$is_separable evaluate_expr(surr$expr, c(2, 1))$value # tangent at theta_0
mmad_expr targetGiven a target expression expr and an initial parameter vector
init, this iteratively maximizes expr by constructing a
separable surrogate at the current iterate (via minorize_at()),
taking Newton steps on the separable and entangled blocks, and
line-searching on the target to enforce ascent.
mmad( expr, init, data = NULL, tol = 1e-06, max_iter = 1000, line_search_max = 30, track_history = FALSE, verbose = FALSE )mmad( expr, init, data = NULL, tol = 1e-06, max_iter = 1000, line_search_max = 30, track_history = FALSE, verbose = FALSE )
expr |
An |
init |
Numeric vector of initial parameter values.
May be named, in which case the names define the parameter
vocabulary (e.g. |
data |
Optional list / data frame of values for theta-free symbols referenced in the formula. Default: the formula's environment. |
tol |
Convergence tolerance for both the maximum absolute parameter change and the maximum absolute target gradient component. |
max_iter |
Maximum number of MM iterations (default 1000). |
line_search_max |
Maximum number of step-halving steps per iteration's line search (default 30). |
track_history |
If |
verbose |
If |
A list with components
estimatethe final parameter vector (named if init was named)
valuethe target value at estimate
iterationsnumber of iterations actually run
convergedTRUE iff both convergence criteria were met
historyper-iteration diagnostics (data frame), or NULL
messagecharacter: short status string
fit <- mmad(~ log(theta[1] + theta[2]) - theta[1] - theta[2] + 2, init = c(2, 2)) fit$estimate # Poisson regression via sum() and X %*% theta: set.seed(1); n <- 50; p <- 2 X <- matrix(rnorm(n * p, sd = 0.5), nrow = n) y <- rpois(n, exp(X %*% c(0.3, -0.2))) mmad(~ sum(y * (X %*% theta) - exp(X %*% theta)), init = rep(0, p), data = list(X = X, y = y))fit <- mmad(~ log(theta[1] + theta[2]) - theta[1] - theta[2] + 2, init = c(2, 2)) fit$estimate # Poisson regression via sum() and X %*% theta: set.seed(1); n <- 50; p <- 2 X <- matrix(rnorm(n * p, sd = 0.5), nrow = n) y <- rpois(n, exp(X %*% c(0.3, -0.2))) mmad(~ sum(y * (X %*% theta) - exp(X %*% theta)), init = rep(0, p), data = list(X = X, y = y))
Construct a numeric-constant expression node
mmad_const(value)mmad_const(value)
value |
A finite numeric scalar. |
An object of class mmad_expr (subclass mmad_const).
mmad_const(2.5)mmad_const(2.5)
theta[i]
Construct a parameter reference theta[i]
mmad_var(i, sign = "unknown")mmad_var(i, sign = "unknown")
i |
Positive integer index into the parameter vector. |
sign |
Optional declaration of the sign of |
An object of class mmad_expr (subclass mmad_var).
mmad_var(1) mmad_var(2, sign = "positive")mmad_var(1) mmad_var(2, sign = "positive")
mmad_expr
Lets users write expressions naturally: 2 * theta1 - log(theta1 + theta2)
becomes a properly-typed expression tree.
## S3 method for class 'mmad_expr' Ops(e1, e2)## S3 method for class 'mmad_expr' Ops(e1, e2)
e1, e2
|
An |
Restrictions enforced at construction time:
expr * expr is rejected (one side must be a numeric scalar) – products
of two parameter-dependent expressions are non-DCP.
numeric / expr is rejected – division by an expression is non-DCP.
expr ^ expr is rejected – the exponent must be a numeric scalar.
An mmad_expr.
mmad_expr
Inferred sign of an mmad_expr
sign_of(expr)sign_of(expr)
expr |
An |
One of "positive", "nonneg", "zero", "nonpos",
"negative", "unknown".
sign_of(exp(mmad_var(1))) # "positive" sign_of(mmad_var(1) ^ 2) # "nonneg"sign_of(exp(mmad_var(1))) # "positive" sign_of(mmad_var(1) ^ 2) # "nonneg"