Skip to content

Retain harm bound in to_integer() for AHR designs - #673

Merged
LittleBeannie merged 3 commits into
mainfrom
to-integer-harm
Sep 16, 2026
Merged

LittleBeannie merged 3 commits into
mainfrom
to-integer-harm

Conversation

@yihui

@yihui yihui commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Problem

to_integer.gs_design() rebuilds an AHR group sequential design with gs_power_ahr() at the rounded integer event counts, but the reconstruction did not forward the harm bound arguments (harm, hpar, test_harm). As a result, the harm boundary was silently dropped whenever an AHR design with harm bounds was converted to integer events:

x <- gs_design_ahr(
  analysis_time = c(18, 30),
  upper = gs_spending_bound,
  upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL),
  lower = gs_spending_bound,
  lpar = list(sf = gsDesign::sfHSD, total_spend = 0.1, param = -2),
  harm = gs_spending_bound,
  hpar = list(sf = gsDesign::sfHSD, total_spend = 0.05, param = -2),
  test_harm = TRUE
)
unique(x$bound$bound)              #> "upper" "harm" "lower"
unique(to_integer(x)$bound$bound)  #> "upper" "lower"   <- harm lost

This surfaced while wiring harm bounds through the gsDesign2 Shiny app (keaven/gsDesign2Shiny#76), where integer sample sizes are on by default.

Fix

Forward harm/hpar/test_harm from x$input into the gs_power_ahr() call in the AHR/WLR branch. These fields are always present on the input and default to gs_b/-Inf/FALSE for designs without a harm bound, so non-harm designs are unaffected. gs_power_wlr() does not accept harm arguments, so the change is guarded to the AHR case (is_ahr).

After the fix, to_integer() retains the harm bound and the cumulative harm crossing probability under H0 at the final analysis matches the input (0.05 in the example above).

Tests

Added an independent test asserting the harm bound survives to_integer() and that the final cumulative H0 harm crossing probability equals the requested total spend. Existing to_integer tests still pass.

🤖 Generated with Claude Code

to_integer.gs_design() rebuilds the design with gs_power_ahr() using the
stored inputs, but did not pass the harm bound arguments, so the harm
boundary was silently dropped when converting an AHR group sequential
design to integer events.

Forward harm/hpar/test_harm from x$input to the gs_power_ahr() call. These
default to gs_b/-Inf/FALSE for designs without a harm bound, so non-harm
designs are unaffected. gs_power_wlr() does not support harm bounds, so
the change applies to the AHR branch only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@jdblischak jdblischak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xref: follow-up to #640

Comment thread tests/testit/test-independent-to_integer.R Outdated
Per review, relocate the harm-bound retention test from
test-independent-to_integer.R to test-developer-to_integer.R, matching where
the rest of the harm bound tests (#640) live: it exercises implementation
behavior of to_integer() rather than validating output against an independent
source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LittleBeannie
LittleBeannie merged commit 16f7947 into main Sep 16, 2026
10 checks passed
@LittleBeannie
LittleBeannie deleted the to-integer-harm branch September 16, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants