Skip to content

Added handling for LinalgError and fill_value for failed pixels#674

Merged
pgbrodrick merged 3 commits into
isofit:devfrom
evan-greenbrg:bugs/linalg
May 5, 2025
Merged

Added handling for LinalgError and fill_value for failed pixels#674
pgbrodrick merged 3 commits into
isofit:devfrom
evan-greenbrg:bugs/linalg

Conversation

@evan-greenbrg

Copy link
Copy Markdown
Collaborator

We hit a strange error case within the invert_analytical routine. Certain pixels were failing within the Sa matrix inversion block, which were accurately caught as failed pixels. We looked into why they were failing, and it turned out to be more complicated that we initially thought.

Rather than the matrix containing any NaNs, the matrix was correctly formed, but the scipy.linalg.eigh function failed with error: LinAlgError: Internal Error. There is some similarity and a chance this error follows: scipy/scipy#13220. The eigh even succeeds on the transpose of the matrix when the only difference is precision.

For now the work around is to explicitly handle this error within the analytical line.

This PR also includes an addition of a fill value for failed pixels within the analytical line. Rather than returning the result of invert_algebraic, failed pixels will now return fill (-9999 by default).

@pgbrodrick pgbrodrick requested a review from jammont May 1, 2025 19:19
Comment thread isofit/inversion/inverse_simple.py Outdated
Sa_surface = Sa[fm.idx_surface, :][:, fm.idx_surface]
Sa_inv = svd_inv_sqrt(Sa_surface, hash_table, hash_size)[0]

except np.linalg.LinAlgError:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it was requested in the tagup meeting how to avoid duplicating code, there's two ways you can merge this together:

  1. Use a dict to retrieve the error code for the respective exception
errorCodes = {
    np.linalg.LinAlgError: -15
    ValueError: -11
}
try:
    ...
except Exception as e:
    C_rcond = []
    trajectory[n + 1, :] = [fill_value for i in x]
    EXIT_CODE = errorCodes[e]
  1. Use isinstance
try:
    ...
except Exception as e:
    C_rcond = []
    trajectory[n + 1, :] = [fill_value for i in x]
    if isinstance(e, np.linalg.LinAlgError):
        EXIT_CODE = -15
    elif isinstance(e, ValueError):
        EXIT_CODE = -11

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, if you'd rather catch specifically those exceptions and no others so others could raise naturally, you can just change to:

except (np.linalg.LinAlgError, ValueError) as e:

Comment thread isofit/inversion/inverse_simple.py Outdated
# On invertible matrix error, save NaN array on step and update exit code
C_rcond = []
trajectory[n + 1, :] = x
trajectory[n + 1, :] = [fill_value for i in x]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super minor, and I'm not certain the shape of your x necessarily or how impactful this could be, but you could potentially save likely a marginal amount of time:

fill_value = 7
x = np.arange(1_000)

a = [fill_value] * len(x)
b = [fill_value for i in x]
assert a == b

%timeit [fill_value] * len(x)
2.24 µs ± 21.8 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

%timeit [fill_value for i in x]
29.3 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's silly I didn't consider np.full, which is even faster

%timeit np.full(len(x), fill_value)
819 ns ± 8.17 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

(sorry, this isn't really necessary just fun to consider)

@evan-greenbrg

Copy link
Copy Markdown
Collaborator Author

Sweet! Thanks so much for the comments @jammont I made the change to a shared exception block (Linalg and ValueError exclusive). Changing to the faster list fill is also a no brainer.

@pgbrodrick pgbrodrick merged commit 74510e3 into isofit:dev May 5, 2025
17 checks passed
@evan-greenbrg evan-greenbrg deleted the bugs/linalg branch May 22, 2026 15:32
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