Skip to content

Use lapack func instead of scipy.linalg.cholesky #1487

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 4 commits into from
Jun 23, 2025

Conversation

Fyrebright
Copy link
Contributor

@Fyrebright Fyrebright commented Jun 19, 2025

  • Now skips 2D checks in perform
  • Updated the default arguments for check_finite to false to match documentation
  • Add benchmark test case

Description

Adds _cholesky method to slinalg.Cholesky to replace the scipy.linalg.cholesky wrapper. It is almost identical to the corresponding scipy function but it skips the 2d check and the batching wrapper.

Previous performance (with check_finite=False):

benchmark: 5.1.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)

--------------------------------------------------- benchmark: 1 tests ---------------------------------------------------
Name (time in us)                Min      Max    Mean  StdDev  Median     IQR   Outliers  OPS (Kops/s)  Rounds  Iterations
--------------------------------------------------------------------------------------------------------------------------
test_cholesky_performance     5.6610  43.2910  6.8433  2.4983  5.9610  0.1810  1312;1574      146.1280    9487           1
--------------------------------------------------------------------------------------------------------------------------

After changes:

benchmark: 5.1.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)

--------------------------------------------------- benchmark: 1 tests --------------------------------------------------
Name (time in us)                Min      Max    Mean  StdDev  Median     IQR  Outliers  OPS (Kops/s)  Rounds  Iterations
-------------------------------------------------------------------------------------------------------------------------
test_cholesky_performance     5.1500  28.5640  5.5494  1.0101  5.4210  0.1100   262;628      180.2012   12753           1
-------------------------------------------------------------------------------------------------------------------------

Related Issue

Checklist

Type of change

  • New feature / enhancement
  • Bug fix
  • Documentation
  • Maintenance
  • Other (please specify): performance

📚 Documentation preview 📚: https://pytensor--1487.org.readthedocs.build/en/1487/

* Now skips 2D checks in perform
* Updated the default arguments for `check_finite` to false to match documentation
* Add benchmark test case
@Fyrebright Fyrebright marked this pull request as ready for review June 19, 2025 22:09
Copy link
Member

@ricardoV94 ricardoV94 left a comment

Choose a reason for hiding this comment

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

Nice start, left some comments

@Fyrebright
Copy link
Contributor Author

Thank you for the feedback. I moved it all to perform and removed the asarray,_datacopied checks. I also moved the empty input case to the beginning and added test coverage for it.

Copy link
Member

@jessegrabowski jessegrabowski left a comment

Choose a reason for hiding this comment

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

Looking really great! I see a few more places we can improve the performance (avoiding copy + cleaning up the code) then I think it'll be ready to merge

@jessegrabowski
Copy link
Member

Looks really nice! I triggered a CI run, waiting to see if tests pass then will approve, pending thumbs up from @ricardoV94

@jessegrabowski
Copy link
Member

jessegrabowski commented Jun 22, 2025

You have a test failure in tests/link/numba/test_slinalg.py::test_cholesky_raises_on_nan_input:

2025-06-21T21:14:33.0385589Z =================================== FAILURES ===================================
2025-06-21T21:14:33.0386251Z ______________________ test_cholesky_raises_on_nan_input _______________________
2025-06-21T21:14:33.0386688Z 
2025-06-21T21:14:33.0386858Z     def test_cholesky_raises_on_nan_input():
2025-06-21T21:14:33.0387381Z         test_value = rng.random(size=(3, 3)).astype(floatX)
2025-06-21T21:14:33.0387886Z         test_value[0, 0] = np.nan
2025-06-21T21:14:33.0388256Z     
2025-06-21T21:14:33.0388568Z         x = pt.tensor(dtype=floatX, shape=(3, 3))
2025-06-21T21:14:33.0389299Z         x = x.T.dot(x)
2025-06-21T21:14:33.0389718Z         g = pt.linalg.cholesky(x)
2025-06-21T21:14:33.0390184Z         f = pytensor.function([x], g, mode="NUMBA")
2025-06-21T21:14:33.0390622Z     
2025-06-21T21:14:33.0391099Z >       with pytest.raises(np.linalg.LinAlgError, match=r"Non-numeric values"):
2025-06-21T21:14:33.0391737Z              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-21T21:14:33.0392394Z E       Failed: DID NOT RAISE <class 'numpy.linalg.LinAlgError'>
2025-06-21T21:14:33.0392804Z 
2025-06-21T21:14:33.0392979Z tests/link/numba/test_slinalg.py:471: Failed

Could be related to the change in the default check_finite flag? You might need to adjust this test to correctly set the flag if so.

Copy link
Member

@ricardoV94 ricardoV94 left a comment

Choose a reason for hiding this comment

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

Looks good. small comment


if self.check_finite and not np.isfinite(x).all():
if self.on_error == "nan":
out[0] = np.full(x.shape, np.nan, dtype=node.outputs[0].type.dtype)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
out[0] = np.full(x.shape, np.nan, dtype=node.outputs[0].type.dtype)
out[0] = np.full(x.shape, np.nan, dtype=potrf.dtype)

@Fyrebright
Copy link
Contributor Author

Could be related to the change in the default check_finite flag? You might need to adjust this test to correctly set the flag if so.

It is fixed now. I had been skipping the link/* tests until now. They are all passing on my machine except for:

FAILED tests/scan/test_basic.py::test_cython_performance - ModuleNotFoundError: No module named 'pytensor.scan.scan_perform'
FAILED tests/scan/test_basic.py::test_output_storage_reuse[cvm] - ModuleNotFoundError: No module named 'pytensor.scan.scan_perform'

Copy link

codecov bot commented Jun 23, 2025

Codecov Report

Attention: Patch coverage is 70.37037% with 8 lines in your changes missing coverage. Please review.

Please upload report for BASE (main@d3bbc20). Learn more about missing BASE report.
Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
pytensor/tensor/slinalg.py 70.37% 5 Missing and 3 partials ⚠️

❌ Your patch status has failed because the patch coverage (70.37%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1487   +/-   ##
=======================================
  Coverage        ?   81.98%           
=======================================
  Files           ?      231           
  Lines           ?    52192           
  Branches        ?     9185           
=======================================
  Hits            ?    42790           
  Misses          ?     7094           
  Partials        ?     2308           
Files with missing lines Coverage Δ
pytensor/tensor/slinalg.py 92.19% <70.37%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@jessegrabowski jessegrabowski left a comment

Choose a reason for hiding this comment

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

Looks great, thanks for this!

@jessegrabowski jessegrabowski merged commit 236e50d into pymc-devs:main Jun 23, 2025
73 of 74 checks passed
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants