% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/matching_full.R
\name{full_match}
\alias{full_match}
\title{Full Matching}
\usage{
full_match(
  left,
  right,
  vars,
  distance = "euclidean",
  min_controls = 1,
  max_controls = Inf,
  caliper = NULL,
  caliper_sd = NULL,
  weights = NULL,
  scale = FALSE,
  auto_scale = FALSE,
  sigma = NULL,
  left_id = "id",
  right_id = "id",
  method = "optimal",
  memory_mode = "auto"
)
}
\arguments{
\item{left}{Data frame of left (treated) units}

\item{right}{Data frame of right (control) units}

\item{vars}{Character vector of variable names to match on}

\item{distance}{Distance metric: \code{"euclidean"} (default),
\code{"mahalanobis"}, \code{"manhattan"}, or a custom function}

\item{min_controls}{Minimum number of right units per group (default: 1)}

\item{max_controls}{Maximum number of right units per group (default: Inf)}

\item{caliper}{Maximum allowable distance for a match. Units with no
eligible partner within the caliper are left unmatched.}

\item{caliper_sd}{If not NULL, caliper is expressed in standard deviations
of the pooled distance distribution rather than absolute units.}

\item{weights}{Named numeric vector of variable weights}

\item{scale}{Scaling method: \code{FALSE} (default), \code{"robust"},
\code{"standardize"}, or \code{"range"}}

\item{auto_scale}{If TRUE, automatically preprocess and scale variables}

\item{sigma}{Optional covariance matrix for Mahalanobis distance}

\item{left_id}{Name of ID column in left (default: \code{"id"})}

\item{right_id}{Name of ID column in right (default: \code{"id"})}

\item{method}{Matching algorithm: \code{"optimal"} (default) uses min-cost
max-flow to find the globally optimal group assignment minimizing total
distance; \code{"greedy"} uses a fast two-pass heuristic.}

\item{memory_mode}{One of "auto" (default) or "dense". "auto" warns if the
dense cost matrix would consume a large fraction of free system RAM.
\code{full_match()} uses a different (min-cost-flow) solver backend than
\code{match_couples()}/\code{assignment()}, so \code{memory_mode = "lazy"} is not
available here yet and errors if requested; "dense" skips the RAM check
entirely.}
}
\value{
An S3 object of class \code{c("full_matching_result", "couplr_result")}
containing:
\describe{
\item{groups}{Tibble with columns \code{group_id}, \code{id}, \code{side}
(\code{"left"}/\code{"right"}), and \code{weight}}
\item{info}{List with \code{n_groups}, \code{n_left}, \code{n_right},
\code{n_unmatched_left}, \code{n_unmatched_right}, \code{method},
\code{vars}}
\item{unmatched}{List of unmatched left and right IDs (if caliper excludes
units)}
}
}
\description{
Assigns every unit (left and right) to a matched group with variable ratios
(1:k or k:1). Unlike 1:1 matching, full matching does not discard units,
producing matched groups where each group contains at least one left and one
right unit.
}
\details{
Full matching creates matched groups of variable size. Two algorithms are
available:

\strong{Optimal} (\code{method = "optimal"}, default): Solves a min-cost
max-flow problem that minimizes total distance across all group assignments
simultaneously. Each left unit becomes a group center absorbing 1 to
\code{max_controls} right units, with the globally optimal assignment found
via Dijkstra's algorithm with Johnson potentials. When \code{n_left > n_right},
roles are transposed automatically.

\strong{Greedy} (\code{method = "greedy"}): A fast two-pass heuristic:
\enumerate{
\item Each left unit picks its nearest eligible right unit
\item Remaining right units are assigned to their nearest already-matched
left unit, respecting \code{max_controls}
}
This is faster but does not guarantee globally optimal results.

Weights are computed so that within each group, the total weight of right
units equals the total weight of left units (which is 1). For a group with
1 left and k right units, each right unit receives weight 1/k.
}
\examples{
set.seed(42)
left <- data.frame(id = 1:5, age = c(25, 35, 45, 55, 65))
right <- data.frame(id = 6:20, age = runif(15, 20, 70))
result <- full_match(left, right, vars = "age")
print(result)

}
