Skip to content

Conversation

@scottshambaugh
Copy link
Contributor

@scottshambaugh scottshambaugh commented Jan 19, 2026

PR summary

This speeds up log and symlog scale transforms by precomputing some constants, putting in a fast path for symlog when all the points are in the linear region, using specific exponential functions where available, and switching from the slow np.power(base, x) to the faster np.exp(x * np.log(base)).

On a comparison benchmark of 100000 points, I'm seeing the following speedup factors with the new vs old algorithms:

Transform Speedup
LogTransform 1.05×
InvertedLogTransform 3.5×
SymmetricalLogTransform (linear region only) 35×
SymmetricalLogTransform (mixed data) 1.05×
InvertedSymmetricalLogTransform (linear region only) 35×
InvertedSymmetricalLogTransform (mixed data) 1.5×

PR checklist

@scottshambaugh scottshambaugh force-pushed the scale_speedups branch 2 times, most recently from d6f1a28 to 49dec48 Compare January 19, 2026 03:49
@scottshambaugh scottshambaugh changed the title [PERF] Speed up log and symlog scale transforms PERF: Speed up log and symlog scale transforms Jan 19, 2026

@image_comparison(["test_loglog_nonpos.png"], remove_text=True, style='mpl20',
tol=0 if platform.machine() == 'x86_64' else 0.029)
tol=0.029)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to increated the tol for x86 now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was a 0.006 RMS change in the image test on some platforms, I chalked it up to round-off

self.base = base
self._clip = _api.check_getitem(
{"clip": True, "mask": False}, nonpositive=nonpositive)
self._log_func = {np.e: np.log, 2: np.log2, 10: np.log10}.get(base)
Copy link
Member

Choose a reason for hiding this comment

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

This introduces state depending on base. While highly unlikely, this would break user code that has changed the base. Are we ok with this?

We possibly should make base private (rename base -> _base) and re-expose baseas a property. You can then decide whether you leave out the setter - with the argument that nobody uses it anyway; or implement it with updateing_log_func`.
Anyway, erroring out on a missing setter would still be better than accepting a new base, but continuing to use the old log function.

Same for some other transforms below.

Copy link
Contributor Author

@scottshambaugh scottshambaugh Jan 19, 2026

Choose a reason for hiding this comment

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

Yeah, that's a really good call.

I did a github search and found a few instances of code setting these, so added those as properties with setters. Also deprecated invlinthresh since it's a derived attr and I couldn't find any instances of setting that.

Speedups are unchanged after this.

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.

2 participants