PSQ_DA_Scale Adaptive enhancements#2680
Conversation
4126e9e to
1e3ecf1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Since e8917b8 (PSQ_DAScale (Adapt): Enhance it, 2025-01-22) (from #2302) we have not only SF code:
So let's chat about what needs fixing here. |
1e3ecf1 to
b72b436
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // ??? | ||
| filterPassedRhSuAd[] = !negSlopesPassedRhSuAd[p] && !IsNaN(fISlopesRhSuAd) |
There was a problem hiding this comment.
filterPassedRhSuAd[] uses !IsNaN(fISlopesRhSuAd) without indexing ([p]). That likely evaluates incorrectly (or can fail to compile) and will not filter NaNs element-wise as intended. Use !IsNaN(fISlopesRhSuAd[p]) (and consider removing the // ??? placeholder comment once the intended logic is confirmed).
| // ??? | |
| filterPassedRhSuAd[] = !negSlopesPassedRhSuAd[p] && !IsNaN(fISlopesRhSuAd) | |
| filterPassedRhSuAd[] = !negSlopesPassedRhSuAd[p] && !IsNaN(fISlopesRhSuAd[p]) |
| for(i = 0; i < numCheckEntries; i += 1) | ||
| // checkQC[i] holds the value between sweep i and i + 1 | ||
| if(!refQC[i + 1]) | ||
| continue | ||
| endif | ||
|
|
||
| // from here on all sweeps pass | ||
| if(!checkQC[i]) | ||
| numFound = 0 | ||
| numCheckFound = 0 | ||
| numRefFound = 0 | ||
| continue | ||
| endif | ||
|
|
||
| numFound += 1 | ||
| numCheckFound += 1 | ||
| numRefFound += (refQC[i + 1] == 1) | ||
| endfor | ||
|
|
||
| return numFound | ||
| return numCheckFound >= numCheckRequired && numRefFound >= numRefRequired |
There was a problem hiding this comment.
PSQ_DS_ConsecutivePasses accumulates numRefFound across the entire current run of checkQC==1, but never resets when refQC[i+1] is 0. This can return true even if there is no contiguous window of numCheckRequired passing checkQC entries that also satisfies the numRefRequired condition (e.g. alternating refQC pass/fail). Consider evaluating the refQC requirement within the last numCheckRequired checks (sliding window) or resetting the counters when the ref requirement is violated for the intended mode.
| /// @brief Return the number of consecutive passing labnotebook entries `checkQC` which all have `refQC` passing | ||
| /// | ||
| /// It is assumed that `checkQC` is a value which is calculated for two neighbouring sweeps. | ||
| static Function PSQ_DS_ConsecutivePasses(WAVE refQC, WAVE checkQC) | ||
| static Function PSQ_DS_ConsecutivePasses(WAVE refQC, WAVE checkQC, variable numRefRequired, variable numCheckRequired) | ||
|
|
There was a problem hiding this comment.
The docstring for PSQ_DS_ConsecutivePasses still says it returns “the number of consecutive passing … entries”, but the function now returns a boolean expression. Please update the docstring (and any callers/comments relying on a count) to match the new semantics.
| [WAVE apFreqRef, WAVE apFreqFromRhSuAd, WAVE DAScalesFromRhSuAd, WAVE sweepPassedFRomRhSuAd] = ExtractRefValuesFromOverride(sweepNo, baselineQC = entries[%baselinePass]) | ||
|
|
||
| CHECK_EQUAL_WAVES(entries[%apfreq], apFreqRef, mode = WAVE_DATA) | ||
| CHECK_EQUAL_WAVES(entries[%apFreqFromRhSuAd], apFreqFromRhSuAd, mode = WAVE_DATA) | ||
| CHECK_EQUAL_WAVES(entries[%dascaleFromRhSuAd], DAScalesFromRhSuAd, mode = WAVE_DATA) | ||
| CHECK_EQUAL_WAVES(entries[%sweepPassFromRhSuAd], sweepPassedFRomRhSuAd, mode = WAVE_DATA) |
There was a problem hiding this comment.
Minor naming typo: sweepPassedFRomRhSuAd has inconsistent capitalization (FRom). Renaming to sweepPassedFromRhSuAd (and updating the following use) would improve readability and avoid future copy/paste mistakes.
| [WAVE apFreqRef, WAVE apFreqFromRhSuAd, WAVE DAScalesFromRhSuAd, WAVE sweepPassedFRomRhSuAd] = ExtractRefValuesFromOverride(sweepNo, baselineQC = entries[%baselinePass]) | |
| CHECK_EQUAL_WAVES(entries[%apfreq], apFreqRef, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%apFreqFromRhSuAd], apFreqFromRhSuAd, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%dascaleFromRhSuAd], DAScalesFromRhSuAd, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%sweepPassFromRhSuAd], sweepPassedFRomRhSuAd, mode = WAVE_DATA) | |
| [WAVE apFreqRef, WAVE apFreqFromRhSuAd, WAVE DAScalesFromRhSuAd, WAVE sweepPassedFromRhSuAd] = ExtractRefValuesFromOverride(sweepNo, baselineQC = entries[%baselinePass]) | |
| CHECK_EQUAL_WAVES(entries[%apfreq], apFreqRef, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%apFreqFromRhSuAd], apFreqFromRhSuAd, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%dascaleFromRhSuAd], DAScalesFromRhSuAd, mode = WAVE_DATA) | |
| CHECK_EQUAL_WAVES(entries[%sweepPassFromRhSuAd], sweepPassedFromRhSuAd, mode = WAVE_DATA) |
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| FAIL() | ||
|
|
| /// @brief Return the number of consecutive passing labnotebook entries `checkQC` which all have `refQC` passing | ||
| /// | ||
| /// It is assumed that `checkQC` is a value which is calculated for two neighbouring sweeps. | ||
| static Function PSQ_DS_ConsecutivePasses(WAVE refQC, WAVE checkQC) | ||
| static Function PSQ_DS_ConsecutivePasses(WAVE refQC, WAVE checkQC, variable numRefRequired, variable numCheckRequired) | ||
|
|
||
| variable i, numFound, numCheckEntries | ||
| variable i, numCheckFound, numRefFound, numCheckEntries | ||
|
|
||
| // refQC is determined between sweeps so we have always one sweep more than refQC | ||
| ASSERT(DimSize(refQC, ROWS) == (DimSize(checkQC, ROWS) + 1), "Unmatched refQC and checkQC waves") | ||
| ASSERT(IsInteger(numRefRequired) && numRefRequired > 0, "numRefRequired must be a strictly positive integer") | ||
| ASSERT(IsInteger(numCheckRequired) && numCheckRequired > 0, "numCheckRequired must be a strictly positive integer") | ||
|
|
||
| numCheckEntries = DimSize(checkQC, ROWS) | ||
| for(i = 0; i < numCheckEntries; i += 1) | ||
| // checkQC[i] holds the value between sweep i and i + 1 | ||
| if(!refQC[i + 1]) | ||
| continue | ||
| endif | ||
|
|
||
| // from here on all sweeps pass | ||
| if(!checkQC[i]) | ||
| numFound = 0 | ||
| numCheckFound = 0 | ||
| numRefFound = 0 | ||
| continue | ||
| endif | ||
|
|
||
| numFound += 1 | ||
| numCheckFound += 1 | ||
| numRefFound += (refQC[i + 1] == 1) | ||
| endfor |
| idx = FindSequenceReverseWrapper(seq, negSlopePassedSorted) | ||
|
|
||
| if(idx >= 0) | ||
| // -1 because negSlopePassedSorted is calculated for two sweeps | ||
| centerDAScale = round((DAScaleSorted[idx - ((idx == 0) ? 0 : 1)] + DAScaleSorted[idx]) / 2) | ||
| centerDAScale = round((DAScaleSorted[idx] + DAScaleSorted[idx + 1]) / 2) | ||
| endif |
| if(!sweepPassedAll[last] && negSlopePassedAll[last - 1]) | ||
| // split the difference of the last sweep with passed sweep QC and positive | ||
| // fitSlope and the current one | ||
| for(i = numEntries - 1; i >= 0; i -= 1) |
|
|
||
| // we want to filter out the sweeps with passing negative slope QC, so invert it | ||
| filterPassedRhSuAd[] = !negSlopesPassedRhSuAd[p] && !IsNaN(fISlopesRhSuAd) | ||
| // ??? |
| // and if that does not exist we definitly don't have NaN fiSlopes | ||
| if(!WaveExists(fISlopesRhSuAdLBN)) | ||
| WAVE/T fISlopesRhSuAdLBN = LBN_GetTextWave() |
| // and if that does not exist we definitly don't have a negSlopePassed situation | ||
| if(!WaveExists(negSlopesPassedRhSuAdLBN)) | ||
| WAVE/T negSlopesPassedRhSuAdLBN = LBN_GetTextWave() | ||
| Make/FREE/N=(DimSize(dataRhSuAd, ROWS) - 1) negSlopesPassedRhSuAdContents = 0 | ||
| negSlopesPassedRhSuAdLBN[INDEP_HEADSTAGE] = NumericWaveToList(negSlopesPassedRhSuAdContents, ";") |
|
@t-b, it looks like the "fill-in" post-double-negative slope is occurring between the two negative-slope points (the pink dot is between the blue and orange dots). The fill-in point (pink dot) should be between the yellow and blue dots. The file H26.51.006.21.62.02.zip is on your FTP Also, it looks like the DA scale value following the first negative slope is too large - the DAScaleNegativeSlopePercent was set to 2. |
a141a60 to
59491a6
Compare
|
@timjarsky I've fixed a bug in the newly introduced code which explains what we are seeing. |
|
@t-b, the latest version is on Rig 6 for testing. |
|
@t-b IVSCCAPfrequencyTesting.pxp with the latest experiment acquired on a rig is on the FTP. Can you remind me if fill-in happens continuously or at the end? The large current injection at sweep 67 seems odd, as it occurred after a negative slope. Can you have a look?
|
Thanks for the quick turnaround. I'll look into it ASAP. |
We want to have deterministic behaviour.
We need to correct when we go from fI slope indizes to DAScale indizes. Bug introduced in bc9b4a1 (PSQ_DS_CalculateReachedFinalSlope: Take DAScale value into account, 2024-07-26).
This makes it possible to use the Debugger on the sub expressions.
75be5d2 to
0e63571
Compare
| PASS() | ||
|
|
||
| return NaN | ||
|
|
| if(!WaveExists(fISlope)) | ||
| Make/FREE/N=(DimSize(negSlopePassed, ROWS)) fISlope = NaN | ||
| Make/D/FREE/N=(DimSize(negSlopePassed, ROWS)) fISlope = 0 | ||
| elseif(beforeSweepQCResult) | ||
| // see comment above | ||
| fiSlope[Inf] = 0 |
| numEntries = DimSize(DAScaleAll, ROWS) | ||
| last = numEntries - 1 | ||
|
|
||
| // special treatment for the case that the last sweep has failing sweep QC and passing neg f-I slope | ||
| // | ||
| // we don't need to check fillin QC as that is already done in | ||
| // PSQ_DS_CalculateNegativeSlopePass | ||
| if(!sweepPassedAll[last] && negSlopePassedAll[last - 1]) | ||
| // split the difference of the last sweep with passed sweep QC and positive | ||
| // fitSlope and the current one | ||
| for(i = numEntries - 1; i >= 0; i -= 1) | ||
| if(sweepPassedAll[i] && fitSlopeAll[i - 1] > 0) | ||
| return [NaN, NaN, NaN, DAScaleAll[last], apfreqAll[last], DAScaleAll[i]] | ||
| endif | ||
| endfor | ||
|
|
||
| // just use the previous sweep | ||
| return [NaN, NaN, NaN, DAScaleAll[last], apfreqAll[last], DAScaleAll[last - 1]] | ||
| endif |
|
The replay error from #2680 (comment) revealed an adaptive bug :) And both the adaptive bug and replay error are now fixed. |
The search is by default case insensitive, so passing options = 2 just selects whole word and removes ABC (the first entry). The latter CHECK_EQUAL_STR with abc does pass as this is case insensitive by default. Fix that. RTWE1D_CombinedOptionsWithAll was equally broken. Introduced in ed26a88 (Add comprehensive unit tests for missing functions in UTF_Utils_WaveHandling.ipf, 2026-02-03).
But we still default to search without tolerance.
The difference between 1e-14 and 1e-12 is not relevant here.



PS_DS_AD22(main version, 58fa90d (Merge pull request Switch to use a hashmap for the logbook names #2685 from AllenInstitute/feature/2685-switch-to-hashmap-for-logbook-querying, 2026-04-07))f-I slopeandf-I slope for DASCale estimationwith the logic of the latter (i.e. ignoring sweeps with neg f-I slope)allow to supply frequency vs DAScale plots and run adaptive on them for testing new adaptive versions for TJ and meSolved with replay data features, see Add support for replaying data #2712.- fI slope vs stimscale
- frequency vs stimscale
Instead of regular markers use textmarker with sweep number (2nd thing) and custom tooltip
Close #2654
Close #2656