Skip to content

Fix tuningcurve calculator dealing over a single stimulus property - #1029

Merged
stevevanhooser merged 1 commit into
mainfrom
claude/fix-tuningcurve-single-deal
Oct 15, 2026
Merged

stevevanhooser merged 1 commit into
mainfrom
claude/fix-tuningcurve-single-deal

Conversation

@stevevanhooser

@stevevanhooser stevevanhooser commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Bug

ndi.calc.stimulus.tuningcurve can "deal" a stimulus property (selection(i).value = 'deal'), which makes one tuning curve per value of that property. When exactly one property was dealt, the deal loop set the index to 1 on every iteration:

if numel(deal_constraints_dim)==1
    eval(['[' deal_str ']=1;']);
else
    eval(['[' deal_str ']=ind2sub(deal_constraints_dim,i);']);
end

So the calculator still produced N_deal curves, but every one was built at the first value of the dealt property.

History

Commit dcf9952 ("small changes for Nielsen", 2025-04-15) added this single-property branch. It replaced the unconditional eval(['[' deal_str ']=ind2sub(deal_constraints_dim,i);']); from 60e0da8 (2023-10-16). Before dcf9952, dealing over one property gave one curve per value.

Any tuning curve made with exactly one dealt property between dcf9952 and this fix was built at the first value of that property, and needs to be recomputed. Calculations with no dealt property, or with two or more, are unaffected.

Evidence (real data)

One stimulus presentation had 8 spatial frequencies (0.04 to 1.25 cpd) × 6 contrasts × 4 angles. Contrast tuning curves dealt over sFrequency came out as 8 identical curves: label Contrast, values [0.04 0.08 0.16 0.32 0.64 1], stimid [97;98;99;100;101;102], all at sFrequency 0.04. Downstream, ndi.calc.vis.contrast_sensitivity (VH-Lab/NDIcalc-vis-matlab) then reported spatial_frequencies as [0.04 x 8].

Changes

  • src/ndi/+ndi/+calc/+stimulus/tuningcurve.m
    • Single-property branch: now sz_{1} = i; (deal_str is 'sz_{1}' in this case, so this is the same as using the loop index). With no dealt property, deal_constraints{1} = 1 and N_deal = 1, so i is always 1 and nothing changes.
    • The deal log line now builds on deal_log_str instead of restarting from deal_str (the 'sz_{1},...' eval string). Each curve's log now lists every property dealt for it.
  • tests/+ndi/+unittest/+calc/+stimulus/testCalcTuningCurve.m: new testDealSingleProperty. It uses ndi.mock.fun.stimulus_response to build a mock 3 contrast × 4 spatial_frequency stimulus set. It then runs the calculator for contrast tuning with spatial_frequency dealt and checks that:
    • there are 4 curves
    • each curve's tuningcurve_calc.stim_property_list.values is different, and together they match the dealt values
    • each log contains dealing spatial_frequency = <value>
    • every stimid in each curve points at a stimulus with that curve's spatial frequency

Testing

MATLAB was not available where this change was written (see AGENTS.md), so it was not run locally. In CI on 49a875c, "Run tests (MATLAB latest)" passed: 1556 tests, +1 vs base (the new testDealSingleProperty), 0 failures. I did not run the new test against the unfixed code to confirm it fails there.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F7s7BWxMrHf4PtsMiWS3sx

When exactly one property was dealt, the deal index was set to 1 on
every iteration, so all N_deal tuning curves were built at the first
value of the dealt property. Use the loop index instead. Also
accumulate deal_log_str rather than restarting it from deal_str.

Adds testDealSingleProperty to testCalcTuningCurve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F7s7BWxMrHf4PtsMiWS3sx
@github-actions

Copy link
Copy Markdown
Contributor

Test Results

1 556 tests  +1   1 523 ✅ +1   9m 24s ⏱️ -29s
  223 suites ±0      33 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 49a875c. ± Comparison against base commit 695e8d8.

@codecov

codecov Bot commented Oct 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 31.66%. Comparing base (695e8d8) to head (49a875c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1029      +/-   ##
==========================================
+ Coverage   31.63%   31.66%   +0.02%     
==========================================
  Files         898      898              
  Lines       46907    46907              
==========================================
+ Hits        14841    14855      +14     
+ Misses      32066    32052      -14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@stevevanhooser
stevevanhooser merged commit 082e688 into main Oct 15, 2026
12 checks passed
@stevevanhooser
stevevanhooser deleted the claude/fix-tuningcurve-single-deal branch October 15, 2026 00:27
stevevanhooser pushed a commit to Waltham-Data-Science/NDI-python that referenced this pull request Oct 15, 2026
tuningcurve: NDI-matlab 49a875c94 (VH-Lab/NDI-matlab#1029) fixed the
single-property deal index in calculate; the Python calculate is still a
stub with no deal loop, so nothing to port. Recorded in decision_log.

downloadDocumentCollection: NDI-matlab 2669a8525 (the paired
claude/download-timeout-60s branch) raises the default Timeout to 60 s,
which this PR ports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T41FUDkgY9DAstG7LzZHaV
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