VV: Multi-Threshold Objects - #1688
Conversation
|
Full V&V review of this PR: every claim in the V&V documents was re-verified against head Independent A/B validation (run for this review)Shared legacy-format input (100 tuples,
Same three configs at 50M random tuples: this branch matches the oracle exactly. All 17 ctest entries pass locally. The same pipelines run against Also confirmed from legacy source: Actionable itemsCorrectness / behavior
V&V document corrections (each verified against head)
CPU / memoryMeasured on 50M tuples (3 threshold filters per pipeline, Release): develop 18.2s / 774MB peak RSS → this PR 21.9s / 861MB. Filter-only time roughly +50%; peak temp usage is now (nesting depth + 2) full-size stores vs 1
Test quality / cleanup
Verified good
|
909bff1 to
f722bb9
Compare
|
Re-review at head Most of the last round is genuinely fixed — see "Resolved" at the bottom. Three items below are new or newly-quantified and should block; the rest are carry-overs and nits. Blocking
Carry-overs still open from the last round
New nits
Highest-value remaining addition
Resolved since the last reviewCancel check restored ( Environmental note, unrelated to this PR: a stale |
imikejackson
left a comment
There was a problem hiding this comment.
Inline anchors for blocking items B1-B3 from the re-review comment above.
|
|
||
| **3 deviations documented: 2 bugs (both in SIMPLNX, both fixed pre-branch), 1 confirmed non-bug capability difference.** Legacy comparison has been **run**: an independent three-way A/B — DREAM3D 6.5.171 `PipelineRunner`, this branch's `nxrunner`, and an independent numpy oracle — on a shared 100-tuple fixture, covering representative flat (`Threshold Objects`), nested, and inverted-nested (`Threshold Objects (Advanced)`) configurations, re-run again at 50M tuples. Post-fix, all three sources MATCH in every case at both scales. All 17 in-repo ctest entries also pass locally. | ||
|
|
||
| The same three pipelines run against `develop` (pre-fix) reproduce two real bugs quantitatively: `MultiThresholdObjectsFilter-D1` (38/100 tuples wrong) and `MultiThresholdObjectsFilter-D2` (51/100 tuples wrong). Both are fixed by commit `25f1986f1` ("Fixed MultiThresholdObjects ThresholdSets algorithm", 2026-04-23), which predates this V&V pass. Neither has a dedicated regression test in the in-repo `TEST_CASE` suite yet (see the V&V report's Code path coverage row 25 and Test inventory "Missing" note) — status should not promote past DRAFT until at least D1's trigger shape has one. |
There was a problem hiding this comment.
B1 (blocking) — this commit reference is unreachable and the timeline is wrong.
25f1986f1 is an ancestor of neither develop nor this PR head; it is the pre-rebase object, now dangling:
git merge-base --is-ancestor 25f1986f1 upstream/develop -> NO
git merge-base --is-ancestor 25f1986f1 c563a3764 -> NO
git patch-id: 25f1986f1 == 6d137ff30 (6d137ff30 is on this branch, NOT on develop)
So the D1/D2 fix does not predate this V&V pass — it ships with this PR, and has never been in a release. That also makes "anyone on a pre-25f1986f1 build should upgrade" (D1 and D2 Recommendation sections) unactionable for a user.
Please reword to "fixed by this PR" (or cite the post-merge SHA at deliverable assembly time), change the D1/D2 Status fields off "retired 2026-04-23 … prior to this V&V pass", and phrase the upgrade advice as "any release prior to the one containing this PR". Same wording appears in the report's Bug flags row and Summary. The report's Verified commit header field is also still a placeholder.
| for(usize i = 0; i < k_TupleCount; i++) | ||
| { | ||
| if(i < 10) | ||
| if(i < 5) |
There was a problem hiding this comment.
B2 (blocking) — this makes Valid Execution, Mask DataType (10 SECTIONs, ctest #568) assert nothing about the filter's output.
This PR reduced k_TupleCount 20 -> 5, but the split here is still the hardcoded 5 from the old 20-tuple fixture, so the loop runs i = 0..4 and the else branch (the only place a TRUE value is checked) is unreachable. The float fixture is 0.01…0.05 compared against > 0.05, so the expected mask is all-false — and the mask array is already zero-initialized at creation.
Net effect: the test cannot fail on any output defect. Confirmed by stubbing MultiThresholdObjects::operator()() to return {}; — every other valid-execution test fails, and #568 still passes. On develop this test was meaningful (20 tuples, 10 false / 10 true).
Fix: pick a comparison value that actually splits the 5-tuple fixture, and derive the split from k_TupleCount instead of a literal. The comment two lines up still describes the old 20-tuple / 0.1 fixture and needs updating too. Worth noting the V&V report cites this test as the only coverage for code-path row 22 (mask DataType dispatch).
| { | ||
| size_t numTuples = m_Input.getNumberOfTuples(); | ||
| size_t numTuples = inputStore.getNumberOfTuples(); | ||
| T value = static_cast<T>(m_ComparisonValue); |
There was a problem hiding this comment.
B3 (blocking) — the analytical oracle disagrees with this line, and the fixture size hides it.
The comparison value is truncated to the input array's type here, so a threshold of 5.5 becomes 5 for an int32 array. ExpectedIntSingleComponentMask in the test file instead compares in double. Valid Single Thresholds: Int GENERATEs 5.5 precisely to probe this, but with k_TupleCount = 5 the array only holds 0…4, so the one divergent element (value 5) never exists.
Confirmed by bumping k_TupleCount to 8 — Valid Single Thresholds: Int then fails for <, ==, and != at threshold 5.5, e.g. 5 == 5.5 is true for the filter (cast to 5) and false for the oracle.
Legacy performs the identical cast (SIMPL/Source/SIMPLib/Filtering/ThresholdFilterHelper.h:60), so this line is correct and the oracle helper is what's wrong. Please model the truncation in the Expected*Mask helpers and record comparison-value truncation as verified, legacy-matching behavior in the report's Oracle section — otherwise any later increase in fixture size will present as a regression.
* Updated MultiThresholdObjects algorithm to account for the IsInverted state allowed by individual thresholds. * WIP: Replacing unit tests with smaller datasets and standardized testing functions. Integer and floating point single component DataArrays are tested for all comparison types and inversion states using a single threshold. Multicomponent arrays are in the process of being tested and the filter was updated for assumptions that may be wrong. The documentation and GUI need to be referenced before moving forward. Multicomponent threshold tests and threshold creation will likely need to be adjusted based on new information. * TODO: Create tests for entire threshold sets and even nested sets.
* Mask array is always 1 component. * Update unit tests for multicomponent array thresholds
* Consolidated unit tests of the same array type and component count using GENERATE. * Added additional value checks. * All single threshold tests pass.
* Removed requirement for input arrays to all have the same number of components, Each threshold specifies the target component.
* MultiThresholdObjects no longer writes directly to the DataStore when running Thresholds. Instead Sets and Thresholds both store temporary vectors that are copied to the parent set's vector. The topmost ThresholdSet copies the vector to the DataStore upon completion. * Added ThresholdSet unit tests.
* Standardized apply threshold values between thresholds and sets. * Removed unnecessary inversion parameter in threshold and set algorithm
* Re-enabled unit test without the Mismatched components section. That case is no longer an error.
* Added function documentation for ApplyThresholdValues * Simplified InsertThreshold parameters. * Deleted unused ThresholdValueFunctor struct.
* Re-enabled invalid execution and mask DataType unit tests and updated for new tuple counts. * Simplified mask DataType unit tests to remove duplicated code. * Removed unused legacy unit tests.
* Converted std::vector data to AbstractDataStore<T> using DataStoreUtilities.
Removed stale testing constant.
* Optimized Mask calculations by only applying / checking trueValue and falseValue once the entire mask value has been calculated. All internal checks use an AbstractDataStore<bool> instead of AbstractDataStore<MaskType>. * Re-added TEMPLATE_TEST_CASE Valid Execution - Custom Values and SIMPL Backwards Compatibility unit tests * Reduced memory usage for thresholdValue
c563a37 to
567048b
Compare
Naming Conventions
Naming of variables should descriptive where needed. Loop Control Variables can use
iif warranted. Most of these conventions are enforced through the clang-tidy and clang-format configuration files. See the filesimplnx/docs/Code_Style_Guide.mdfor a more in depth explanation.Filter Checklist
The help file
simplnx/docs/Porting_Filters.mdhas documentation to help you port or write new filters. At the top is a nice checklist of items that should be noted when porting a filter.Unit Testing
The idea of unit testing is to test the filter for proper execution and error handling. How many variations on a unit test each filter needs is entirely dependent on what the filter is doing. Generally, the variations can fall into a few categories:
Code Cleanup