R Under development (unstable) (2026-10-03 r90638) -- "Unsuffered Consequences" Copyright (C) 2026 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. Natural language support but running in an English locale R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > pkgname <- "couplr" > source(file.path(R.home("share"), "R", "examples-header.R")) > options(warn = 1) > library('couplr') > > base::assign(".oldSearch", base::search(), pos = 'CheckExEnv') > base::assign(".old_wd", base::getwd(), pos = 'CheckExEnv') > cleanEx() > nameEx("as_matchit") > ### * as_matchit > > flush(stderr()); flush(stdout()) > > ### Name: as_matchit > ### Title: Convert couplr Result to matchit Object > ### Aliases: as_matchit > > ### ** Examples > > ## Not run: > ##D left <- data.frame(id = 1:5, age = c(25, 35, 45, 55, 65)) > ##D right <- data.frame(id = 6:15, age = runif(10, 20, 70)) > ##D result <- match_couples(left, right, vars = "age") > ##D mi <- as_matchit(result, left, right) > ##D # Now use with cobalt: > ##D cobalt::bal.tab(mi) > ## End(Not run) > > > > > cleanEx() > nameEx("assignment") > ### * assignment > > flush(stderr()); flush(stdout()) > > ### Name: assignment > ### Title: Linear assignment solver > ### Aliases: assignment > > ### ** Examples > > cost <- matrix(c(4,2,5, 3,3,6, 7,5,4), nrow = 3, byrow = TRUE) > res <- assignment(cost) > res$match; res$total_cost [1] 2 1 3 [1] 9 > > > > > cleanEx() > nameEx("assignment_duals") > ### * assignment_duals > > flush(stderr()); flush(stdout()) > > ### Name: assignment_duals > ### Title: Solve assignment problem and return dual variables > ### Aliases: assignment_duals > > ### ** Examples > > cost <- matrix(c(4, 2, 5, 3, 3, 6, 7, 5, 4), nrow = 3, byrow = TRUE) > result <- assignment_duals(cost) > > # Check optimality: u + v should equal cost for assigned pairs > for (i in 1:3) { + j <- result$match[i] + cat(sprintf("Row %d -> Col %d: u + v = %.2f, cost = %.2f\n", + i, j, result$u[i] + result$v[j], cost[i, j])) + } Row 1 -> Col 2: u + v = 2.00, cost = 2.00 Row 2 -> Col 1: u + v = 3.00, cost = 3.00 Row 3 -> Col 3: u + v = 4.00, cost = 4.00 > > # Verify strong duality > cat("sum(u) + sum(v) =", sum(result$u) + sum(result$v), "\n") sum(u) + sum(v) = 9 > cat("total_cost =", result$total_cost, "\n") total_cost = 9 > > # Reduced costs (how much must cost decrease to enter solution) > reduced <- outer(result$u, result$v, "+") > reduced_cost <- cost - reduced > print(round(reduced_cost, 2)) [,1] [,2] [,3] [1,] 2 0 4 [2,] 0 0 4 [3,] 2 0 0 > > > > > cleanEx() > nameEx("augment.matching_result") > ### * augment.matching_result > > flush(stderr()); flush(stdout()) > > ### Name: augment.matching_result > ### Title: Augment Matching Results with Original Data (broom-style) > ### Aliases: augment.matching_result > > ### ** Examples > > left <- data.frame( + id = 1:5, + treatment = 1, + age = c(25, 30, 35, 40, 45) + ) > > right <- data.frame( + id = 6:10, + treatment = 0, + age = c(24, 29, 36, 41, 44) + ) > > result <- match_couples(left, right, vars = "age") > augment(result, left, right) # A tibble: 5 × 9 pair_id left_id right_id distance .age_diff treatment_left age_left 1 1 1 6 1 1 1 25 2 2 2 7 1 1 1 30 3 3 3 8 1 -1 1 35 4 4 4 9 1 -1 1 40 5 5 5 10 1 1 1 45 # ℹ 2 more variables: treatment_right , age_right > > > > > cleanEx() > nameEx("autoplot.balance_diagnostics") > ### * autoplot.balance_diagnostics > > flush(stderr()); flush(stdout()) > > ### Name: autoplot.balance_diagnostics > ### Title: ggplot2 autoplot for balance diagnostics > ### Aliases: autoplot.balance_diagnostics > > ### ** Examples > > > > > > cleanEx() > nameEx("autoplot.matching_result") > ### * autoplot.matching_result > > flush(stderr()); flush(stdout()) > > ### Name: autoplot.matching_result > ### Title: ggplot2 autoplot for matching results > ### Aliases: autoplot.matching_result > > ### ** Examples > > > > > > cleanEx() > nameEx("balance_diagnostics") > ### * balance_diagnostics > > flush(stderr()); flush(stdout()) > > ### Name: balance_diagnostics > ### Title: Balance Diagnostics for Matched Pairs > ### Aliases: balance_diagnostics balance_diagnostics.matching_result > ### balance_diagnostics.full_matching_result > ### balance_diagnostics.cem_result balance_diagnostics.subclass_result > > ### ** Examples > > # Create sample data > set.seed(123) > left <- data.frame( + id = 1:10, + age = rnorm(10, 45, 10), + income = rnorm(10, 50000, 15000) + ) > right <- data.frame( + id = 11:30, + age = rnorm(20, 47, 10), + income = rnorm(20, 52000, 15000) + ) > > # Match > result <- match_couples(left, right, vars = c("age", "income")) > > # Get balance diagnostics > balance <- balance_diagnostics(result, left, right, vars = c("age", "income")) > print(balance) Balance Diagnostics for Matched Pairs ====================================== Matching Summary: Method: lap Matched pairs: 10 Unmatched left: 0 (of 10) Unmatched right: 10 (of 20) Variable-level Balance: # A tibble: 2 × 7 Variable `Mean Left` `Mean Right` `Mean Diff` `Std Diff` `Var Ratio` `KS Stat` 1 age 45.7 46.1 -0.34 -0.044 2.96 0.3 2 income 53129. 53745. -616. -0.042 1.35 0.2 Overall Balance: Mean |Std Diff|: 0.043 (Excellent) Max |Std Diff|: 0.044 Vars with |Std Diff| > 0.25: 0.0% Balance Interpretation: |Std Diff| < 0.10: Excellent balance |Std Diff| 0.10-0.25: Good balance |Std Diff| 0.25-0.50: Acceptable balance |Std Diff| > 0.50: Poor balance > > # Get balance table > balance_table(balance) # A tibble: 2 × 7 Variable `Mean Left` `Mean Right` `Mean Diff` `Std Diff` `Var Ratio` `KS Stat` 1 age 45.7 46.1 -0.34 -0.044 2.96 0.3 2 income 53129. 53745. -616. -0.042 1.35 0.2 > > > > > cleanEx() > nameEx("bottleneck_assignment") > ### * bottleneck_assignment > > flush(stderr()); flush(stdout()) > > ### Name: bottleneck_assignment > ### Title: Solve the Bottleneck Assignment Problem > ### Aliases: bottleneck_assignment > > ### ** Examples > > # Simple example: minimize max cost > cost <- matrix(c(1, 5, 3, + 2, 4, 6, + 7, 1, 2), nrow = 3, byrow = TRUE) > result <- bottleneck_assignment(cost) > result$bottleneck # Maximum edge cost in optimal assignment [1] 3 > > # Maximize minimum (fair allocation) > profits <- matrix(c(10, 5, 8, + 6, 12, 4, + 3, 7, 11), nrow = 3, byrow = TRUE) > result <- bottleneck_assignment(profits, maximize = TRUE) > result$bottleneck # Minimum profit among all assignments [1] 10 > > # With forbidden assignments > cost <- matrix(c(1, NA, 3, + 2, 4, Inf, + 5, 1, 2), nrow = 3, byrow = TRUE) > result <- bottleneck_assignment(cost) > > > > > cleanEx() > nameEx("cardinality_match") > ### * cardinality_match > > flush(stderr()); flush(stdout()) > > ### Name: cardinality_match > ### Title: Cardinality Matching > ### Aliases: cardinality_match > > ### ** Examples > > set.seed(42) > left <- data.frame(id = 1:20, x = rnorm(20), y = rnorm(20), + region = rep(c("A", "B"), length.out = 20)) > right <- data.frame(id = 21:50, x = rnorm(30, 0.5), y = rnorm(30, 0.3), + region = rep(c("A", "B"), length.out = 30)) > > # Exact fine balance on region, no moment constraint: one flow solve, > # answered with a certificate. > fit <- cardinality_match(left, right, vars = c("x", "y"), + fine = "region") > fit$cardinality Matched units: 20 Best possible: 20 Optimality gap: 0 Certified optimal > > # A standardized-difference bound as well: the same match, searched by > # branch and bound under a small node budget. > bb <- cardinality_match(left, right, vars = c("x", "y"), + fine = "region", max_std_diff = 0.1, + node_limit = 25L) > bb$cardinality Matched units: 19 Global upper bound: 20 Cardinality gap: 1 unit (5.000%) > > > > > cleanEx() > nameEx("cem_match") > ### * cem_match > > flush(stderr()); flush(stdout()) > > ### Name: cem_match > ### Title: Coarsened Exact Matching > ### Aliases: cem_match > > ### ** Examples > > set.seed(42) > left <- data.frame( + id = 1:20, age = rnorm(20, 40, 10), + income = rnorm(20, 50000, 10000) + ) > right <- data.frame( + id = 21:60, age = rnorm(40, 42, 10), + income = rnorm(40, 52000, 10000) + ) > result <- cem_match(left, right, vars = c("age", "income")) > print(result) Coarsened Exact Matching Result =============================== Strata: 18 total, 7 matched Left: 13 matched, 7 pruned Right: 29 matched, 11 pruned Variables: age, income > > > > > cleanEx() > nameEx("compute_distances") > ### * compute_distances > > flush(stderr()); flush(stdout()) > > ### Name: compute_distances > ### Title: Compute and Cache Distance Matrix for Reuse > ### Aliases: compute_distances > > ### ** Examples > > # Compute distances once > left <- data.frame(id = 1:5, age = c(25, 30, 35, 40, 45), income = c(45, 52, 48, 61, 55) * 1000) > right <- data.frame(id = 6:10, age = c(24, 29, 36, 41, 44), income = c(46, 51, 47, 60, 54) * 1000) > > dist_obj <- compute_distances( + left, right, + vars = c("age", "income"), + scale = "standardize" + ) > > # Reuse for different matching strategies > result1 <- match_couples(dist_obj, max_distance = 0.5) Warning: 80.0% of pairs are forbidden! Only 5 valid pairs for 5 left units - the matching pool is shallow! Your constraints might be concerningly strict. Consider: - Relaxing max_distance threshold - Widening calipers - Using fewer/broader blocks - Checking if your data actually overlaps > result2 <- match_couples(dist_obj, max_distance = 1.0) Warning: 76.0% of pairs are forbidden! Only 6 valid pairs for 5 left units - the matching pool is shallow! Your constraints might be concerningly strict. Consider: - Relaxing max_distance threshold - Widening calipers - Using fewer/broader blocks - Checking if your data actually overlaps > result3 <- match_couples(dist_obj, method = "greedy", strategy = "sorted") > > # All use the same precomputed distances > > > > > cleanEx() > nameEx("estimate_dense_matrix_mb") > ### * estimate_dense_matrix_mb > > flush(stderr()); flush(stdout()) > > ### Name: estimate_dense_matrix_mb > ### Title: Estimate dense cost-matrix memory footprint in megabytes > ### Aliases: estimate_dense_matrix_mb > > ### ** Examples > > estimate_dense_matrix_mb(5000, 5000) [1] 800 > > > > cleanEx() > nameEx("estimate_dense_solve_mb") > ### * estimate_dense_solve_mb > > flush(stderr()); flush(stdout()) > > ### Name: estimate_dense_solve_mb > ### Title: Estimate the peak footprint of a dense solve in megabytes > ### Aliases: estimate_dense_solve_mb > > ### ** Examples > > estimate_dense_solve_mb(5000, 5000) [1] 2400 > > > > cleanEx() > nameEx("example_costs") > ### * example_costs > > flush(stderr()); flush(stdout()) > > ### Name: example_costs > ### Title: Example cost matrices for assignment problems > ### Aliases: example_costs > > ### ** Examples > > # Simple 3x3 assignment > result <- lap_solve(example_costs$simple_3x3) > print(result) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 2 2 2 2 1 3 3 3 3 4 Total cost: 9 Method: bruteforce > # Optimal: sources 1,2,3 -> targets 2,1,3 with cost 9 > > # Rectangular problem (3 sources, 5 targets) > result <- lap_solve(example_costs$rectangular_3x5) > print(result) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 1 1 2 2 5 2 3 3 2 3 Total cost: 6 Method: bruteforce > # All 3 sources assigned; 2 targets unassigned > > # Sparse problem with forbidden assignments > result <- lap_solve(example_costs$sparse_with_na) > print(result) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 2 2 2 2 1 3 3 3 3 4 Total cost: 9 Method: bruteforce > # Avoids NA positions > > # Binary costs - test HK01 algorithm > result <- lap_solve(example_costs$binary_costs, method = "hk01") > print(result) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 1 0 2 2 2 0 3 3 3 0 Total cost: 0 Method: hk01 > # Finds diagonal assignment (cost = 0) > > > > > cleanEx() > nameEx("example_df") > ### * example_df > > flush(stderr()); flush(stdout()) > > ### Name: example_df > ### Title: Example assignment problem data frame > ### Aliases: example_df > > ### ** Examples > > library(dplyr) Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union > > # Solve both problems with grouped workflow > example_df |> + group_by(sim) |> + lap_solve(source, target, cost) # A tibble: 6 × 4 sim source target cost 1 1 1 2 3 2 1 2 1 2 3 1 3 3 4 4 2 1 1 1 5 2 2 2 3 6 2 3 3 1 > > # Batch solving for efficiency > example_df |> + group_by(sim) |> + lap_solve_batch(source, target, cost) Batch Assignment Results ======================== # A tibble: 6 × 6 sim source target cost total_cost method_used 1 1 1 2 3 9 bruteforce 2 1 2 1 2 9 bruteforce 3 1 3 3 4 9 bruteforce 4 2 1 1 1 5 bruteforce 5 2 2 2 3 5 bruteforce 6 2 3 3 1 5 bruteforce > > # Inspect the data structure > example_df |> + group_by(sim) |> + summarise( + n_pairs = n(), + min_cost = min(cost), + max_cost = max(cost) + ) # A tibble: 2 × 4 sim n_pairs min_cost max_cost 1 1 9 2 7 2 2 9 1 5 > > > > > cleanEx() detaching ‘package:dplyr’ > nameEx("explain_dispatch") > ### * explain_dispatch > > flush(stderr()); flush(stdout()) > > ### Name: explain_dispatch > ### Title: Explain which solver 'method = "auto"' selects, and why > ### Aliases: explain_dispatch print.dispatch_explanation > > ### ** Examples > > explain_dispatch(matrix(runif(400), 20, 20)) Solver dispatch =============== Problem: 20 x 20 Selected: jv Rule: default -- no earlier rule applies Why: fastest general-purpose solver measured across the regime grid Rules tested, in order: [ ] tiny bruteforce at most 8 rows and 8 columns [ ] no_cost_scale hk01 finite entries all equal, or all either 0 or 1 [x] default jv no earlier rule applies Probe: 0 of 400 entries non-finite > explain_dispatch(matrix(sample(0:1, 400, TRUE), 20, 20)) Solver dispatch =============== Problem: 20 x 20 Selected: hk01 Rule: no_cost_scale -- finite entries all equal, or all either 0 or 1 Why: there is no cost scale to exploit, so a cardinality algorithm suffices Rules tested, in order: [ ] tiny bruteforce at most 8 rows and 8 columns [x] no_cost_scale hk01 finite entries all equal, or all either 0 or 1 Probe: 0 of 400 entries non-finite, binary > > > > > cleanEx() > nameEx("full_match") > ### * full_match > > flush(stderr()); flush(stdout()) > > ### Name: full_match > ### Title: Full Matching > ### Aliases: full_match > > ### ** 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) Full Matching Result ==================== Status: optimal Groups formed: 5 Left units: 5 matched, 0 unmatched (of 5) Right units: 15 matched, 0 unmatched (of 15) Right units per group: min=1, median=3, max=5 > > > > > cleanEx() > nameEx("hospital_staff") > ### * hospital_staff > > flush(stderr()); flush(stdout()) > > ### Name: hospital_staff > ### Title: Hospital staff scheduling example dataset > ### Aliases: hospital_staff > ### Keywords: datasets > > ### ** Examples > > # Basic assignment: assign nurses to shifts minimizing cost > lap_solve(hospital_staff$basic_costs) Assignment Result ================= # A tibble: 10 × 3 source target cost 1 1 8 2 2 2 2 2 3 3 7 2 4 4 4 2 5 5 9 2 6 6 10 3 7 7 1 2 8 8 3 2 9 9 6 3 10 10 5 2 Total cost: 22 Method: jv > > # Maximize preferences instead > lap_solve(hospital_staff$preferences, maximize = TRUE) Assignment Result ================= # A tibble: 10 × 3 source target cost 1 1 8 9 2 2 2 9 3 3 7 9 4 4 4 9 5 5 9 9 6 6 10 8 7 7 1 9 8 8 3 9 9 9 6 8 10 10 5 9 Total cost: 88 Method: jv > > # Data frame workflow > library(dplyr) Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union > hospital_staff$schedule_df |> + lap_solve(nurse_id, shift_id, cost) Assignment Result ================= # A tibble: 10 × 3 source target cost 1 1 8 2 2 2 2 2 3 3 7 2 4 4 4 2 5 5 9 2 6 6 10 3 7 7 1 2 8 8 3 2 9 9 6 3 10 10 5 2 Total cost: 22 Method: jv > > # Batch solve weekly schedule > hospital_staff$weekly_df |> + group_by(day) |> + lap_solve(nurse_id, shift_id, cost) # A tibble: 50 × 4 day source target cost 1 Monday 1 1 2 2 Monday 2 9 2 3 Monday 3 3 1 4 Monday 4 4 0 5 Monday 5 5 2 6 Monday 6 6 1 7 Monday 7 8 4 8 Monday 8 10 1 9 Monday 9 2 4 10 Monday 10 7 1 # ℹ 40 more rows > > # Matching workflow: match full-time to part-time nurses > match_couples( + left = hospital_staff$nurses_extended, + right = hospital_staff$controls_extended, + vars = c("age", "experience_years", "certification_level"), + auto_scale = TRUE + ) Auto-selected scaling method: standardize Warning: No id column found in left, so ids left_1 ... left_200 were used. Downstream verbs (join_matched(), match_data(), balance_diagnostics()) join on these values, so pass left_id = "" to key the matching on your own identifier. Warning: No id column found in right, so ids right_1 ... right_300 were used. Downstream verbs (join_matched(), match_data(), balance_diagnostics()) join on these values, so pass right_id = "" to key the matching on your own identifier. Matching Result =============== Method: lap Pairs matched: 200 Unmatched (left): 0 Unmatched (right): 100 Total distance: 59.9496 Status: optimal Matched pairs: # A tibble: 200 × 6 left_id right_id distance .age_diff .experience_years_diff 1 left_1 right_60 0.217 -1 1 2 left_2 right_94 0.175 2 0 3 left_3 right_12 0 0 0 4 left_4 right_244 0.591 -5 2 5 left_5 right_104 0.481 -5 -1 6 left_6 right_73 0 0 0 7 left_7 right_296 0.199 0 1 8 left_8 right_92 0.262 3 0 9 left_9 right_42 0.622 -2 3 10 left_10 right_247 0 0 0 # ℹ 190 more rows # ℹ 1 more variable: .certification_level_diff > > > > > cleanEx() detaching ‘package:dplyr’ > nameEx("is_distance_object") > ### * is_distance_object > > flush(stderr()); flush(stdout()) > > ### Name: is_distance_object > ### Title: Check if Object is a Distance Object > ### Aliases: is_distance_object > > ### ** Examples > > left <- data.frame(id = 1:3, x = c(1, 2, 3)) > right <- data.frame(id = 4:6, x = c(1.1, 2.1, 3.1)) > dist_obj <- compute_distances(left, right, vars = "x") > is_distance_object(dist_obj) # TRUE [1] TRUE > is_distance_object(list()) # FALSE [1] FALSE > > > > > cleanEx() > nameEx("join_matched") > ### * join_matched > > flush(stderr()); flush(stdout()) > > ### Name: join_matched > ### Title: Join Matched Pairs with Original Data > ### Aliases: join_matched join_matched.matching_result > ### join_matched.full_matching_result join_matched.cem_result > ### join_matched.subclass_result > > ### ** Examples > > # Basic usage > left <- data.frame( + id = 1:5, + treatment = 1, + age = c(25, 30, 35, 40, 45), + income = c(45000, 52000, 48000, 61000, 55000) + ) > > right <- data.frame( + id = 6:10, + treatment = 0, + age = c(24, 29, 36, 41, 44), + income = c(46000, 51500, 47500, 60000, 54000) + ) > > result <- match_couples(left, right, vars = c("age", "income")) > matched_data <- join_matched(result, left, right) > head(matched_data) # A tibble: 5 × 12 pair_id left_id right_id distance .age_diff .income_diff treatment_left 1 1 1 6 1000. 1 -1000 1 2 2 2 7 500. 1 500 1 3 3 3 8 500. -1 500 1 4 4 4 9 1000. -1 1000 1 5 5 5 10 1000. 1 1000 1 # ℹ 5 more variables: age_left , income_left , treatment_right , # age_right , income_right > > # Specify which variables to include > matched_data <- join_matched( + result, left, right, + left_vars = c("treatment", "age", "income"), + right_vars = c("age", "income"), + suffix = c("_treated", "_control") + ) > > # Without distance or pair_id > matched_data <- join_matched( + result, left, right, + include_distance = FALSE, + include_pair_id = FALSE + ) > > > > > cleanEx() > nameEx("lap_animate") > ### * lap_animate > > flush(stderr()); flush(stdout()) > > ### Name: lap_animate > ### Title: Animate an assignment algorithm step-by-step > ### Aliases: lap_animate > > ### ** Examples > > ## Not run: > ##D cost <- matrix(c(4, 2, 5, > ##D 3, 3, 6, > ##D 7, 5, 4), nrow = 3, byrow = TRUE) > ##D lap_animate(cost, method = "hungarian") > ## End(Not run) > > > > > cleanEx() > nameEx("lap_solve") > ### * lap_solve > > flush(stderr()); flush(stdout()) > > ### Name: lap_solve > ### Title: Solve linear assignment problems > ### Aliases: lap_solve > > ### ** Examples > > # Matrix input > cost <- matrix(c(4, 2, 5, 3, 3, 6, 7, 5, 4), nrow = 3) > lap_solve(cost) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 2 3 2 2 1 2 3 3 3 4 Total cost: 9 Method: bruteforce > > # Data frame input > library(dplyr) Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union > df <- tibble( + source = rep(1:3, each = 3), + target = rep(1:3, times = 3), + cost = c(4, 2, 5, 3, 3, 6, 7, 5, 4) + ) > lap_solve(df, source, target, cost) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 2 2 2 2 1 3 3 3 3 4 Total cost: 9 Method: bruteforce > > # With NA masking (forbidden assignments) > cost[1, 3] <- NA > lap_solve(cost) Assignment Result ================= # A tibble: 3 × 3 source target cost 1 1 2 3 2 2 1 2 3 3 3 4 Total cost: 9 Method: bruteforce > > # Grouped data frames > df <- tibble( + sim = rep(1:2, each = 9), + source = rep(1:3, times = 6), + target = rep(1:3, each = 3, times = 2), + cost = runif(18, 1, 10) + ) > df |> group_by(sim) |> lap_solve(source, target, cost) # A tibble: 6 × 4 sim source target cost 1 1 1 1 3.39 2 1 2 2 2.82 3 1 3 3 6.66 4 2 1 3 5.48 5 2 2 2 4.46 6 2 3 1 2.59 > > > > > cleanEx() detaching ‘package:dplyr’ > nameEx("lap_solve_batch") > ### * lap_solve_batch > > flush(stderr()); flush(stdout()) > > ### Name: lap_solve_batch > ### Title: Solve multiple assignment problems efficiently > ### Aliases: lap_solve_batch > > ### ** Examples > > # List of matrices > costs <- list( + matrix(c(1, 2, 3, 4), 2, 2), + matrix(c(5, 6, 7, 8), 2, 2) + ) > lap_solve_batch(costs) Batch Assignment Results ======================== Number of problems solved: 2 Total cost range: [5.00, 13.00] # A tibble: 4 × 6 problem_id source target cost total_cost method_used 1 1 1 1 1 5 bruteforce 2 1 2 2 4 5 bruteforce 3 2 1 1 5 13 bruteforce 4 2 2 2 8 13 bruteforce > > # 3D array > arr <- array(runif(2 * 2 * 10), dim = c(2, 2, 10)) > lap_solve_batch(arr) Batch Assignment Results ======================== Number of problems solved: 10 Total cost range: [0.27, 1.37] # A tibble: 20 × 6 problem_id source target cost total_cost method_used 1 1 1 2 0.573 0.945 bruteforce 2 1 2 1 0.372 0.945 bruteforce 3 2 1 1 0.202 0.862 bruteforce 4 2 2 2 0.661 0.862 bruteforce 5 3 1 2 0.206 0.268 bruteforce 6 3 2 1 0.0618 0.268 bruteforce 7 4 1 2 0.770 1.15 bruteforce 8 4 2 1 0.384 1.15 bruteforce 9 5 1 2 0.380 1.37 bruteforce 10 5 2 1 0.992 1.37 bruteforce 11 6 1 2 0.652 0.864 bruteforce 12 6 2 1 0.212 0.864 bruteforce 13 7 1 2 0.0134 0.400 bruteforce 14 7 2 1 0.386 0.400 bruteforce 15 8 1 2 0.482 0.822 bruteforce 16 8 2 1 0.340 0.822 bruteforce 17 9 1 2 0.827 1.01 bruteforce 18 9 2 1 0.186 1.01 bruteforce 19 10 1 2 0.724 0.832 bruteforce 20 10 2 1 0.108 0.832 bruteforce > > # Grouped data frame > library(dplyr) Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union > df <- tibble( + sim = rep(1:5, each = 9), + source = rep(1:3, times = 15), + target = rep(1:3, each = 3, times = 5), + cost = runif(45, 1, 10) + ) > df |> group_by(sim) |> lap_solve_batch(source, target, cost) Batch Assignment Results ======================== # A tibble: 15 × 6 sim source target cost total_cost method_used 1 1 1 3 1.21 15.0 bruteforce 2 1 2 2 5.77 15.0 bruteforce 3 1 3 1 8.05 15.0 bruteforce 4 2 1 3 1.90 8.83 bruteforce 5 2 2 1 5.30 8.83 bruteforce 6 2 3 2 1.64 8.83 bruteforce 7 3 1 2 3.64 13.6 bruteforce 8 3 2 1 4.66 13.6 bruteforce 9 3 3 3 5.31 13.6 bruteforce 10 4 1 3 4.00 9.88 bruteforce 11 4 2 1 1.76 9.88 bruteforce 12 4 3 2 4.12 9.88 bruteforce 13 5 1 3 4.60 16.5 bruteforce 14 5 2 1 4.51 16.5 bruteforce 15 5 3 2 7.41 16.5 bruteforce > > # Parallel execution (requires n_threads > 1) > lap_solve_batch(costs, n_threads = 2) Batch Assignment Results ======================== Number of problems solved: 2 Total cost range: [5.00, 13.00] # A tibble: 4 × 6 problem_id source target cost total_cost method_used 1 1 1 1 1 5 bruteforce 2 1 2 2 4 5 bruteforce 3 2 1 1 5 13 bruteforce 4 2 2 2 8 13 bruteforce > > > > > cleanEx() detaching ‘package:dplyr’ > nameEx("lap_solve_kbest") > ### * lap_solve_kbest > > flush(stderr()); flush(stdout()) > > ### Name: lap_solve_kbest > ### Title: Find k-best optimal assignments > ### Aliases: lap_solve_kbest > > ### ** Examples > > # Matrix input - find 5 best solutions > cost <- matrix(c(4, 2, 5, 3, 3, 6, 7, 5, 4), nrow = 3) > lap_solve_kbest(cost, k = 5) K-Best Assignment Results ========================= Number of solutions: 5 Solution costs: Rank 1: 9.0000 Rank 2: 11.0000 Rank 3: 13.0000 Rank 4: 15.0000 Rank 5: 15.0000 Assignments: # A tibble: 15 × 6 rank solution_id source target cost total_cost 1 1 1 1 2 3 9 2 1 1 2 1 2 9 3 1 1 3 3 4 9 4 2 2 1 1 4 11 5 2 2 2 2 3 11 6 2 2 3 3 4 11 7 3 3 1 2 3 13 8 3 3 2 3 5 13 9 3 3 3 1 5 13 10 4 4 1 3 7 15 11 4 4 2 1 2 15 12 4 4 3 2 6 15 13 5 5 1 1 4 15 14 5 5 2 3 5 15 15 5 5 3 2 6 15 > > # Data frame input > library(dplyr) Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union > df <- tibble( + source = rep(1:3, each = 3), + target = rep(1:3, times = 3), + cost = c(4, 2, 5, 3, 3, 6, 7, 5, 4) + ) > lap_solve_kbest(df, k = 3, source, target, cost) K-Best Assignment Results ========================= Number of solutions: 3 Solution costs: Rank 1: 9.0000 Rank 2: 11.0000 Rank 3: 13.0000 Assignments: # A tibble: 9 × 6 rank solution_id source target cost total_cost 1 1 1 1 2 2 9 2 1 1 2 1 3 9 3 1 1 3 3 4 9 4 2 2 1 1 4 11 5 2 2 2 2 3 11 6 2 2 3 3 4 11 7 3 3 1 3 5 13 8 3 3 2 1 3 13 9 3 3 3 2 5 13 > > # With maximization > lap_solve_kbest(cost, k = 3, maximize = TRUE) K-Best Assignment Results ========================= Number of solutions: 3 Solution costs: Rank 1: 15.0000 Rank 2: 15.0000 Rank 3: 15.0000 Assignments: # A tibble: 9 × 6 rank solution_id source target cost total_cost 1 1 1 1 3 7 15 2 1 1 2 1 2 15 3 1 1 3 2 6 15 4 2 2 1 1 4 15 5 2 2 2 3 5 15 6 2 2 3 2 6 15 7 3 3 1 3 7 15 8 3 3 2 2 3 15 9 3 3 3 1 5 15 > > > > > cleanEx() detaching ‘package:dplyr’ > nameEx("lap_solve_line_metric") > ### * lap_solve_line_metric > > flush(stderr()); flush(stdout()) > > ### Name: lap_solve_line_metric > ### Title: Solve 1-D Line Assignment Problem > ### Aliases: lap_solve_line_metric > > ### ** Examples > > # Square case: equal number of sources and targets > x <- c(1.5, 3.2, 5.1) > y <- c(2.0, 3.0, 5.5) > result <- lap_solve_line_metric(x, y, cost = "L1") > print(result) 1-D Line Assignment Result =========================== Assignments (1-based): Source 1 -> Target 1 Source 2 -> Target 2 Source 3 -> Target 3 Total cost: 1.1 > > # Rectangular case: more targets than sources > x <- c(1.0, 3.0, 5.0) > y <- c(0.5, 2.0, 3.5, 4.5, 6.0) > result <- lap_solve_line_metric(x, y, cost = "L2") > print(result) 1-D Line Assignment Result =========================== Assignments (1-based): Source 1 -> Target 1 Source 2 -> Target 3 Source 3 -> Target 4 Total cost: 0.75 > > # With unsorted inputs (will be sorted internally) > x <- c(5.0, 1.0, 3.0) > y <- c(4.5, 0.5, 6.0, 2.0, 3.5) > result <- lap_solve_line_metric(x, y, cost = "L1") > print(result) 1-D Line Assignment Result =========================== Assignments (1-based): Source 1 -> Target 1 Source 2 -> Target 2 Source 3 -> Target 5 Total cost: 1.5 > > > > > cleanEx() > nameEx("match_couples") > ### * match_couples > > flush(stderr()); flush(stdout()) > > ### Name: match_couples > ### Title: Match two datasets into couples > ### Aliases: match_couples > > ### ** Examples > > # Basic matching > left <- data.frame(id = 1:5, x = c(1, 2, 3, 4, 5), y = c(2, 4, 6, 8, 10)) > right <- data.frame(id = 6:10, x = c(1.1, 2.2, 3.1, 4.2, 5.1), y = c(2.1, 4.1, 6.2, 8.1, 10.1)) > result <- match_couples(left, right, vars = c("x", "y")) > print(result$pairs) # A tibble: 5 × 5 left_id right_id distance .x_diff .y_diff 1 1 6 0.141 -0.100 -0.100 2 2 7 0.224 -0.200 -0.1000 3 3 8 0.224 -0.100 -0.200 4 4 9 0.224 -0.200 -0.1000 5 5 10 0.141 -0.1000 -0.1000 > > # With constraints > result <- match_couples(left, right, vars = c("x", "y"), + max_distance = 1, + calipers = list(x = 0.5)) Warning: 80.0% of pairs are forbidden! Only 5 valid pairs for 5 left units - the matching pool is shallow! Your constraints might be concerningly strict. Consider: - Relaxing max_distance threshold - Widening calipers - Using fewer/broader blocks - Checking if your data actually overlaps > > # With blocking > left$region <- c("A", "A", "B", "B", "B") > right$region <- c("A", "A", "B", "B", "B") > blocks <- matchmaker(left, right, block_type = "group", block_by = "region") > result <- match_couples(blocks$left, blocks$right, vars = c("x", "y")) > > # Fast greedy matching for large datasets > result <- match_couples(left, right, vars = c("x", "y"), + method = "greedy", strategy = "sorted") > > > > > cleanEx() > nameEx("match_data") > ### * match_data > > flush(stderr()); flush(stdout()) > > ### Name: match_data > ### Title: Extract Analysis-Ready Data from Matching Results > ### Aliases: match_data match_data.matching_result > ### match_data.full_matching_result match_data.cem_result > ### match_data.subclass_result > > ### ** Examples > > set.seed(42) > left <- data.frame(id = 1:5, age = c(25, 35, 45, 55, 65)) > right <- data.frame(id = 6:15, age = runif(10, 20, 70)) > result <- match_couples(left, right, vars = "age") > md <- match_data(result, left, right) > head(md) # A tibble: 6 × 6 id age treatment weights subclass distance 1 1 25 1 1 1 1.73 2 2 35 1 1 2 0.693 3 3 45 1 1 3 0.955 4 4 55 1 1 4 0.253 5 5 65 1 1 5 0.740 6 13 26.7 0 1 1 1.73 > > > > > cleanEx() > nameEx("match_path") > ### * match_path > > flush(stderr()); flush(stdout()) > > ### Name: match_path > ### Title: Match across a range of one design choice > ### Aliases: match_path > > ### ** Examples > > set.seed(1) > left <- data.frame(id = 1:20, x = rnorm(20), y = rnorm(20)) > right <- data.frame(id = 1:60, x = rnorm(60), y = rnorm(60)) > path <- match_path(left, right, vars = c("x", "y"), + vary = "max_distance", values = c(0.5, 1, 2, Inf)) /usr/include/c++/16/bits/stl_vector.h:1254:34: runtime error: reference binding to null pointer of type 'value_type' #0 0x7b938e16dfd9 in std::vector >::operator[](unsigned long) /usr/include/c++/16/bits/stl_vector.h:1254 #1 0x7b938e16dfd9 in lap::build_ball_tree(lap::LazyCostMatrix const&, int) flow/flow_balltree.h:577 #2 0x7b938e16dfd9 in lap::RowSearch::RowSearch(lap::LazyCostMatrix const&) flow/flow_row_search.h:98 #3 0x7b938e4096ee in solve_path >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool):::: > flow/flow_path.h:150 #4 0x7b938e4096ee in operator() flow/flow_path_rcpp.cpp:166 #5 0x7b938e4096ee in __invoke_impl, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:63 #6 0x7b938e4096ee in __invoke >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:99 #7 0x7b938e4096ee in __visit_invoke /usr/include/c++/16/variant:1073 #8 0x7b938e4096ee in __do_visit >, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1919 #9 0x7b938e4096ee in visit >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1984 #10 0x7b938e4096ee in match_path_lazy_impl(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) flow/flow_path_rcpp.cpp:174 #11 0x7b938deee604 in lap_match_path_lazy(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/rcpp_interface.cpp:654 #12 0x7b938debf698 in _couplr_lap_match_path_lazy /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/RcppExports.cpp:862 #13 0x00000075d373 in R_doDotCall /data/localhost/ripley/R/svn/R-devel/src/main/dotcode.c:801 #14 0x000000901dd7 in bcEval_loop /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:8704 #15 0x0000008d918b in bcEval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:7533 #16 0x0000008770ba in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1167 #17 0x0000008904aa in R_execClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2398 #18 0x0000008941ad in applyClosure_core /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2314 #19 0x000000877768 in Rf_applyClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2333 #20 0x000000877768 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1278 #21 0x0000008acaee in do_set /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:3585 #22 0x000000877b96 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1230 #23 0x000000a224b8 in Rf_ReplIteration /data/localhost/ripley/R/svn/R-devel/src/main/main.c:264 #24 0x000000a224b8 in R_ReplConsole /data/localhost/ripley/R/svn/R-devel/src/main/main.c:319 #25 0x000000a4017a in run_Rmainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1239 #26 0x000000a40212 in Rf_mainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1246 #27 0x00000041234f in main /data/localhost/ripley/R/svn/R-devel/src/main/Rmain.c:29 #28 0x7f939dc0a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #29 0x7f939dc0a797 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3797) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #30 0x000000412d14 in _start (/data/localhost/ripley/R/gcc-SAN3/bin/exec/R+0x412d14) (BuildId: 305ee1ce3f251d1f19959992ff84ce2bcc93ef61) /usr/include/c++/16/bits/stl_vector.h:1254:34: runtime error: reference binding to null pointer of type 'value_type' #0 0x7b938e16def8 in std::vector >::operator[](unsigned long) /usr/include/c++/16/bits/stl_vector.h:1254 #1 0x7b938e16def8 in lap::build_ball_tree(lap::LazyCostMatrix const&, int) flow/flow_balltree.h:578 #2 0x7b938e16def8 in lap::RowSearch::RowSearch(lap::LazyCostMatrix const&) flow/flow_row_search.h:98 #3 0x7b938e4096ee in solve_path >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool):::: > flow/flow_path.h:150 #4 0x7b938e4096ee in operator() flow/flow_path_rcpp.cpp:166 #5 0x7b938e4096ee in __invoke_impl, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:63 #6 0x7b938e4096ee in __invoke >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:99 #7 0x7b938e4096ee in __visit_invoke /usr/include/c++/16/variant:1073 #8 0x7b938e4096ee in __do_visit >, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1919 #9 0x7b938e4096ee in visit >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1984 #10 0x7b938e4096ee in match_path_lazy_impl(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) flow/flow_path_rcpp.cpp:174 #11 0x7b938deee604 in lap_match_path_lazy(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/rcpp_interface.cpp:654 #12 0x7b938debf698 in _couplr_lap_match_path_lazy /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/RcppExports.cpp:862 #13 0x00000075d373 in R_doDotCall /data/localhost/ripley/R/svn/R-devel/src/main/dotcode.c:801 #14 0x000000901dd7 in bcEval_loop /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:8704 #15 0x0000008d918b in bcEval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:7533 #16 0x0000008770ba in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1167 #17 0x0000008904aa in R_execClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2398 #18 0x0000008941ad in applyClosure_core /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2314 #19 0x000000877768 in Rf_applyClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2333 #20 0x000000877768 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1278 #21 0x0000008acaee in do_set /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:3585 #22 0x000000877b96 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1230 #23 0x000000a224b8 in Rf_ReplIteration /data/localhost/ripley/R/svn/R-devel/src/main/main.c:264 #24 0x000000a224b8 in R_ReplConsole /data/localhost/ripley/R/svn/R-devel/src/main/main.c:319 #25 0x000000a4017a in run_Rmainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1239 #26 0x000000a40212 in Rf_mainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1246 #27 0x00000041234f in main /data/localhost/ripley/R/svn/R-devel/src/main/Rmain.c:29 #28 0x7f939dc0a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #29 0x7f939dc0a797 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3797) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #30 0x000000412d14 in _start (/data/localhost/ripley/R/gcc-SAN3/bin/exec/R+0x412d14) (BuildId: 305ee1ce3f251d1f19959992ff84ce2bcc93ef61) /usr/include/c++/16/bits/stl_vector.h:1254:34: runtime error: reference binding to null pointer of type 'value_type' #0 0x7b938e16d899 in std::vector >::operator[](unsigned long) /usr/include/c++/16/bits/stl_vector.h:1254 #1 0x7b938e16d899 in lap::build_ball_tree(lap::LazyCostMatrix const&, int) flow/flow_balltree.h:577 #2 0x7b938e16d899 in lap::RowSearch::RowSearch(lap::LazyCostMatrix const&) flow/flow_row_search.h:98 #3 0x7b938e4096ee in solve_path >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool):::: > flow/flow_path.h:150 #4 0x7b938e4096ee in operator() flow/flow_path_rcpp.cpp:166 #5 0x7b938e4096ee in __invoke_impl, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:63 #6 0x7b938e4096ee in __invoke >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:99 #7 0x7b938e4096ee in __visit_invoke /usr/include/c++/16/variant:1073 #8 0x7b938e4096ee in __do_visit >, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1919 #9 0x7b938e4096ee in visit >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1984 #10 0x7b938e4096ee in match_path_lazy_impl(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) flow/flow_path_rcpp.cpp:174 #11 0x7b938deee604 in lap_match_path_lazy(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/rcpp_interface.cpp:654 #12 0x7b938debf698 in _couplr_lap_match_path_lazy /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/RcppExports.cpp:862 #13 0x00000075d373 in R_doDotCall /data/localhost/ripley/R/svn/R-devel/src/main/dotcode.c:801 #14 0x000000901dd7 in bcEval_loop /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:8704 #15 0x0000008d918b in bcEval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:7533 #16 0x0000008770ba in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1167 #17 0x0000008904aa in R_execClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2398 #18 0x0000008941ad in applyClosure_core /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2314 #19 0x000000877768 in Rf_applyClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2333 #20 0x000000877768 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1278 #21 0x0000008acaee in do_set /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:3585 #22 0x000000877b96 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1230 #23 0x000000a224b8 in Rf_ReplIteration /data/localhost/ripley/R/svn/R-devel/src/main/main.c:264 #24 0x000000a224b8 in R_ReplConsole /data/localhost/ripley/R/svn/R-devel/src/main/main.c:319 #25 0x000000a4017a in run_Rmainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1239 #26 0x000000a40212 in Rf_mainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1246 #27 0x00000041234f in main /data/localhost/ripley/R/svn/R-devel/src/main/Rmain.c:29 #28 0x7f939dc0a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #29 0x7f939dc0a797 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3797) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #30 0x000000412d14 in _start (/data/localhost/ripley/R/gcc-SAN3/bin/exec/R+0x412d14) (BuildId: 305ee1ce3f251d1f19959992ff84ce2bcc93ef61) /usr/include/c++/16/bits/stl_vector.h:1254:34: runtime error: reference binding to null pointer of type 'value_type' #0 0x7b938e16d97c in std::vector >::operator[](unsigned long) /usr/include/c++/16/bits/stl_vector.h:1254 #1 0x7b938e16d97c in lap::build_ball_tree(lap::LazyCostMatrix const&, int) flow/flow_balltree.h:578 #2 0x7b938e16d97c in lap::RowSearch::RowSearch(lap::LazyCostMatrix const&) flow/flow_row_search.h:98 #3 0x7b938e4096ee in solve_path >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool):::: > flow/flow_path.h:150 #4 0x7b938e4096ee in operator() flow/flow_path_rcpp.cpp:166 #5 0x7b938e4096ee in __invoke_impl, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:63 #6 0x7b938e4096ee in __invoke >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, lap::LazyCostMatrix&> /usr/include/c++/16/bits/invoke.h:99 #7 0x7b938e4096ee in __visit_invoke /usr/include/c++/16/variant:1073 #8 0x7b938e4096ee in __do_visit >, match_path_lazy_impl(Rcpp::NumericMatrix, Rcpp::NumericMatrix, SEXP, Rcpp::Nullable >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1919 #9 0x7b938e4096ee in visit >, Rcpp::NumericVector, Rcpp::List, bool, double, double, double, double, bool)::, std::variant&> /usr/include/c++/16/variant:1984 #10 0x7b938e4096ee in match_path_lazy_impl(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) flow/flow_path_rcpp.cpp:174 #11 0x7b938deee604 in lap_match_path_lazy(Rcpp::Matrix<14, Rcpp::PreserveStorage>, Rcpp::Matrix<14, Rcpp::PreserveStorage>, SEXPREC*, Rcpp::Nullable >, Rcpp::Vector<14, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage>, bool, double, double, double, double, bool) /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/rcpp_interface.cpp:654 #12 0x7b938debf698 in _couplr_lap_match_path_lazy /data/localhost/ripley/R/packages/tests-gcc-SAN/couplr/src/RcppExports.cpp:862 #13 0x00000075d373 in R_doDotCall /data/localhost/ripley/R/svn/R-devel/src/main/dotcode.c:801 #14 0x000000901dd7 in bcEval_loop /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:8704 #15 0x0000008d918b in bcEval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:7533 #16 0x0000008770ba in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1167 #17 0x0000008904aa in R_execClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2398 #18 0x0000008941ad in applyClosure_core /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2314 #19 0x000000877768 in Rf_applyClosure /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:2333 #20 0x000000877768 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1278 #21 0x0000008acaee in do_set /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:3585 #22 0x000000877b96 in Rf_eval /data/localhost/ripley/R/svn/R-devel/src/main/eval.c:1230 #23 0x000000a224b8 in Rf_ReplIteration /data/localhost/ripley/R/svn/R-devel/src/main/main.c:264 #24 0x000000a224b8 in R_ReplConsole /data/localhost/ripley/R/svn/R-devel/src/main/main.c:319 #25 0x000000a4017a in run_Rmainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1239 #26 0x000000a40212 in Rf_mainloop /data/localhost/ripley/R/svn/R-devel/src/main/main.c:1246 #27 0x00000041234f in main /data/localhost/ripley/R/svn/R-devel/src/main/Rmain.c:29 #28 0x7f939dc0a680 in __libc_start_call_main (/lib64/libc.so.6+0x3680) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #29 0x7f939dc0a797 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3797) (BuildId: 85075e2ae2e13352a1413d41c327dacf6461526c) #30 0x000000412d14 in _start (/data/localhost/ripley/R/gcc-SAN3/bin/exec/R+0x412d14) (BuildId: 305ee1ce3f251d1f19959992ff84ce2bcc93ef61) > path$path # A tibble: 4 × 12 max_distance status n_matched total_distance mean_abs_std_diff 1 0.5 partial 18 4.46 0.113 2 1 optimal 20 5.36 0.0560 3 2 optimal 20 5.36 0.0560 4 Inf optimal 20 5.36 0.0560 # ℹ 7 more variables: max_abs_std_diff , certified , seconds , # n_rounds , candidate_edges , pairs_added , # edges_evaluated > path$balance # A tibble: 8 × 13 max_distance variable mean_left mean_right mean_diff sd_left sd_right std_diff 1 0.5 x 0.0391 0.134 -0.0947 0.830 0.745 -0.120 2 0.5 y 0.0278 -0.0447 0.0726 0.712 0.653 0.106 3 1 x 0.191 0.252 -0.0617 0.913 0.785 -0.0725 4 1 y -0.00647 0.0274 -0.0339 0.871 0.849 -0.0394 5 2 x 0.191 0.252 -0.0617 0.913 0.785 -0.0725 6 2 y -0.00647 0.0274 -0.0339 0.871 0.849 -0.0394 7 Inf x 0.191 0.252 -0.0617 0.913 0.785 -0.0725 8 Inf y -0.00647 0.0274 -0.0339 0.871 0.849 -0.0394 # ℹ 5 more variables: var_ratio , ks_statistic , ks_pvalue , # n_left , n_right > > > > > cleanEx() > nameEx("matchmaker") > ### * matchmaker > > flush(stderr()); flush(stdout()) > > ### Name: matchmaker > ### Title: Create blocks for stratified matching > ### Aliases: matchmaker > > ### ** Examples > > # Group blocking > left <- data.frame(id = 1:10, region = rep(c("A", "B"), each = 5), x = rnorm(10)) > right <- data.frame(id = 11:20, region = rep(c("A", "B"), each = 5), x = rnorm(10)) > blocks <- matchmaker(left, right, block_type = "group", block_by = "region") > print(blocks$block_summary) # A tibble: 2 × 3 block_id n_left n_right 1 A 5 5 2 B 5 5 > > # Clustering > blocks <- matchmaker(left, right, block_type = "cluster", + block_vars = "x", n_blocks = 3) > > > > > cleanEx() > nameEx("pixel_morph") > ### * pixel_morph > > flush(stderr()); flush(stdout()) > > ### Name: pixel_morph > ### Title: Pixel-level image morphing (final frame only) > ### Aliases: pixel_morph > > ### ** Examples > > if (requireNamespace("magick", quietly = TRUE)) { + imgA <- system.file("extdata/icons/circleA_40.png", package = "couplr") + imgB <- system.file("extdata/icons/circleB_40.png", package = "couplr") + if (nzchar(imgA) && nzchar(imgB)) { + result <- pixel_morph(imgA, imgB, n_frames = 4, show = FALSE) + } + } > > > > > cleanEx() > nameEx("pixel_morph_animate") > ### * pixel_morph_animate > > flush(stderr()); flush(stdout()) > > ### Name: pixel_morph_animate > ### Title: Pixel-level image morphing (animation) > ### Aliases: pixel_morph_animate > > ### ** Examples > > if (requireNamespace("magick", quietly = TRUE)) { + imgA <- system.file("extdata/icons/circleA_40.png", package = "couplr") + imgB <- system.file("extdata/icons/circleB_40.png", package = "couplr") + if (nzchar(imgA) && nzchar(imgB)) { + outfile <- tempfile(fileext = ".gif") + pixel_morph_animate(imgA, imgB, outfile = outfile, n_frames = 4, show = FALSE) + } + } > > > > > cleanEx() > nameEx("ps_match") > ### * ps_match > > flush(stderr()); flush(stdout()) > > ### Name: ps_match > ### Title: Propensity Score Matching > ### Aliases: ps_match > > ### ** Examples > > set.seed(42) > n <- 100 > data <- data.frame( + id = seq_len(n), + treated = rbinom(n, 1, 0.4), + age = rnorm(n, 50, 10), + income = rnorm(n, 50000, 15000) + ) > result <- ps_match(treated ~ age + income, data = data, treatment = "treated") Warning: 88.4% of pairs are forbidden! Only 287 valid pairs for 46 left units - the matching pool is shallow! Your constraints might be concerningly strict. Consider: - Relaxing max_distance threshold - Widening calipers - Using fewer/broader blocks - Checking if your data actually overlaps > print(result) Matching Result =============== Method: lap Pairs matched: 40 Unmatched (left): 6 Unmatched (right): 14 Total distance: 0.5506 Status: partial Matched pairs: # A tibble: 40 × 4 left_id right_id distance .logit_ps_diff 1 1 6 0.0145 0.0145 2 2 15 0.0193 -0.0193 3 5 8 0.000603 0.000603 4 7 27 0.0213 -0.0213 5 10 80 0.0286 0.0286 6 12 70 0.0134 0.0134 7 13 26 0.000811 0.000811 8 16 88 0.0300 0.0300 9 17 59 0.0393 0.0393 10 21 74 0.0110 -0.0110 # ℹ 30 more rows > > > > > cleanEx() > nameEx("sensitivity_analysis") > ### * sensitivity_analysis > > flush(stderr()); flush(stdout()) > > ### Name: sensitivity_analysis > ### Title: Rosenbaum Sensitivity Analysis > ### Aliases: sensitivity_analysis > > ### ** Examples > > set.seed(42) > left <- data.frame(id = 1:20, x = rnorm(20), outcome = rnorm(20, 1, 1)) > right <- data.frame(id = 21:40, x = rnorm(20), outcome = rnorm(20, 0, 1)) > result <- match_couples(left, right, vars = "x") > sens <- sensitivity_analysis(result, left, right, outcome_var = "outcome") > print(sens) Rosenbaum Sensitivity Analysis ============================== Matched pairs: 20 Alternative: greater Test statistic (T+): 155.0 Gamma p (upper bound) ----- --------------- 1.00 0.0323 * 1.25 0.0776 1.50 0.1388 1.75 0.2091 2.00 0.2829 2.25 0.3562 2.50 0.4262 2.75 0.4916 3.00 0.5514 * significant at alpha = 0.05 Insensitive to hidden bias up to Gamma = 1.00 To explain away the effect, hidden bias would need Gamma >= 1.25 > > > > > cleanEx() > nameEx("sinkhorn") > ### * sinkhorn > > flush(stderr()); flush(stdout()) > > ### Name: sinkhorn > ### Title: 'Sinkhorn-Knopp' optimal transport solver > ### Aliases: sinkhorn > > ### ** Examples > > cost <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, byrow = TRUE) > > # Soft assignment with default parameters > result <- sinkhorn(cost) > print(round(result$transport_plan, 3)) [,1] [,2] [,3] [1,] 0.111 0.111 0.111 [2,] 0.111 0.111 0.111 [3,] 0.111 0.111 0.111 > > # Sharper assignment (higher lambda) > result_sharp <- sinkhorn(cost, lambda = 50) > print(round(result_sharp$transport_plan, 3)) [,1] [,2] [,3] [1,] 0.111 0.111 0.111 [2,] 0.111 0.111 0.111 [3,] 0.111 0.111 0.111 > > # With custom marginals (more mass from row 1) > result_weighted <- sinkhorn(cost, r_weights = c(0.5, 0.25, 0.25)) > print(round(result_weighted$transport_plan, 3)) [,1] [,2] [,3] [1,] 0.167 0.167 0.167 [2,] 0.083 0.083 0.083 [3,] 0.083 0.083 0.083 > > # Round to hard assignment > hard_match <- sinkhorn_to_assignment(result) > print(hard_match) [1] 1 3 2 > > > > > cleanEx() > nameEx("sinkhorn_to_assignment") > ### * sinkhorn_to_assignment > > flush(stderr()); flush(stdout()) > > ### Name: sinkhorn_to_assignment > ### Title: Round 'Sinkhorn' transport plan to hard assignment > ### Aliases: sinkhorn_to_assignment > > ### ** Examples > > cost <- matrix(c(1, 2, 3, 4, 5, 6, 7, 8, 9), nrow = 3, byrow = TRUE) > result <- sinkhorn(cost, lambda = 20) > hard_match <- sinkhorn_to_assignment(result) > print(hard_match) [1] 1 3 2 > > > > > cleanEx() > nameEx("solver_status_values") > ### * solver_status_values > > flush(stderr()); flush(stdout()) > > ### Name: solver_status_values > ### Title: Solver status values > ### Aliases: solver_status_values > > ### ** Examples > > solver_status_values() [1] "optimal" "partial" "infeasible" "eps_optimal" [5] "iteration_limit" "interrupted" "heuristic" > > > > > cleanEx() > nameEx("subclass_match") > ### * subclass_match > > flush(stderr()); flush(stdout()) > > ### Name: subclass_match > ### Title: Subclassification on Propensity Score > ### Aliases: subclass_match > > ### ** Examples > > set.seed(42) > n <- 200 > data <- data.frame( + id = 1:n, + age = rnorm(n, 40, 10), + income = rnorm(n, 50000, 15000) + ) > data$treatment <- rbinom(n, 1, plogis(-2 + 0.05 * data$age)) > result <- subclass_match(treatment ~ age + income, data, treatment = "treatment") > print(result) Subclassification Result ======================== Estimand: ATT Subclasses: 5 Treated (left): 103 Control (right): 97 Subclass summary: # A tibble: 5 × 7 subclass n_treated n_control mean_ps ps_range_low ps_range_high has_overlap 1 1 11 29 0.290 0.119 0.372 TRUE 2 2 20 20 0.431 0.373 0.472 TRUE 3 3 16 24 0.518 0.472 0.554 TRUE 4 4 27 13 0.605 0.554 0.643 TRUE 5 5 29 11 0.731 0.644 0.880 TRUE > > > > > cleanEx() > nameEx("update_constraints") > ### * update_constraints > > flush(stderr()); flush(stdout()) > > ### Name: update_constraints > ### Title: Update Constraints on Distance Object > ### Aliases: update_constraints > > ### ** Examples > > left <- data.frame(id = 1:5, age = c(25, 30, 35, 40, 45)) > right <- data.frame(id = 6:10, age = c(24, 29, 36, 41, 44)) > dist_obj <- compute_distances(left, right, vars = "age") > > # Apply constraints > constrained <- update_constraints(dist_obj, max_distance = 2) > result <- match_couples(constrained) Warning: All distances are identical (1.0000) Your matching variables aren't informative! Possible causes: - Constant variables (no variation) - Highly correlated variables - Inappropriate distance metric Try: - Using auto_scale = TRUE - Checking variable variation - Adding more informative variables Warning: 80.0% of pairs are forbidden! Only 5 valid pairs for 5 left units - the matching pool is shallow! Your constraints might be concerningly strict. Consider: - Relaxing max_distance threshold - Widening calipers - Using fewer/broader blocks - Checking if your data actually overlaps > > > > > cleanEx() > nameEx("verify_assignment") > ### * verify_assignment > > flush(stderr()); flush(stdout()) > > ### Name: verify_assignment > ### Title: Verify that an assignment is optimal > ### Aliases: verify_assignment print.assignment_certificate > > ### ** Examples > > set.seed(1) > cost <- matrix(runif(100), 10, 10) > verify_assignment(assignment(cost), cost) Assignment certificate ====================== primal_feasible TRUE valid matching TRUE all rows matched TRUE dual_feasible TRUE complementary_slackness TRUE matched arcs tight TRUE (max slack 0.000e+00) unmatched columns free TRUE (max |v_j| 0.000e+00) duality_gap 0.000000e+00 max_suboptimality 0.000000e+00 certified_optimal TRUE arithmetic exact, no tolerance (solver potentials) primal objective 1.287826853 dual objective 1.287826853 matched 10 of 10 rows, 10 columns > > # A rectangular problem, where the condition on unmatched columns bites. > # Passing the duals result reuses its duals instead of solving again. > rect <- matrix(runif(120), 6, 20) > verify_assignment(assignment_duals(rect), rect) Assignment certificate ====================== primal_feasible TRUE valid matching TRUE all rows matched TRUE dual_feasible TRUE complementary_slackness TRUE matched arcs tight TRUE (max slack 0.000e+00) unmatched columns free TRUE (max |v_j| 0.000e+00) duality_gap 0.000000e+00 max_suboptimality 0.000000e+00 certified_optimal TRUE arithmetic exact, no tolerance (solver potentials) primal objective 0.5591071853 dual objective 0.5591071853 matched 6 of 6 rows, 20 columns > > # Integer costs carry an integer optimal dual solution, so the conditions > # hold with no tolerance and the certificate is exact. > int_cost <- matrix(sample(1:100, 64, replace = TRUE), 8, 8) > cert <- verify_assignment(assignment(int_cost), int_cost) > cert$arithmetic [1] "exact" > > > > > cleanEx() > nameEx("verify_flow") > ### * verify_flow > > flush(stderr()); flush(stdout()) > > ### Name: verify_flow > ### Title: Verify that a flow is optimal > ### Aliases: verify_flow print.flow_certificate > > ### ** Examples > > # Two supply nodes shipping to two demand nodes, stated directly as a flow. > prob <- list( + n_nodes = 4, + supply = c(2, 1, -2, -1), + arcs = data.frame( + tail = c(1, 1, 2, 2), + head = c(3, 4, 3, 4), + lower = c(0, 0, 0, 0), + upper = c(2, 2, 2, 2), + cost = c(1, 3, 2, 1) + ) + ) > > # Both of node 1's units go to node 3, node 2's unit goes to node 4. > verify_flow(c(2, 0, 0, 1), prob, potential = c(0, 0, 1, 1)) Flow certificate ================ primal_feasible TRUE arcs outside bounds 0 nodes out of balance 0 (max error 0.000e+00) dual_feasible TRUE complementary_slackness TRUE (0 violating arc(s)) duality_gap 0.000000e+00 certified_optimal TRUE arithmetic exact, no tolerance (solver potentials) primal objective 3 dual objective 3 > > # The same flow against potentials that certify nothing. > verify_flow(c(2, 0, 0, 1), prob, potential = c(0, 0, 0, 0)) Flow certificate ================ primal_feasible TRUE arcs outside bounds 0 nodes out of balance 0 (max error 0.000e+00) dual_feasible TRUE complementary_slackness FALSE (2 violating arc(s)) duality_gap 3.000000e+00 certified_optimal FALSE arithmetic double, tolerance 1.0e-09 primal objective 3 dual objective 0 > > > > > ### *