Skip to content

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

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Fyrebright
Copy link

@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
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

Comment on lines +73 to +76
eye = np.eye(1, dtype=x.dtype)
(potrf,) = scipy_linalg.get_lapack_funcs(("potrf",), (eye,))
c, _ = potrf(eye, lower=False, overwrite_a=False, clean=True)
out[0] = np.empty_like(x, dtype=c.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
eye = np.eye(1, dtype=x.dtype)
(potrf,) = scipy_linalg.get_lapack_funcs(("potrf",), (eye,))
c, _ = potrf(eye, lower=False, overwrite_a=False, clean=True)
out[0] = np.empty_like(x, dtype=c.dtype)
(potrf,) = scipy_linalg.get_lapack_funcs(("potrf",), (x,))
out[0] = np.empty_like(x, dtype=potrf.dtype)

Since you just want the dtype, you can check potrf.dtype, you don't need to actually call the routine. It's also fine to pass in the empty x in this case, since it's only used for its dtype attribute (the data or lack thereof doesn't matter)

out[0] = np.empty_like(x, dtype=c.dtype)
return

x1 = np.asarray_chkfinite(x) if self.check_finite else x
Copy link
Member

Choose a reason for hiding this comment

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

I don't like the introduction of a new variable. We know for sure x is a numpy array inside this function, so the only relevant code in np.asarray_chkfinite(x) is the if not np.isfinite(x).all(): part. We should just directly do that

# Quick return for square empty array
if x.size == 0:
eye = np.eye(1, dtype=x.dtype)
(potrf,) = scipy_linalg.get_lapack_funcs(("potrf",), (eye,))
Copy link
Member

Choose a reason for hiding this comment

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

Get potrf once before this size check and re-use it in all branches


# Scipy cholesky only makes use of overwrite_a when it is F_CONTIGUOUS
# If we have a `C_CONTIGUOUS` array we transpose to benefit from it
if self.overwrite_a and x.flags["C_CONTIGUOUS"]:
Copy link
Member

Choose a reason for hiding this comment

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

save a variable c_contiguous_input and use that for checks later (instead of maintaining two arrays)

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.

3 participants