Skip to content

[OpenVINO backend] solve_triangular implementation for OpenVINO backend#22588

Open
satheeshbhukya wants to merge 1 commit intokeras-team:masterfrom
satheeshbhukya:openvino-backend.st
Open

[OpenVINO backend] solve_triangular implementation for OpenVINO backend#22588
satheeshbhukya wants to merge 1 commit intokeras-team:masterfrom
satheeshbhukya:openvino-backend.st

Conversation

@satheeshbhukya
Copy link
Copy Markdown
Contributor

@satheeshbhukya satheeshbhukya commented Mar 28, 2026

Summary

This PR adds support for solve_triangular (solving the linear system AX = B where A is a triangular matrix) in the OpenVINO backend.

Implementation

The operation is implemented using matrix inversion and multiplication, built entirely with OpenVINO ops.

The main steps are:

  1. Build a boolean index mask using range + greater_equal / less_equal comparisons to zero out the unwanted triangle of A, depending on the lower flag.
  2. Apply the mask via elementwise multiply to enforce the triangular structure before inversion.
  3. Invert the masked triangular matrix using ov_opset.inverse.
  4. Compute the solution as A⁻¹ @ B using ov_opset.matmul.
  5. Handle 1D b inputs by unsqueezing to a column vector before the solve and squeezing back on output, matching NumPy/SciPy behavior.
  6. Temporarily cast f64 inputs to f32 for compatibility with OpenVINO's inverse op, then cast the result back to f64.

closes: openvinotoolkit/openvino/issues/#35002

cc @hertschuh for review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the solve_triangular operation for the OpenVINO backend and enables the corresponding correctness tests. The implementation handles input conversion, rank alignment, and uses a masking strategy to ensure the matrix is triangular before applying matrix inversion and multiplication. Review feedback highlights the need for documentation regarding the precision loss caused by downcasting f64 to f32 and the numerical stability and performance trade-offs associated with using matrix inversion instead of standard substitution methods.

Comment on lines +957 to +959
if orig_type == Type.f64:
a_ov = ov_opset.convert(a_ov, Type.f32).output(0)
b_ov = ov_opset.convert(b_ov, Type.f32).output(0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The downcasting from f64 to f32 here introduces a loss of precision. While the PR description explains this is a workaround for ov_opset.inverse lacking f64 support, this critical information should be captured in a code comment for future maintainers.

Please add a comment explaining why this conversion is necessary. For example:

# `ov_opset.inverse` does not support f64, so we downcast to f32
# and cast back the result later.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

please address this

Comment on lines +997 to +998
a_inv = ov_opset.inverse(a_ov, adjoint=False).output(0)
result = ov_opset.matmul(a_inv, b_ov, False, False).output(0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using matrix inversion followed by matrix multiplication to solve a triangular system is generally less numerically stable and less performant (O(n³) vs. O(n²)) than using forward or backward substitution.

If OpenVINO does not provide a more direct way to solve triangular systems (e.g., via a substitution op), this implementation is a reasonable workaround. However, this trade-off is significant, especially since the inputs are already downcast from f64 to f32, which can further impact numerical accuracy.

Consider adding a comment here to document this implementation choice and its potential drawbacks for future reference.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 28, 2026

Codecov Report

❌ Patch coverage is 77.41935% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.29%. Comparing base (8a941a4) to head (b5feff4).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
keras/src/backend/openvino/linalg.py 77.41% 3 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #22588      +/-   ##
==========================================
- Coverage   83.30%   83.29%   -0.01%     
==========================================
  Files         596      596              
  Lines       67951    67982      +31     
  Branches    10577    10582       +5     
==========================================
+ Hits        56604    56628      +24     
- Misses       8600     8603       +3     
- Partials     2747     2751       +4     
Flag Coverage Δ
keras 83.10% <77.41%> (-0.01%) ⬇️
keras-jax 59.69% <0.00%> (-0.03%) ⬇️
keras-numpy 54.32% <0.00%> (-0.03%) ⬇️
keras-openvino 51.81% <77.41%> (+0.02%) ⬆️
keras-tensorflow 61.01% <0.00%> (-0.03%) ⬇️
keras-torch 59.89% <0.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Keras 3 OpenVINO backend] Implement solve_triangular for triangular linear system solving

5 participants