Skip to content

Instantly share code, notes, and snippets.

@ajparsons
Created September 13, 2025 14:44
Show Gist options
  • Select an option

  • Save ajparsons/ce29bcbf12e6c0595e298905502f5a77 to your computer and use it in GitHub Desktop.

Select an option

Save ajparsons/ce29bcbf12e6c0595e298905502f5a77 to your computer and use it in GitHub Desktop.
# Install rextendr if you haven't
# install.packages("rextendr")
library(rextendr)
rust_code <- "
use extendr_api::prelude::*;
use extendr_api::prelude::*;
use statrs::distribution::{Normal, Continuous};
/// Midpoint-based approach to computing expected distance to nearest party
///
/// This divides the voter space into segments between party midpoints,
/// then integrates the expected distance to the nearest party in each segment.
#[extendr]
fn mean_min_abs_distance_midpoints(parties: Vec<f64>) -> f64 {
// Return NaN if no parties provided
if parties.is_empty() {
return f64::NAN;
}
// Create the standard normal distribution
let norm = Normal::new(0.0, 1.0).unwrap();
// Sort the party positions
let mut sorted_parties = parties.clone();
sorted_parties.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mut total = 0.0;
// Extend the party list with artificial outer boundaries
// so we handle voters beyond the outermost parties
let mut boundaries = Vec::new();
// Add a lower bound far to the left (say, -∞ → party 0 dominates)
boundaries.push(f64::NEG_INFINITY);
for i in 0..(sorted_parties.len() - 1) {
// Midpoint between party i and i+1
let mid = (sorted_parties[i] + sorted_parties[i + 1]) / 2.0;
boundaries.push(mid);
}
// Add upper bound far to the right (∞ → last party dominates)
boundaries.push(f64::INFINITY);
// Now integrate each segment where one party is closest
for i in 0..sorted_parties.len() {
let left = boundaries[i];
let right = boundaries[i + 1];
let party_pos = sorted_parties[i];
// Numerical integration in this segment
let num_points = 1000;
let step = (right - left) / num_points as f64;
let mut segment_sum = 0.0;
for j in 0..num_points {
let x = left + j as f64 * step;
// If x is infinite (at the far ends), skip it
if !x.is_finite() {
continue;
}
let pdf = norm.pdf(x);
let distance = (x - party_pos).abs();
// Add to segment's contribution
segment_sum += distance * pdf * step;
}
total += segment_sum;
}
total
}
extendr_module! {
mod mymodule;
fn mean_min_abs_distance_midpoints;
}
"
# Compile and load
rust_source(
code = rust_code,
dependencies = list(
statrs = "0.16",
rayon = "1.6"
),
quiet = TRUE,
profile = "release"
)
library(tictoc)
# Example usage
set.seed(42)
party_positions <- rnorm(5)
cat("Party positions:\n")
print(party_positions)
tic()
# Compute the exact mean minimal absolute distance
result <- mean_min_abs_distance_midpoints(party_positions)
toc()
cat("Mean minimal absolute distance (exact, no sampling):\n")
print(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment