Skip to content

Shared parseCommonInputs utility -- scoped proof-of-concept for #378 - #772

Open
aaravkharwar-hub wants to merge 4 commits into
CERN:masterfrom
aaravkharwar-hub:parse-inputs-refactor
Open

aaravkharwar-hub wants to merge 4 commits into
CERN:masterfrom
aaravkharwar-hub:parse-inputs-refactor

Conversation

@aaravkharwar-hub

Copy link
Copy Markdown

Addresses #378 -- but scoped intentionally, and I'd like your input on direction before going further.

What this is

parse_inputs() is duplicated across all 26 algorithm files, and most of that duplication is identical: init/initimg, verbose, qualmeas, gpuids, groundtruth. This PR extracts that shared portion into MATLAB/Utilities/parseCommonInputs.m, and migrates two algorithms (CGLS and SIRT) as a proof of concept, rather than attempting all 26 at once.

Each migrated algorithm's own parse_inputs now just calls parseCommonInputs for the common options, then parses its own remaining algorithm-specific options (e.g. CGLS's restart; SIRT's lambda, lambda_red, nonneg, redundancy_weighting) from the leftover argument list, exactly as before.

Why only 2 algorithms, not all 26

This touches option-parsing for real reconstruction algorithms that people run on real scan data, so I didn't want to blind-rewrite all 26 in one pass without your sign-off on the approach. A few things came up even in just these two that I think are worth your eyes before scaling this up:

  1. A bug found and fixed: ind=double.empty(0,1) (present in every algorithm's parse_inputs) relies on MATLAB's classdef static-method syntax and isn't portable (fails under GNU Octave, for instance). Replaced with ind=[] -- identical behavior, more portable. I did NOT change this in the other 24 files, only the 2 migrated here.
  2. A design subtlety: init_multigrid is not a shared utility -- it's duplicated as a local (file-private) function inside several algorithms (SART, SIRT, OSEM, OS_SART, SART_TV), each only visible within its own file. A shared parser calling init_multigrid(...) directly would silently break any algorithm whose own copy isn't globally visible. parseCommonInputs takes an optional function handle for this (multigridFn) so each algorithm can pass its own local implementation; SIRT does this (@init_multigrid, created inside SIRT.m so it correctly captures SIRT's own local function). CGLS doesn't have its own init_multigrid at all today (calling 'Init','multigrid' on CGLS already errors with "undefined function" before this PR) -- that's a pre-existing gap, unrelated to this refactor, and I've left it alone.
  3. A preserved (not fixed) quirk: if 'init','image' is passed without 'initimg', every existing algorithm silently leaves the result empty rather than erroring. I kept this exact behavior in parseCommonInputs rather than "fixing" it, since this PR is meant to be structural only. Flagging it here in case it's worth its own issue.

Testing

I don't have a MATLAB license in my environment, so I used GNU Octave to actually execute the code rather than just writing it and hoping:

  • Unit-tested parseCommonInputs directly (defaults, every option, error paths, the preserved quirks above).
  • Side-by-side equivalence tests: ran the original and migrated parse_inputs for both CGLS and SIRT against 12-14 cases each and diffed every output field -- all matched, including error identifiers.
  • A targeted test confirming the multigridFn handle actually resolves to the caller's own local function and not some other init_multigrid, using a deliberately "wrong" decoy to make sure.

I have not been able to verify against real MATLAB specifically, only Octave -- flagging that as a limitation.

If this approach looks right to you, I'm happy to continue migrating the remaining 24 algorithms in follow-up PRs. If you'd rather see a different shape (e.g. a struct-based options object instead of a leftover-args split, or algorithm name dispatch as you originally suggested), let me know before I put more algorithms through this pattern.

This function parses common input options for TIGRE algorithms, handling initialization, verbosity, quality measures, GPU IDs, and ground truth parameters.
Common options (init/initimg, verbose, qualmeas, gpuids, groundtruth) now come from parseCommonInputs; CGLS's own parse_inputs only handles 'restart'. Verified against the original implementation with a side-by-side equivalence test (12 cases including error paths) before pushing.
init_multigrid is a local (file-private) function duplicated inside SART/SIRT/OSEM/etc, not a shared utility -- so parseCommonInputs calling it directly would break algorithms whose own local copy isn't visible from this file. This lets each algorithm pass a handle to its own local init_multigrid, defaulting to a bare @init_multigrid for algorithms (like CGLS) that don't have one, preserving each algorithm's existing behavior exactly. Verified the handle correctly resolves to the caller's local function vs. a decoy global one before pushing.
Common options now come from parseCommonInputs; SIRT's own parse_inputs only handles lambda/lambda_red/nonneg/redundancy_weighting. Passes SIRT's own local init_multigrid as a handle (@init_multigrid) since that function is file-private, not a shared utility -- important so 'init','multigrid' keeps working exactly as before. Verified via a side-by-side equivalence test (14 cases including error paths) plus a targeted test confirming the handle resolves to SIRT's own local function rather than any other init_multigrid.
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.

1 participant