pub fn run_monte_carlo_adaptive_seeded(
base_inputs: &BallisticInputs,
base_wind: &WindConditions,
params: &MonteCarloParams,
convergence: &McConvergence,
hit_radius_m: f64,
seed: u64,
) -> Result<AdaptiveMcReportV1, String>Expand description
Runs a Monte Carlo hit-probability estimate that decides its own sample size, from an explicit seed.
§What this buys over the fixed-count path
run_monte_carlo_with_wind_and_direction_std_dev_seeded runs exactly
params.num_simulations trials and reports a point estimate; whether that count was enough
is left to the caller to guess. This instead runs until the answer is as precise as the
caller asked for, and reports the achieved precision either way. params.num_simulations
is ignored here – the sample count comes from convergence, and
McConvergence::min_samples defaults to the same 1_000 so the floor matches the legacy
default.
§Why the interval is anytime-valid
Stopping when the interval looks tight enough is optional stopping, and a fixed-n
interval (Wilson, Wald, anything) checked repeatedly that way has no coverage guarantee:
the error rate grows with the number of peeks. BernoulliConfidenceSequence is instead
valid at every n simultaneously, so a data-dependent stopping rule is legitimate. The
price is a strictly wider interval at any given n than the fixed-n
MonteCarloResults::hit_probability_wilson would report on the same counts. Paying it is
the point.
§The trial is the same trial
Each trial is MonteCarloTrialSampler::sample_one_trial (crate-internal), the same body
and the same six-draw sequence the legacy loop runs, and the hit test is
MonteCarloResults::position_is_hit, the same predicate
MonteCarloResults::hit_probability counts with. A trial that never reached the target
plane is a definite miss and stays in the denominator, exactly as it does there. Wind
direction is not dispersed: MonteCarloParams has no direction-sigma field, so this
passes 0.0, matching run_monte_carlo_with_wind.
§Sample accounting
Trials are run in batches of McConvergence::batch_size (the last batch truncated so the
ceiling is never overshot), and the stopping rule is evaluated after each batch: stop with
McStopReason::TargetHalfWidthMet once at least min_samples trials are in and the
half-width is at or below target_half_width; stop with
McStopReason::MaxSamplesReached once max_samples trials have been attempted.
The report carries three cardinalities, and they are three different numbers:
attempts– trials drawn. This is whatmax_samplescaps.samples– trials that produced an outcome, i.e. thenbehindhit_probabilityand the confidence interval.arrivals– trials that reached the target plane, i.e. thenbehind the three at-target statistics.
attempts >= samples >= arrivals always. A trial whose solve fails is dropped rather than
counted as a miss – the legacy loop’s behaviour, preserved so the two paths cannot
disagree about what a solver failure means – but it still consumes an attempt, so it
shows up as attempts > samples. With no dropped trials the two are equal, which is the
normal case; when they differ, samples can finish below min_samples, and the honest
report of that is MaxSamplesReached with the smaller n and the correspondingly wider
interval. A trial that solved but fell short of the target plane is a definite miss: it is
a sample (in hit_probability’s denominator) but not an arrival, so it shows up as
samples > arrivals. A run in which every trial was dropped is an error, not a report.
§hit_radius_m is not validated
Every McConvergence field is Err-checked up front, but hit_radius_m itself is not:
a NaN or negative radius makes MonteCarloResults::position_is_hit false for every
trial, yielding p = 0.0 with a tight interval and (usually) a
McStopReason::TargetHalfWidthMet stop rather than an error. This is deliberate, not an
oversight – it matches MonteCarloResults::hit_probability’s equally lenient legacy
posture on the same input, so this path cannot diverge from the fixed-count one over how a
bad radius is treated.
§Errors
Returns Err if convergence is not usable (McConvergence::validate, which names the
offending field), if the baseline solve or an input distribution is invalid, or if no trial
at all produced an outcome.