Skip to content

Feature/Bug: Support Pocock-Simon Minimization in Dynamic Code Generator #650

Description

@fderuiter

Root cause

The rejection occurs in:

src/app/domain/schema-management/services/generation/base.strategy.ts

if (mode === 'DYNAMIC' && method === 'MINIMIZATION') {
  throw new CodeGenerationError(
    'Dynamic simulation engine is not supported for Pocock-Simon Minimization. Please use Static Manifest mode.',
    config
  );
}

This is not caused by StaticMappingGuard; that guard only validates output after generation succeeds.

The modal currently allows users to choose both Dynamic Generator and Both (ZIP Bundle) for a minimization study:

  • DYNAMIC calls dynamic generation and displays an error panel.
  • BOTH generates static output first, then dynamic output; dynamic generation throws, so the otherwise-valid static artifact is discarded.
  • Downloading BOTH repeats that same failing dynamic-generation call, so no ZIP is produced.

Why dynamic minimization is not a small template change

The current dynamic-generator IR and language strategies are built around block randomization:

  • precomputed site/stratum/cap tasks;
  • block-size selection;
  • block construction and Fisher–Yates shuffling;
  • a round-robin loop until each task reaches its cap.

Pocock-Simon minimization instead needs state that the IR does not represent:

  • sequential subject generation up to minimizationConfig.totalSampleSize;
  • sampling of every stratification factor level according to expectedProbability;
  • per-site, per-factor, per-level, per-arm marginal counts;
  • candidate-arm imbalance scoring relative to treatment ratios;
  • biased-coin selection using minimizationConfig.p;
  • cap eligibility and the algorithm’s existing truncation behavior;
  • deterministic MT19937 draw ordering compatible with the application engine.

The application-side implementation already exists in:

src/app/domain/randomization-engine/core/minimization-algorithm.ts

It is covered by unit, property, golden-fixture, and worker/fallback parity tests. Static code generation works because it runs that implementation first and exports the resulting concrete manifest.

Proposed split

1. Immediate UX fix — prevent a dead-end export flow

Update CodeGeneratorModalComponent to derive whether dynamic export is supported from the active config.

For randomizationMethod === 'MINIMIZATION':

  • disable the Dynamic Generator and Both (ZIP Bundle) segmented controls;
  • add an explanatory tooltip or inline note:
    “Dynamic export is not yet available for Pocock-Simon minimization. A static manifest preserves the generated allocation sequence.”
  • if a stale persisted selection is DYNAMIC or BOTH, automatically set exportMode to STATIC, regenerate the preview, and announce the fallback accessibly;
  • defensively normalize to STATIC in refreshCode() and downloadCode() as well, so programmatic calls and restored UI state cannot reach the unsupported path;
  • do not silently produce a ZIP containing only a static file while labeling it “Both.”

This resolves the user-facing bug without misrepresenting dynamic-engine support.

2. Long-term dynamic-engine support — separate feature work

Implement a minimization-specific dynamic strategy rather than forcing the existing block strategy to handle it:

  1. Extend LogicIR with a discriminated minimization payload:

    • total sample size;
    • sites and allocation behavior;
    • factors, levels, and expected probabilities;
    • cap strategy / cap data;
    • arm IDs, names, normalized ratios;
    • biased-coin probability;
    • explicit RNG-consumption contract.
  2. Add a minimization dynamic-logic builder to the algorithm registry/framework.

  3. Add language-specific implementations for R, Python, SAS, and Stata that maintain their own local mutable marginal-count state.

  4. Preserve the application algorithm’s semantics:

    • eligible-arm selection;
    • ratio-aware imbalance calculation;
    • tie handling;
    • biased-coin selection;
    • subject-ID sequence behavior;
    • deterministic seeded output.
  5. Replace the current minimization rejection test with per-language dynamic-generation and behavioral-parity tests.

Acceptance criteria

UX fallback

  • A Pocock-Simon minimization study opens the code generator in Static Manifest mode.
  • Dynamic and ZIP-bundle export options are unavailable, clearly explained, and keyboard/accessibility semantics remain correct.
  • A persisted or programmatically selected dynamic/both mode automatically falls back to static without showing a CodeGenerationError.
  • Static preview, copy, and download continue to work for all four target languages.
  • Unit tests cover minimization initialization, mode switching, stale-mode fallback, and download behavior.

Dynamic-engine support

  • Dynamic minimization generation succeeds for R, Python, SAS, and Stata.
  • Generated scripts implement sequential Pocock-Simon allocation rather than block randomization.
  • Generated scripts honor p, total sample size, factor-level expected probabilities, treatment ratios, sites, and supported caps.
  • A fixed seed produces deterministic output within each target runtime.
  • Generated allocations are verified against canonical fixtures or a defined behavioral-parity contract with generateMinimization().
  • Unsupported configuration combinations remain explicitly guarded with actionable errors.

Suggested priority

I recommend treating the UX fallback as the immediate bug fix, and tracking dynamic minimization parity as a separate, larger feature. The latter is a multi-language port of a stateful allocation algorithm and should not be hidden behind a small “remove the guard” change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions