From d835650b416eb5f9e442d8e8424b47a7c9c70e8d Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 17:18:36 -0800 Subject: [PATCH 01/10] Add `max.smd` arg --- r-package/balnet/R/balnet.R | 7 +++++-- r-package/balnet/R/utils.R | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/r-package/balnet/R/balnet.R b/r-package/balnet/R/balnet.R index db23138..c61091f 100644 --- a/r-package/balnet/R/balnet.R +++ b/r-package/balnet/R/balnet.R @@ -7,6 +7,7 @@ #' @param W Treatment vector (0: control, 1: treated). #' @param target The target estimand. Default is ATE. #' @param sample.weights Optional sample weights. If `NULL` (default), then each unit receives the same weight. +#' @param max.smd TODO #' @param nlambda Number of values for `lambda`, if generated automatically. Default is 100. #' @param lambda.min.ratio Ratio between smallest and largest value of lambda. Default is 1e-2. #' @param lambda Optional `lambda` sequence. By default, the `lambda` sequence is constructed automatically using `nlambda` and `lambda.min.ratio`. @@ -57,6 +58,7 @@ balnet <- function( W, target = c("ATE", "ATT", "treated", "control"), sample.weights = NULL, + max.smd = NULL, nlambda = 100L, lambda.min.ratio = 1e-2, lambda = NULL, @@ -116,6 +118,7 @@ balnet <- function( } else { target_scale = 1 } + lambda.min.ratio <- get_lambda_min_ratio(lambda.min.ratio, max.smd, stan$X, W, sample.weights, target, alpha) fit0 <- fit1 <- NULL lmdas0 <- lmdas1 <- NULL @@ -128,7 +131,7 @@ balnet <- function( target_scale = target_scale, lambda = lambda.in[[1]], lmda_path_size = nlambda, - min_ratio = lambda.min.ratio, + min_ratio = lambda.min.ratio[[1]], penalty = penalty.factor, groups = groups, alpha = alpha, @@ -149,7 +152,7 @@ balnet <- function( target_scale = target_scale, lambda = lambda.in[[2]], lmda_path_size = nlambda, - min_ratio = lambda.min.ratio, + min_ratio = lambda.min.ratio[[2]], penalty = penalty.factor, groups = groups, alpha = alpha, diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index 13f875e..c9672ca 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -71,6 +71,38 @@ standardize <- function( list(X = X, center = center, scale = scale) } +get_lambda_min_ratio <- function(lambda.min.ratio, max.smd, X.stan, W, sample.weights, target, alpha) { + if (is.null(max.smd)) { + out <- c(lambda.min.ratio, lambda.min.ratio) + } else { + if (alpha < 1) { + stop("setting max.smd is only possible with lasso (alpha = 1).") + } + if (max.smd <= 0) { + stop("lambda.min should be > 0.") + } + lambda.min.ratio0 <- lambda.min.ratio1 <- lambda.min.ratio + if (target %in% c("ATE", "ATT", "control")) { + stats0 <- col_stats(X.stan, weights = (1 - W) * sample.weights) + lambda0.max <- max(abs(stats0$center)) + if (max.smd < lambda0.max) { + lambda.min.ratio0 <- max.smd / lambda0.max + } + } + if (target %in% c("ATE", "treated")) { + stats1 <- col_stats(X.stan, weights = W * sample.weights + ) + lambda1.max <- max(abs(stats1$center)) + if (max.smd < lambda1.max) { + lambda.min.ratio1 <- max.smd / lambda1.max + } + } + out <- c(lambda.min.ratio0, lambda.min.ratio1) + } + + out +} + validate_lambda <- function(lambda) { if (is.character(lambda)) { stop("Unsupported lambda argument.") From c31c2eb4866baed2142e12d2a493073754f10e6f Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 17:25:12 -0800 Subject: [PATCH 02/10] S --- r-package/balnet/R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index c9672ca..40c58e5 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -76,7 +76,7 @@ get_lambda_min_ratio <- function(lambda.min.ratio, max.smd, X.stan, W, sample.we out <- c(lambda.min.ratio, lambda.min.ratio) } else { if (alpha < 1) { - stop("setting max.smd is only possible with lasso (alpha = 1).") + stop("Setting max.smd is only possible with lasso (alpha = 1).") } if (max.smd <= 0) { stop("lambda.min should be > 0.") From 2fdce4cfb13aa384eceb1feebbe21d2d732291c4 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 17:30:47 -0800 Subject: [PATCH 03/10] arg order --- r-package/balnet/R/balnet.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/r-package/balnet/R/balnet.R b/r-package/balnet/R/balnet.R index c61091f..cf27c65 100644 --- a/r-package/balnet/R/balnet.R +++ b/r-package/balnet/R/balnet.R @@ -94,7 +94,6 @@ balnet <- function( } else if (is.null(sample.weights)) { sample.weights <- rep_len(1, nrow(X)) } - lambda.in <- validate_lambda(lambda) if (is.character(standardize) && standardize == "inplace") { inplace <- TRUE standardize <- TRUE @@ -103,6 +102,7 @@ balnet <- function( } else { stop("Invalid standardize option.") } + lambda.in <- validate_lambda(lambda) colnames <- if (is.null(colnames(X))) make.names(1:ncol(X)) else colnames(X) validate_groups(groups, ncol(X), colnames) @@ -113,12 +113,13 @@ balnet <- function( inplace = inplace, n_threads = num.threads ) + lambda.min.ratio <- get_lambda_min_ratio(lambda.min.ratio, max.smd, stan$X, W, sample.weights, target, alpha) + if (target == "ATT") { target_scale = sum(sample.weights) / sum(sample.weights * W) # "n / n_1" } else { target_scale = 1 } - lambda.min.ratio <- get_lambda_min_ratio(lambda.min.ratio, max.smd, stan$X, W, sample.weights, target, alpha) fit0 <- fit1 <- NULL lmdas0 <- lmdas1 <- NULL From 0a0033f172060025b98e189fcd37fcf3ba1a7244 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 17:58:01 -0800 Subject: [PATCH 04/10] docstr --- r-package/balnet/R/balnet.R | 10 ++++++++-- r-package/balnet/man/balnet.Rd | 11 ++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/r-package/balnet/R/balnet.R b/r-package/balnet/R/balnet.R index cf27c65..a3f5f47 100644 --- a/r-package/balnet/R/balnet.R +++ b/r-package/balnet/R/balnet.R @@ -7,10 +7,16 @@ #' @param W Treatment vector (0: control, 1: treated). #' @param target The target estimand. Default is ATE. #' @param sample.weights Optional sample weights. If `NULL` (default), then each unit receives the same weight. -#' @param max.smd TODO +#' @param max.smd Optional upper bound on the standardized mean difference. +#' For lasso penalization (`alpha = 1`), there is a one-to-one correspondence between the penalty parameter +#' \eqn{\lambda} and the maximum allowable covariate imbalance under the balancing loss. +#' When supplied, \code{max.smd} is used to adjust the lambda sequence (via `lambda.min.ratio`) so that the +#' generated lambda sequence ends at the specified imbalance level. #' @param nlambda Number of values for `lambda`, if generated automatically. Default is 100. #' @param lambda.min.ratio Ratio between smallest and largest value of lambda. Default is 1e-2. -#' @param lambda Optional `lambda` sequence. By default, the `lambda` sequence is constructed automatically using `nlambda` and `lambda.min.ratio`. +#' @param lambda Optional `lambda` sequence. +#' By default, the `lambda` sequence is constructed automatically using `nlambda` and `lambda.min.ratio` +#' (or `max.smd`, if specified). #' @param penalty.factor Penalty factor per feature. Default is 1 (i.e, each feature recieves the same penalty). #' @param groups An optional list of group indices for group penalization. #' @param alpha Elastic net mixing parameter. Default is 1 (lasso). 0 is ridge. diff --git a/r-package/balnet/man/balnet.Rd b/r-package/balnet/man/balnet.Rd index 4b398e8..8697305 100644 --- a/r-package/balnet/man/balnet.Rd +++ b/r-package/balnet/man/balnet.Rd @@ -9,6 +9,7 @@ balnet( W, target = c("ATE", "ATT", "treated", "control"), sample.weights = NULL, + max.smd = NULL, nlambda = 100L, lambda.min.ratio = 0.01, lambda = NULL, @@ -32,11 +33,19 @@ balnet( \item{sample.weights}{Optional sample weights. If \code{NULL} (default), then each unit receives the same weight.} +\item{max.smd}{Optional upper bound on the standardized mean difference. +For lasso penalization (\code{alpha = 1}), there is a one-to-one correspondence between the penalty parameter +\eqn{\lambda} and the maximum allowable covariate imbalance under the balancing loss. +When supplied, \code{max.smd} is used to adjust the lambda sequence (via \code{lambda.min.ratio}) so that the +generated lambda sequence ends at the specified imbalance level.} + \item{nlambda}{Number of values for \code{lambda}, if generated automatically. Default is 100.} \item{lambda.min.ratio}{Ratio between smallest and largest value of lambda. Default is 1e-2.} -\item{lambda}{Optional \code{lambda} sequence. By default, the \code{lambda} sequence is constructed automatically using \code{nlambda} and \code{lambda.min.ratio}.} +\item{lambda}{Optional \code{lambda} sequence. +By default, the \code{lambda} sequence is constructed automatically using \code{nlambda} and \code{lambda.min.ratio} +(or \code{max.smd}, if specified).} \item{penalty.factor}{Penalty factor per feature. Default is 1 (i.e, each feature recieves the same penalty).} From fcdbc9520e1e45effdfac907fa88816d071a5d39 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 18:26:58 -0800 Subject: [PATCH 05/10] vig --- r-package/balnet/site/get-started.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r-package/balnet/site/get-started.Rmd b/r-package/balnet/site/get-started.Rmd index a2dfa46..22417e1 100644 --- a/r-package/balnet/site/get-started.Rmd +++ b/r-package/balnet/site/get-started.Rmd @@ -59,7 +59,7 @@ max(abs(smd.baseline)) Since the smallest value of $\lambda$ attained for the treated arm is approximately $\lambda_{\min} \approx 0.21$, this indicates that the closest we can bring the standardized treated covariate means to the overall means is an absolute SMD of about 0.21. -This interpretation of $\lambda$ provides a convenient way to target a desired level of imbalance. Users can compute $\lambda^{\max}$ for their dataset and then choose `lambda.min.ratio` to reflect an acceptable fraction of this maximum imbalance. For example, if $\lambda^{\max} = 10$, the default setting `lambda.min.ratio = 0.01` corresponds to a target maximum absolute SMD of $10 \times 0.01 = 0.1$. The algorithm then attempts to compute the full regularization path, stopping gracefully if further reductions in imbalance are not achievable (in cases where balance remains approximate, users may wish to augment IPW estimation with an outcome model). +This interpretation of $\lambda$ provides a convenient way to target a desired level of imbalance, available through the option `max.smd`. For lasso penalization, `balnet` then adjusts the generated $\lambda$ sequence so that it terminates at this value. The algorithm then attempts to compute the full regularization path, stopping gracefully if further reductions in imbalance are not achievable. Alternatively, users may compute $\lambda^{\max}$ (e.g., the maximum absolute unweighted SMD) for their dataset and then choose `lambda.min.ratio` to reflect an acceptable fraction of this maximum imbalance. For example, if $\lambda^{\max} = 10$, the default setting `lambda.min.ratio = 0.01` corresponds to a target maximum absolute SMD of $10 \times 0.01 = 0.1$. > *Note*: Setting lambda = 0 to try to achieve exact balance is not recommended, just as `glmnet` advises against it. `balnet` works best by using warm starts and gradually decreasing regularization, a strategy similar to barrier methods in convex optimization. This approach helps the algorithm converge reliably and improves performance on real-world datasets where achieving covariate balance can be difficult. From ccfa05a93e49b8e37e01eda8a15a7dd7d6de7298 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 18:27:55 -0800 Subject: [PATCH 06/10] wpd --- r-package/balnet/site/get-started.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r-package/balnet/site/get-started.Rmd b/r-package/balnet/site/get-started.Rmd index 22417e1..7edd468 100644 --- a/r-package/balnet/site/get-started.Rmd +++ b/r-package/balnet/site/get-started.Rmd @@ -65,7 +65,7 @@ This interpretation of $\lambda$ provides a convenient way to target a desired l ## Plotting path diagnostics -`balnet` provides default plotting methods for visualizing regularization path diagnostics. Calling `plot` without additional arguments produces a summary of key metrics along the path, indexed by $\lambda$ on the log scale. +`balnet` provides default plotting methods for visualizing regularization path diagnostics. Calling `plot` without additional arguments produces a summary of metrics along the path, indexed by $\lambda$ on the log scale. ```{r} plot(fit) From 3fc0d4db37b9b564fd447f1deb86d2d7aaf48700 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 18:30:03 -0800 Subject: [PATCH 07/10] wp --- r-package/balnet/R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index 40c58e5..a169c32 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -79,7 +79,7 @@ get_lambda_min_ratio <- function(lambda.min.ratio, max.smd, X.stan, W, sample.we stop("Setting max.smd is only possible with lasso (alpha = 1).") } if (max.smd <= 0) { - stop("lambda.min should be > 0.") + stop("max.smd should be > 0.") } lambda.min.ratio0 <- lambda.min.ratio1 <- lambda.min.ratio if (target %in% c("ATE", "ATT", "control")) { From cfdc0e30ccec99b6d84b7dda9af1765bedc6fb82 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 18:32:23 -0800 Subject: [PATCH 08/10] wp --- r-package/balnet/R/utils.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index a169c32..6ccc08c 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -90,8 +90,7 @@ get_lambda_min_ratio <- function(lambda.min.ratio, max.smd, X.stan, W, sample.we } } if (target %in% c("ATE", "treated")) { - stats1 <- col_stats(X.stan, weights = W * sample.weights - ) + stats1 <- col_stats(X.stan, weights = W * sample.weights) lambda1.max <- max(abs(stats1$center)) if (max.smd < lambda1.max) { lambda.min.ratio1 <- max.smd / lambda1.max From b99f6cc053046d5d67d29ab9137c9951ee024226 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 19:30:03 -0800 Subject: [PATCH 09/10] rename --- r-package/balnet/R/balnet.R | 12 ++++++------ r-package/balnet/R/utils.R | 18 +++++++++--------- r-package/balnet/man/balnet.Rd | 10 +++++----- r-package/balnet/site/get-started.Rmd | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/r-package/balnet/R/balnet.R b/r-package/balnet/R/balnet.R index a3f5f47..81bc0ce 100644 --- a/r-package/balnet/R/balnet.R +++ b/r-package/balnet/R/balnet.R @@ -7,16 +7,16 @@ #' @param W Treatment vector (0: control, 1: treated). #' @param target The target estimand. Default is ATE. #' @param sample.weights Optional sample weights. If `NULL` (default), then each unit receives the same weight. -#' @param max.smd Optional upper bound on the standardized mean difference. +#' @param max.imbalance Optional upper bound on the covariate imbalance. #' For lasso penalization (`alpha = 1`), there is a one-to-one correspondence between the penalty parameter -#' \eqn{\lambda} and the maximum allowable covariate imbalance under the balancing loss. -#' When supplied, \code{max.smd} is used to adjust the lambda sequence (via `lambda.min.ratio`) so that the +#' \eqn{\lambda} and the maximum allowable covariate imbalance. +#' When supplied, `max.imbalance` is used to adjust the lambda sequence (via `lambda.min.ratio`) so that the #' generated lambda sequence ends at the specified imbalance level. #' @param nlambda Number of values for `lambda`, if generated automatically. Default is 100. #' @param lambda.min.ratio Ratio between smallest and largest value of lambda. Default is 1e-2. #' @param lambda Optional `lambda` sequence. #' By default, the `lambda` sequence is constructed automatically using `nlambda` and `lambda.min.ratio` -#' (or `max.smd`, if specified). +#' (or `max.imbalance`, if specified). #' @param penalty.factor Penalty factor per feature. Default is 1 (i.e, each feature recieves the same penalty). #' @param groups An optional list of group indices for group penalization. #' @param alpha Elastic net mixing parameter. Default is 1 (lasso). 0 is ridge. @@ -64,7 +64,7 @@ balnet <- function( W, target = c("ATE", "ATT", "treated", "control"), sample.weights = NULL, - max.smd = NULL, + max.imbalance = NULL, nlambda = 100L, lambda.min.ratio = 1e-2, lambda = NULL, @@ -119,7 +119,7 @@ balnet <- function( inplace = inplace, n_threads = num.threads ) - lambda.min.ratio <- get_lambda_min_ratio(lambda.min.ratio, max.smd, stan$X, W, sample.weights, target, alpha) + lambda.min.ratio <- get_lambda_min_ratio(lambda.min.ratio, max.imbalance, stan$X, W, sample.weights, target, alpha) if (target == "ATT") { target_scale = sum(sample.weights) / sum(sample.weights * W) # "n / n_1" diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index 6ccc08c..4912bf0 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -71,29 +71,29 @@ standardize <- function( list(X = X, center = center, scale = scale) } -get_lambda_min_ratio <- function(lambda.min.ratio, max.smd, X.stan, W, sample.weights, target, alpha) { - if (is.null(max.smd)) { +get_lambda_min_ratio <- function(lambda.min.ratio, max.imbalance, X.stan, W, sample.weights, target, alpha) { + if (is.null(max.imbalance)) { out <- c(lambda.min.ratio, lambda.min.ratio) } else { if (alpha < 1) { - stop("Setting max.smd is only possible with lasso (alpha = 1).") + stop("Setting max.imbalance is only possible with lasso (alpha = 1).") } - if (max.smd <= 0) { - stop("max.smd should be > 0.") + if (max.imbalance <= 0) { + stop("max.imbalance should be > 0.") } lambda.min.ratio0 <- lambda.min.ratio1 <- lambda.min.ratio if (target %in% c("ATE", "ATT", "control")) { stats0 <- col_stats(X.stan, weights = (1 - W) * sample.weights) lambda0.max <- max(abs(stats0$center)) - if (max.smd < lambda0.max) { - lambda.min.ratio0 <- max.smd / lambda0.max + if (max.imbalance < lambda0.max) { + lambda.min.ratio0 <- max.imbalance / lambda0.max } } if (target %in% c("ATE", "treated")) { stats1 <- col_stats(X.stan, weights = W * sample.weights) lambda1.max <- max(abs(stats1$center)) - if (max.smd < lambda1.max) { - lambda.min.ratio1 <- max.smd / lambda1.max + if (max.imbalance < lambda1.max) { + lambda.min.ratio1 <- max.imbalance / lambda1.max } } out <- c(lambda.min.ratio0, lambda.min.ratio1) diff --git a/r-package/balnet/man/balnet.Rd b/r-package/balnet/man/balnet.Rd index 8697305..857770b 100644 --- a/r-package/balnet/man/balnet.Rd +++ b/r-package/balnet/man/balnet.Rd @@ -9,7 +9,7 @@ balnet( W, target = c("ATE", "ATT", "treated", "control"), sample.weights = NULL, - max.smd = NULL, + max.imbalance = NULL, nlambda = 100L, lambda.min.ratio = 0.01, lambda = NULL, @@ -33,10 +33,10 @@ balnet( \item{sample.weights}{Optional sample weights. If \code{NULL} (default), then each unit receives the same weight.} -\item{max.smd}{Optional upper bound on the standardized mean difference. +\item{max.imbalance}{Optional upper bound on the covariate imbalance. For lasso penalization (\code{alpha = 1}), there is a one-to-one correspondence between the penalty parameter -\eqn{\lambda} and the maximum allowable covariate imbalance under the balancing loss. -When supplied, \code{max.smd} is used to adjust the lambda sequence (via \code{lambda.min.ratio}) so that the +\eqn{\lambda} and the maximum allowable covariate imbalance. +When supplied, \code{max.imbalance} is used to adjust the lambda sequence (via \code{lambda.min.ratio}) so that the generated lambda sequence ends at the specified imbalance level.} \item{nlambda}{Number of values for \code{lambda}, if generated automatically. Default is 100.} @@ -45,7 +45,7 @@ generated lambda sequence ends at the specified imbalance level.} \item{lambda}{Optional \code{lambda} sequence. By default, the \code{lambda} sequence is constructed automatically using \code{nlambda} and \code{lambda.min.ratio} -(or \code{max.smd}, if specified).} +(or \code{max.imbalance}, if specified).} \item{penalty.factor}{Penalty factor per feature. Default is 1 (i.e, each feature recieves the same penalty).} diff --git a/r-package/balnet/site/get-started.Rmd b/r-package/balnet/site/get-started.Rmd index 7edd468..98d25b3 100644 --- a/r-package/balnet/site/get-started.Rmd +++ b/r-package/balnet/site/get-started.Rmd @@ -59,7 +59,7 @@ max(abs(smd.baseline)) Since the smallest value of $\lambda$ attained for the treated arm is approximately $\lambda_{\min} \approx 0.21$, this indicates that the closest we can bring the standardized treated covariate means to the overall means is an absolute SMD of about 0.21. -This interpretation of $\lambda$ provides a convenient way to target a desired level of imbalance, available through the option `max.smd`. For lasso penalization, `balnet` then adjusts the generated $\lambda$ sequence so that it terminates at this value. The algorithm then attempts to compute the full regularization path, stopping gracefully if further reductions in imbalance are not achievable. Alternatively, users may compute $\lambda^{\max}$ (e.g., the maximum absolute unweighted SMD) for their dataset and then choose `lambda.min.ratio` to reflect an acceptable fraction of this maximum imbalance. For example, if $\lambda^{\max} = 10$, the default setting `lambda.min.ratio = 0.01` corresponds to a target maximum absolute SMD of $10 \times 0.01 = 0.1$. +This interpretation of $\lambda$ provides a convenient way to target a desired level of imbalance, available through the option `max.imbalance`. For lasso penalization, `balnet` then adjusts the generated $\lambda$ sequence so that it terminates at this value. The algorithm then attempts to compute the full regularization path, stopping gracefully if further reductions in imbalance are not achievable. Alternatively, users may compute $\lambda^{\max}$ (e.g., the maximum absolute unweighted SMD) for their dataset and then choose `lambda.min.ratio` to reflect an acceptable fraction of this maximum imbalance. For example, if $\lambda^{\max} = 10$, the default setting `lambda.min.ratio = 0.01` corresponds to a target maximum absolute SMD of $10 \times 0.01 = 0.1$. > *Note*: Setting lambda = 0 to try to achieve exact balance is not recommended, just as `glmnet` advises against it. `balnet` works best by using warm starts and gradually decreasing regularization, a strategy similar to barrier methods in convex optimization. This approach helps the algorithm converge reliably and improves performance on real-world datasets where achieving covariate balance can be difficult. From 2d5817986fabb0db9410f853acd373fd0da5668c Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 24 Jan 2026 19:36:33 -0800 Subject: [PATCH 10/10] wpd --- r-package/balnet/R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r-package/balnet/R/utils.R b/r-package/balnet/R/utils.R index 4912bf0..2e56d81 100644 --- a/r-package/balnet/R/utils.R +++ b/r-package/balnet/R/utils.R @@ -84,7 +84,7 @@ get_lambda_min_ratio <- function(lambda.min.ratio, max.imbalance, X.stan, W, sam lambda.min.ratio0 <- lambda.min.ratio1 <- lambda.min.ratio if (target %in% c("ATE", "ATT", "control")) { stats0 <- col_stats(X.stan, weights = (1 - W) * sample.weights) - lambda0.max <- max(abs(stats0$center)) + lambda0.max <- max(abs(stats0$center)) # Note, this assumes X.stan is standardized. if (max.imbalance < lambda0.max) { lambda.min.ratio0 <- max.imbalance / lambda0.max }