Skip to content

Reject non-one-based t in munge_data - #624

Draft
ChrisRackauckas-Claude wants to merge 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:pr-s4-munge-data-offset-t
Draft

ChrisRackauckas-Claude wants to merge 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:pr-s4-munge-data-offset-t

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

What changed and why

Every interpolation type's constructor funnels u, t through
munge_data, which only validates length(t) (and, for vector u,
sortedness). It never checks that t has one-based indexing. An
OffsetArray t is therefore accepted silently, and subsequent 1-based
indexing into t elsewhere in the package (knot lookups, get_idx,
etc.) reads the wrong elements — a wrong value rather than an error.
Most interpolation types happen to throw anyway (via some other
incidental bounds check), but ConstantInterpolation does not.

This PR adds Base.require_one_based_indexing(t) to all three
munge_data methods (vector, matrix, N-d u) in interpolation_utils.jl,
matching the existing use of require_one_based_indexing elsewhere in the
package (_cumulative_integral).

Failing before / passing after

Before (unmodified master, commit b98db80):

=== candidate 6: ConstantInterpolation with OffsetArray t ===
ConstantInterpolation(u, t)(2.5) = 0.9092974268256817
ConstantInterpolation(u, OffsetArray(t,0:9))(2.5) = 0.8414709848078965

ConstantInterpolation(sin.(1:10), OffsetArray(1:10, 0:9))(2.5) silently
returns 0.8415 on master (the value at t=1), where 0.9093 (the value
at t=2) is correct — no error, no warning.

New test "munge_data rejects offset t" in test/interpolation_tests.jl
checks munge_data directly (vector, matrix, N-d) and ConstantInterpolation
end-to-end, asserting all four throw ArgumentError for an offset t.
This test fails (wrong value, no error) against unmodified master and
passes with the fix.

What was not verified

  • Did not exhaustively check every interpolation type's behavior with an
    offset t beyond ConstantInterpolation (the one the audit found
    silently wrong); other types already throw before this change for
    unrelated reasons, and this change makes that uniform and explicit.
  • Core/QA groups were run on Julia 1.12 and 1.10 (lts); pre was not
    run.

Anything a reviewer should push back on

None beyond the usual: this duplicates the vector-u require_one_based_indexing(t)
call across three methods rather than factoring it into a shared helper;
given each method's small size this seemed clearer than adding another
layer of indirection, but a reviewer might prefer otherwise.

Please ignore until reviewed by @ChrisRackauckas.

Risk assessment

  • Risk: low
  • Blast radius: all three munge_data methods in
    interpolation_utils.jl, used by every interpolation type's
    constructor. Only rejects input (non-one-based t) that was already
    silently wrong or (for most types) already erroring; no change to
    accepted (one-based) input. No public API signature change.
  • Evidence: failing-before/passing-after shown above; new test added.
  • Independent review: pending
  • Merge: needs human review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LPHREnnonfLg1VcE1EJovv

Breaking change

Breaking: ConstantInterpolation(u, OffsetArray(t, 1)) and direct munge_data calls with non-one-based time axes now throw ArgumentError; the current release accepts them. This is a public behavior change and needs a major bump (Project.toml is still 10.1.3), or Chris's explicit call that it ships as a bug fix. The one-based time-axis precondition should also be documented. Flagged by the independent review (codex-cli gpt-6.1-sol).


🤖 Posted by an AI agent — harness: Claude Code 2.1.301 · model: claude-opus-5-5
Conversation: https://claude.ai/code/session_01VZjwf7Ux9YtTzwHGHMqnsq

🤖 Automated comment from an AI agent running as @ChrisRackauckas — not written or reviewed by Chris.

Risk assessment (independent review, Codex gpt-6.1-sol, head 83cb259)

  • Verdict: CHANGES
  • Rating: Codex gpt-6.1-sol rated it high (H1) at head 83cb259
  • Risk: high (H1: exported interpolation constructors now reject inputs that previously produced correct results; Project.toml remains 10.1.3).
  • Blast radius: All three munge_data dispatches, their constructor callers, and append! callers acquire the restriction. In particular, offset vector inputs that missing-data filtering converts into ordinary vectors are rejected before filtering. Existing one-based vector/matrix evaluation and vector/matrix/N-dimensional data preparation passed targeted controls.
  • Evidence: Read pr.txt, all of risk.md, and git diff origin/HEAD...HEAD; there is no linked issue. Archived origin/HEAD (898d88e) and HEAD into scratch, then created separate scratch environments with Pkg.develop and OffsetArrays. All Julia commands used Julia 1.12.4, the requested JULIA_DEPOT_PATH, JULIA_NUM_THREADS=4, TMPDIR under scratch, and timeout 3600, sequentially. Commands from scratch: julia --project=env_base regression.jl → 4 failures/4 assertions (three missing exceptions and one BoundsError instead of ArgumentError); julia --project=env_head head-tests.jl → new assertions 4/4 pass, data-preparation controls 24/24 pass, interpolation controls 8/8 pass; julia --project=env_base controls.jl → the same 24/24 and 8/8 controls pass. Logs: scratch/regression-base.log, scratch/head-tests.log, scratch/controls-base.log. Probes reproduce the negative-offset wrong value (0.8414709848078965 instead of sin(2)=0.9092974268256817) and the valid missing-data regression described below. git diff --check origin/HEAD...HEAD passed; git status --porcelain stayed empty. GitHub read-only metadata reports no failing checks; there was consequently no failing CI check requiring a default-branch comparison. Read the Extensions job log: Mooncake 769/769, sparse tracer 660/660, Zygote 29470/29470, overall passed. G1 pass: independent left-sample and analytic linear references checked; no numerical formula changes. G2 pass for affected AD integration through the existing Extensions CI; no GPU/Reactant/MPI paths changed. G3 not applicable: no shared-state/cache/threading changes. No existing assertions are skipped or weakened, no new public names are added, and the added dependency is test-only OffsetArrays with an inspected MIT license; the implementation uses public Base.require_one_based_indexing.
  • Merge: needs changes (preserve the working filtering case or obtain a breaking-release decision with the appropriate major version and documented input contract; also remove the task-specific test comment).

🤖 Posted by an AI agent — harness: Codex CLI 0.162.1 · model: gpt-6.1-sol
Conversation: local headless review run (no public session URL); transcript kept by the fleet master.

`munge_data` only checked `length(t)`, so a `t` with non-one-based
indexing (e.g. an OffsetArray) passes through silently: subsequent
1-based indexing into `t` elsewhere in the package reads the wrong
elements, giving a wrong value instead of an error. Other interpolation
types already throw for this case; ConstantInterpolation does not.

Add `Base.require_one_based_indexing(t)` to all three `munge_data`
methods (vector, matrix, N-d `u`).

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Agent-Harness: Claude Code
Agent-Model: claude-sonnet-5
Agent-Session: https://claude.ai/code/session_01LPHREnnonfLg1VcE1EJovv
@ChrisRackauckas

Copy link
Copy Markdown
Member

🤖 Automated comment from an AI agent running as @ChrisRackauckas — not written or reviewed by Chris.

Independent review (Devin CLI 3000.11.3, model fusion-claude-opus-5-5-high-sidekick-swe-2-medium): CHANGES, risk low.

Full review

VERDICT: CHANGES
RISK: low

The code change is correct. On master I reproduced the silent wrong value, the fix turns it into an ArgumentError, one-based inputs give the same results as before, and Core and QA pass. One small required edit (a comment that breaks the repo rules), plus test and PR-body accuracy items.

Blocking findings

  1. test/interpolation_tests.jl:2219-2221: the comment points at the agent's audit task, and its "link" is fake. (Found by reading.)
    The comment reads: (https://github.com/SciML/DataInterpolations.jl, candidate 6). That is the repo root plus an internal audit label, "candidate 6", which no issue or PR in the repo defines. The PR links no issue at all. CLAUDE.md says to delete comments "referencing the current task", and a citation that leads nowhere is worse than none.
    Change required: delete the parenthetical. Either keep only the first sentence, which explains why munge_data must reject offset t, or drop the comment entirely (the testset name already says it). Sibling PR Reject unsorted t in munge_data for matrix/N-d u #623 has the same "candidate 5" pseudo-link.

Non-blocking findings

  1. The constructor-level assertion uses an offset that already threw an error on master, so it does not pin the silent-wrong-value bug. (Confirmed by running scratch/probe.jl against master and against the PR.)

    • The test uses OffsetArray(t, 1), i.e. indices 2:11. On master, ConstantInterpolation(u, OffsetArray(t, 1)) already throws a BoundsError. The new @test_throws ArgumentError does fail on master, but only because the exception type differs.
    • The silent-wrong case is a negative offset, i.e. indices 0:9, which is what the PR body uses. On master, with that offset:
      • ConstantInterpolation(...)(2.5) returns 0.8415 (reference 0.9093).
      • SmoothedConstantInterpolation returns 0.8415 (reference 0.9093).
      • LinearInterpolation returns 0.8754 (reference 0.5252).
    • The three direct munge_data assertions do pin the fix: on master they return without error. So regression coverage exists, but the end-to-end check misses the case that motivates the PR.
    • Suggest OffsetArray(t, -1) and looping over ConstantInterpolation, SmoothedConstantInterpolation and LinearInterpolation.
  2. The PR body claims only ConstantInterpolation was silently wrong. That is false. (Confirmed by running.)
    The body says: "Most interpolation types happen to throw anyway … but ConstantInterpolation does not." With indices 0:9 on master, LinearInterpolation and SmoothedConstantInterpolation also return wrong values without any error. The fix covers them, since every constructor goes through munge_data, but the body understates the bug.

  3. The body's "failing before" evidence does not match the test. The pasted before/after output uses offset 0:9, while the test uses offset +1 (see 1). The "candidate 6" output block also comes from the agent's own audit harness, not from the test.

  4. u is still not checked for one-based indexing. (Probed.) With offset u, every constructor I tried already throws some error on master and on the PR: ArgumentError, BoundsError or DimensionMismatch, depending on the type. I found no silent wrong value, so this is not a correctness gap. Base.require_one_based_indexing(u, t) would make the error uniform; optional.

  5. Test conflicts with sibling PR Reject unsorted t in munge_data for matrix/N-d u #623. git merge-tree p624 p623 reports a conflict in test/interpolation_tests.jl, because both PRs insert a testset at the same spot before "user error". It is trivial to resolve. Against Reject non-one-based out/tt in sorted-batch itp(out, ts) kernels #622 the merge is clean: both PRs make the same Project.toml and using OffsetArrays changes.

  6. Dependency and API checks found no problems:

    • OffsetArrays (MIT) is a test-only extra with compat "1".
    • Only Base.require_one_based_indexing is used, which the package already uses at src/interpolation_utils.jl:421.
    • No exported names change and there is no NEWS entry. Rejecting input that was already broken is a reasonable patch-level change.

What I ran

  • I built two scratch environments in scratch/: envpr (develops the PR checkout) and envmaster (develops a git archive origin/master copy). Both add OffsetArrays and ForwardDiff, on Julia 1.12.

  • scratch/probe.jl (output in probe_pr.txt and probe_master.txt) checks:

    • the PR's four assertions;
    • 13 constructors with t offset by +1 and by −1, and with u offset by +1 and by −1;
    • an offset matrix U;
    • that one-based t still works: Vector, range, view, OffsetArray(t, 0), for vector, matrix and N-d u;
    • the missing path.

    Results:

    • The PR rejects every offset t with ArgumentError.
    • On master, offset −1 gives wrong values for Constant, SmoothedConstant and Linear. Offset +1 gives BoundsError.
    • One-based results are identical between master and the PR.
    • BSplineInterpolation and BSplineApprox threw MethodError even on one-based input. My probe called them with the wrong signature, so they are not covered.
  • GROUP=Core julia +1.12 --project=. -e 'using Pkg; Pkg.test()' on a git archive HEAD copy of the PR:

    • abstract_interface: 11 passed
    • extrapolation_tests: 316 passed
    • interface: 31 passed
    • interpolation_tests: 17575 passed, 5 broken
    • parameter_tests: 20 passed

    The PR adds no new @test_broken.

  • GROUP=QA on the same copy: alloc_tests 16/16, qa 20/20, passed.

  • julia +1.12 --project=@runic -m Runic --check --diff on both changed files: clean.

  • typos on the changed files: clean.

  • git merge-tree --write-tree of Reject non-one-based t in munge_data #624 against Reject non-one-based out/tt in sorted-batch itp(out, ts) kernels #622 (clean) and against Reject unsorted t in munge_data for matrix/N-d u #623 (conflict in the test file), in a scratch clone.

What I did not verify

  • Julia 1.10 (lts) and pre. I ran only 1.12.
  • The Methods, Misc and Extensions test groups. ChainRules and Mooncake treat munge_data as a rule boundary, so the added check shouldn't affect AD, but I did not run AD tests with this change.
  • BSplineInterpolation and BSplineApprox with offset t (my probe's call signature was wrong). They call munge_data (src/interpolation_caches.jl:1240, 1292, 1340, 1480, 1550, 1622), so by reading they get the same check.
  • The append! paths in src/online.jl, which also call munge_data. I did not probe them.
  • Docs build: the PR touches no docs or docstrings.

🤖 Posted by an AI agent — harness: Devin CLI 3000.11.3 (review), Claude Code 2.1.285 (posting) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Conversation: local Claude session 3c311569-dda6-4687-bc9f-65febb212c33 (fleet master)

This branch has not been deployed

No deployments
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.

2 participants