Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: PecaWolf/LoopAlgorithm
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: tidepool-org/LoopAlgorithm
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 8 commits
  • 16 files changed
  • 7 contributors

Commits on Mar 11, 2026

  1. Configuration menu
    Copy the full SHA
    9e1ff9b View commit details
    Browse the repository at this point in the history
  2. formatting, typo

    PecaWolf committed Mar 11, 2026
    Configuration menu
    Copy the full SHA
    07afc01 View commit details
    Browse the repository at this point in the history

Commits on Mar 25, 2026

  1. force rebuild

    PecaWolf committed Mar 25, 2026
    Configuration menu
    Copy the full SHA
    1816673 View commit details
    Browse the repository at this point in the history
  2. Merge pull request tidepool-org#27 from tidepool-org/pd/NEMO-254/meal…

    …_bolus_no_isf_test
    
    Pd/nemo 254/meal bolus no isf test
    PecaWolf authored Mar 25, 2026
    Configuration menu
    Copy the full SHA
    bd1a879 View commit details
    Browse the repository at this point in the history

Commits on May 21, 2026

  1. Make decayEffect a continuous function of sample timestamp (tidepool-…

    …org#33)
    
    * Make decayEffect a continuous function of sample timestamp
    
    Reformulates decayEffect using a closed-form quadratic in time-since-sample
    rather than accumulating step-by-step from the floored simulation boundary.
    This makes the effect value at any future absolute timestamp independent of
    which delta-sized simulation bucket the sample's startDate falls into.
    
    For samples aligned to delta boundaries the two formulations are
    mathematically identical. For unaligned samples (the common case with real
    CGM streams) the new formulation removes a small discontinuity that the
    old code exhibited at bucket boundaries.
    
    Adds LoopMathTests covering continuity across a delta boundary. Existing
    fixture-calibrated tests are re-pinned to the new values; per-prediction
    drift is on the order of 0.1 mg/dL.
    
    Ports the LoopMath change from LoopKit/LoopKit#556 by Moti Nisenson-Ken to
    the LoopAlgorithm package, where decayEffect now lives.
    
    * Space to kick off tests
    
    ---------
    
    Co-authored-by: Pete Schwamb <pete@pete-mbp-eth.maplect.net>
    ps2 and Pete Schwamb authored May 21, 2026
    Configuration menu
    Copy the full SHA
    b05f72b View commit details
    Browse the repository at this point in the history
  2. Add unit tests for StandardRetrospectiveCorrection (tidepool-org#32)

    StandardRetrospectiveCorrection (the P-only retrospective correction
    controller) had no dedicated unit tests — its behavior was only
    covered transitively via the higher-level LoopAlgorithm tests.
    
    Adds 9 tests covering:
    - Recency gating: stale / nil / empty discrepancy lists clear the
      correction and return empty.
    - Total correction effect equals the latest (most-recent) discrepancy
      magnitude (Standard is P-only on .last).
    - Positive / negative discrepancies project glucose forward in the
      expected direction, with the last sample ≈ starting + discrepancy.
    - The first effect sample equals the starting glucose value at the
      starting date (correction hasn't yet had time to apply).
    - Only the latest discrepancy contributes — older entries are ignored
      (the key behavioral difference vs IntegralRC, which integrates them).
    - Short discrepancies are clamped to retrospectiveCorrectionGroupingInterval
      to prevent over-amplified velocity from very short windows.
    
    These tests will serve as a backstop while the active-insulin / EGP
    decomposition work modifies the glucose-effect computation upstream of
    the RC discrepancy calculation.
    
    Co-authored-by: LoopKit Developer <dev@loopkit.org>
    ps2 and loopkitdev authored May 21, 2026
    Configuration menu
    Copy the full SHA
    a1d0e57 View commit details
    Browse the repository at this point in the history
  3. Faster filterDateRange via binary search (tidepool-org#31)

    SampleValue.swift: add a filterDateRange overload for
    RandomAccessCollection where Element: TimelineValue, Index == Int.
    Returns the same result as the existing Sequence-based linear-filter
    implementation but uses two binary searches instead of a linear scan.
    Picks up automatically for Array-backed callers (which is every caller
    in this codebase via Swift protocol dispatch).
    
    Significant speedup for hot paths that call filterDateRange repeatedly
    on long schedules — for example, InsulinMath.glucoseEffectsMidAbsorptionISF
    and DoseMath.insulinCorrection when the sensitivity schedule has many
    segments. In a LoopEval 60-day per-step prediction sweep with a per-step
    ISF schedule, total sim wall-clock went from ~30 min to ~1 min (≈30×
    faster) with bit-identical output to the linear-filter path.
    
    Tests: FilterDateRangeTests.swift with 11 cases covering equivalence
    with the linear-filter reference: boundary cases (empty, both bounds
    nil, only start, only end), start-before-all, end-after-all, fully-
    outside, single-sample collections, exact-match-one-segment, and a
    100-iteration randomized fuzz over a 200-element contiguous schedule.
    ps2 authored May 21, 2026
    Configuration menu
    Copy the full SHA
    729e508 View commit details
    Browse the repository at this point in the history
  4. Add PrecomputedInsulinInput for efficient multi-step prediction sweep…

    …s + parallelize glucose-effects (tidepool-org#29)
    
    * Add PrecomputedInsulinInput for efficient multi-step prediction sweeps
    
    Introduces PrecomputedInsulinInput and a new generatePrediction overload
    that accepts pre-annotated dose data, enabling significant speedups for
    historical back-testing / evaluation sweeps.
    
    The key bottleneck in a dense prediction sweep is doses.annotated(with: basal),
    which is O(doses × basalSegments) and was called from scratch at every step.
    Between adjacent 5-min steps the dose list changes only at its edges; the
    annotation of every dose in the middle is identical.
    
    Changes:
    - Sources/LoopAlgorithm/Insulin/PrecomputedInsulinInput.swift (new)
      PrecomputedInsulinInput struct holding pre-annotated doses and an optional
      pre-built insulinEffects timeline. Includes a convenience .build() factory.
    
    - Sources/LoopAlgorithm/LoopAlgorithm.swift
      New generatePrediction(start:glucoseHistory:precomputedInsulin:carbEntries:
      sensitivity:carbRatio:...) overload. Skips annotated(with:) entirely;
      optionally skips glucoseEffects() when insulinEffects is pre-supplied.
    
    - Sources/LoopAlgorithm/Glucose/GlucoseEffect.swift
      Add Sendable conformance (struct with value-type fields, safe).
    
    - Tests/LoopAlgorithmTests/PrecomputedInsulinInputTests.swift (new)
      3 tests verifying the new overload produces output matching the standard
      path (bit-identical for annotation-only, count-identical + clinically
      equivalent for pre-built effects).
    
    Expected speedup for a 7-day sweep at 5-min step (~2016 calls):
      annotation bypass alone: ~40-60% wall-clock reduction
      + effects cache (fixed ISF): additional ~20-30%
    
    * Refactor PrecomputedInsulinInput for explicit ISF-sweep pattern
    
    Split the API into two explicit steps so ISF sweeps pay annotation cost
    exactly once across all multipliers:
    
      annotate(doses:basal:)          → ISF-independent, build once
      .withEffects(sensitivity:from:to:) → ISF-dependent, once per multiplier
    
    Correct ISF sweep pattern:
      let base = PrecomputedInsulinInput.annotate(doses: doses, basal: basal)
      for multiplier in isfMultipliers {
        let input = base.withEffects(sensitivity: scale(sensitivity, by: multiplier))
        // run ~2016 steps with input — no annotation, no per-step glucoseEffects
      }
    
    Cost breakdown for 10-multiplier × 7-day sweep (n≈2016 steps each):
      Before: annotated(with:) + glucoseEffects() called 20160× each
      After:  annotated(with:) called 1×, glucoseEffects() called 10×
    
    Also adds testISFSweepPattern verifying bit-identical output across
    multipliers [0.7, 0.8, ..., 1.3] vs the standard generatePrediction path.
    
    * Add sliced(from:to:) for per-step dose window slicing
    
    Enables EvalCore to slice pre-annotated doses to the per-step lookback
    window without re-annotating. Uses binary search on startDate + linear
    filter on endDate (arrays are ~100-200 entries, linear endDate scan
    is negligible).
    
    Also cleans up the unused private partition helper (now only used by sliced).
    
    * Expose dose-recommendation internals as public API
    
    Downstream callers (LoopEval bench engine) need to compute dose
    recommendations from a forecast without going through the full run() API,
    which re-computes insulin effects. Making insulinCorrection,
    recommendTempBasal, and recommendAutomaticDose public lets them do
    that efficiently using already-computed predictions.
    
    Enables delivery-based ODR/UDR metrics in LoopEval that compare the
    actual insulin Loop would deliver across two configurations.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
    
    * Parallelize glucose-effects accumulation in InsulinMath
    
    Replace the sequential reduce loop with DispatchQueue.concurrentPerform
    over per-step increments, then a final cumsum. Per-step contributions are
    independent until the final summation, so this scales with available cores.
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    
    * chore: carry forward momentumVelocityMaximum param from eval/precomputed-insulin-effects
    
    ---------
    
    Co-authored-by: Bot <bot@macmini.maplect.net>
    Co-authored-by: LoopKit Developer <dev@loopkit.org>
    Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
    4 people authored May 21, 2026
    Configuration menu
    Copy the full SHA
    6e4e315 View commit details
    Browse the repository at this point in the history
Loading