
A Fixed Rerandomization Design (Rejection-Sampled on Covariate Balance)
Source:R/design_fixed_rerandomization.R
DesignFixedRerandomization.RdA fixed-sample-size DesignFixed implementing
rerandomization (Morgan and Rubin, 2012): candidate allocations \(w\) are drawn
from the design's base randomization law (balanced complete randomization when
prob_T = 0.5, i.i.d. \(\mathrm{Bernoulli}(prob\_T)\) draws otherwise — see
generate_one_rerandomized_w()) and only accepted if a covariate
imbalance criterion \(M(w)\) between the treated and control groups falls below a
threshold a (obj_val_cutoff), i.e. the accepted allocations are drawn
from the base randomization distribution truncated to \(\{w : M(w) \le a\}\).
Unlike DesignFixedGreedy/
DesignFixedGreedyDOptimal, which search for a
single well-balanced allocation, rerandomization instead filters the design's
own randomization distribution, which is what makes it directly compatible with
Fisherian/randomization-based inference (the accepted allocations remain a
well-defined — if truncated — randomization distribution, valid for randomization
tests/CIs restricted to that truncated support).
Two mutually exclusive acceptance modes are supported (specifying both errors):
obj_val_cutoff accepts/rejects each draw against a fixed threshold a;
prop_acceptable instead draws \(r / prop\_acceptable\) candidates and keeps
the \(r\) with the lowest \(M(w)\) (an empirical top-quantile acceptance
region, equivalent in the large-draw limit to an implied cutoff at the
prop_acceptable quantile of \(M(w)\)'s distribution under the base
randomization law).
Objective \(M(w)\). objective = "mahal_dist" (default) uses the
squared Mahalanobis distance between treated and control covariate means,
\(M(w) = (\bar X_T - \bar X_C)^\top S^{-1} (\bar X_T - \bar X_C)\), where \(S\) is
the sample covariance of all covariates (ridge-regularized by \(10^{-6}I\) if
\(|\det S| < 10^{-10}\)), computed once and cached in private$S_inv.
objective = "abs_sum_diff" instead uses the sum of absolute mean differences,
\(M(w) = \sum_j |\bar X_{T,j} - \bar X_{C,j}|\), with no correlation adjustment.
Only these two objectives are supported; any other value errors at draw time.
Fast path (native C++). When prob_T = 0.5 and \(n\) is even,
candidate generation and filtering run via a parallel C++ rejection sampler
(rerandomization_search_cpp()), which internally works with a rescaled
objective \(f_{\mathrm{cpp}}\): \(f_{\mathrm{cpp}} = M(w)/4\) for
"mahal_dist" and \(f_{\mathrm{cpp}} = M(w)/2\) (on a GED-standardized scale)
for "abs_sum_diff"; the user-facing obj_val_cutoff is converted to this
internal scale before being passed to C++, so the accepted-allocation semantics are
unaffected, but this rescaling is a backend implementation detail worth knowing when
comparing C++-path and pure-R-path acceptance rates for the same nominal cutoff. The
sampler draws up to max(r * 1000, 100000) candidates internally; if fewer than
r allocations are accepted within that budget (an overly tight cutoff), this
errors naming how many were actually found – loosen obj_val_cutoff or use
prop_acceptable instead. (Earlier versions silently recycled the accepted set
to pad out to r, duplicating some draws; fixed, since that meant some
"independent" replicates were literal duplicates of an accepted allocation.)
Seed reproducibility and multi-core parallelism. The C++ fast path's
rejection sampler is a genuine work-stealing search: with more than one core
(set_num_cores/a fork cluster/mirai daemons; the package default is a
single core), threads race via atomic operations for both which candidate draws to
try next and which output column an accepted draw claims, so which per-thread-seeded
RNG stream ends up producing a given replicate – and in what order – depends on
real-time OS scheduling, not just seed. With the default single core, draws
are exactly seed-reproducible; this is not guaranteed once more than one core
is in use. Contrast with DesignFixedGreedy/
DesignFixedBinaryMatch, whose C++ kernels
use static (not work-stealing) thread scheduling and remain seed-reproducible
regardless of core count.
prop_acceptable path. Uses
complete_randomization_forced_balanced_cpp() (balanced case) or
complete_randomization_imbalanced_cpp() (prob_T != 0.5) to draw
\(n_{\mathrm{draw}} = \mathrm{round}(r / prop\_acceptable)\) candidate allocations
in one batched call, computes \(M(w)\) for all of them via
compute_objective_vals_cpp(), and keeps the \(r\) with smallest \(M(w)\).
Pure-R fallback (unbalanced or odd \(n\), obj_val_cutoff mode
only). Draws one candidate at a time via generate_one_rerandomized_w() in an
unbounded repeat loop that accepts the first candidate with
\(M(w) \le a\). Unlike the C++ fast path, this fallback has no draw-count
safety limit: if obj_val_cutoff is set tight enough that acceptance
probability under the base randomization law is extremely small for this \(n\)/
covariate structure, this loop can run for a very long time (in principle
indefinitely) before finding an acceptable draw.
References
Morgan, K. L., and Rubin, D. B. (2012). "Rerandomization to improve covariate balance in experiments." The Annals of Statistics, 40(2), 1263-1282, doi:10.1214/12-AOS1008 , for the rerandomization framework and its randomization-inference validity. See also Mahalanobis distance for the default objective.
Super classes
Design -> DesignFixed -> DesignFixedRerandomization
Methods
+ inherited public methods from DesignFixed
+ inherited public methods from Design
Design$add_one_subject_response()Design$any_censoring()Design$applicable_inference_class_names()Design$assert_all_responses_recorded()Design$assert_all_subjects_arrived()Design$assert_even_allocation()Design$assert_fixed_sample()Design$capabilities()Design$check_experiment_completed()Design$draw_ws_according_to_design()Design$duplicate()Design$get_X()Design$get_X_imp()Design$get_X_raw()Design$get_design_formula()Design$get_edi_version_created()Design$get_effective_dead()Design$get_effective_time()Design$get_missingness_method()Design$get_n()Design$get_ordinal_levels()Design$get_original_ordinal_levels()Design$get_prob_T()Design$get_response_type()Design$get_response_type_original()Design$get_t()Design$get_w()Design$get_y()Design$get_y_L()Design$get_y_R()Design$get_y_original()Design$has_general_censoring()Design$incompatible_inference_classes_due_to_design_structure()Design$is_a_bernoulli_capable()Design$is_a_cluster_capable()Design$is_a_kk_matching_capable()Design$is_blocking_design()Design$is_fixed_sample_size()Design$is_matching_design()Design$prepare_for_resampling_replay()Design$randomization_family()Design$supports()Design$supports_randomization_draw()Design$supports_resampling()Design$supports_resampling_replay()Design$transform_y()Design$unavailable_inference_classes_due_to_missing_packages()Design$warm_all_subject_data_cache()
DesignFixedRerandomization$new()
Initialize a rerandomization fixed experimental design.
Exactly one of obj_val_cutoff/prop_acceptable may be
specified (or neither, which accepts every candidate, i.e. no filtering);
supplying both raises an error. See class documentation for the exact
acceptance semantics of each mode and the covariate-imbalance objective.
Usage
DesignFixedRerandomization$new(
response_type,
prob_T = 0.5,
obj_val_cutoff = NULL,
prop_acceptable = NULL,
objective = "mahal_dist",
include_is_missing_as_a_new_feature = TRUE,
n = NULL,
verbose = FALSE,
missingness_method = "impute",
design_formula = ~.,
seed = NULL
)Arguments
response_typeThe data type of response values.
prob_TThe probability of the treatment assignment.
obj_val_cutoffThe maximum allowable objective value \(a\); a candidate allocation is accepted iff \(M(w) \le a\). Cannot be specified together with prop_acceptable.
prop_acceptableThe proportion of randomizations to accept (draws r/prop_acceptable total, returns r lowest). Cannot be specified together with obj_val_cutoff.
objectiveThe covariate-imbalance objective \(M(w)\) to filter on: either
"mahal_dist"(default, squared Mahalanobis distance) or"abs_sum_diff"(sum of absolute mean differences); see class documentation for the exact formulas.include_is_missing_as_a_new_featureFlag for missingness indicators.
nThe sample size.
verboseFlag for verbosity.
missingness_methodHow to handle missing values in covariates.
design_formulaA formula object.
seedInteger seed for reproducibility.