Skip to content

improve speed Whittaker smoother#358

Merged
jobo322 merged 4 commits intomainfrom
357-uses-cuthillmckee-permutation-in-xwhittakersmoother-to-improve-speed
Apr 13, 2026
Merged

improve speed Whittaker smoother#358
jobo322 merged 4 commits intomainfrom
357-uses-cuthillmckee-permutation-in-xwhittakersmoother-to-improve-speed

Conversation

@jobo322
Copy link
Copy Markdown
Member

@jobo322 jobo322 commented Apr 9, 2026

uses cuthillmckee permutation in xwhittakersmoother (it improve speed in around 3x faster)
implement Thomas algorithm to solve tridiagonal systems like Q = W + λ D'D
keep cholesky method by add a new option in xWhittakerSmoother `algorithm: 'cholesky' | 'thomas'. by default the function uses Thomas method because currently it is used for big arrays.

@jobo322 jobo322 linked an issue Apr 9, 2026 that may be closed by this pull request
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 97.92746% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.02%. Comparing base (8fb44b2) to head (e744a90).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/x/xWhittakerSmoother.ts 96.05% 3 Missing ⚠️
src/matrix/matrixCholeskySolver.ts 99.12% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main     #358    +/-   ##
========================================
  Coverage   97.02%   97.02%            
========================================
  Files         199      199            
  Lines        3863     3970   +107     
  Branches      986     1003    +17     
========================================
+ Hits         3748     3852   +104     
- Misses        111      114     +3     
  Partials        4        4            

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jobo322 jobo322 merged commit c736e4c into main Apr 13, 2026
11 checks passed
@jobo322 jobo322 deleted the 357-uses-cuthillmckee-permutation-in-xwhittakersmoother-to-improve-speed branch April 13, 2026 15:12
@targos targos requested a review from Copilot April 14, 2026 06:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR speeds up xWhittakerSmoother by introducing a tridiagonal (Thomas) solver path and by adding a bandwidth-reducing permutation to the Cholesky solver path.

Changes:

  • Added algorithm: 'cholesky' | 'thomas' option to xWhittakerSmoother (defaulting to 'thomas') and implemented a Thomas-based smoother.
  • Updated the Cholesky smoother path to use a Cuthill–McKee permutation and updated createSystemMatrix to return both triplets + permutation.
  • Refactored matrixCholeskySolver internals to use typed arrays and updated/added tests to exercise both algorithms.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/x/xWhittakerSmoother.ts Adds algorithm selection, implements Thomas solver path, and routes Cholesky path through a permutation.
src/x/tests/xWhittakerSmoother.test.ts Adds coverage for both solver algorithms via a new test.
src/utils/createSystemMatrix.ts Now returns { upperTriangularNonZeros, permutationEncodedArray } and computes permutation via Cuthill–McKee.
src/matrix/matrixCholeskySolver.ts Optimizes solver internals with typed arrays and supports permutation input.
src/matrix/tests/matrixCholeskySolver.test.ts Adapts to the new createSystemMatrix return type and fixes one call-site typo.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +260 to +264
baseline = xEnsureFloat64(solution);
iteration++;
}

return baseline;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

In the Thomas loop, baseline = xEnsureFloat64(solution) copies the full array every iteration (and xEnsureFloat64 always clones Float64Arrays). This adds O(n * iterations) extra copying and can erase much of the intended speedup. Consider keeping solution as the working baseline and only cloning once at the end (or when returning early).

Suggested change
baseline = xEnsureFloat64(solution);
iteration++;
}
return baseline;
baseline = solution;
iteration++;
}
return xEnsureFloat64(baseline);

Copilot uses AI. Check for mistakes.
const newBaseline = cho(rightHandSide);

weights = calculateAdaptiveWeights(yData, newBaseline, weights, {
//weights is updated inplace
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

Minor: comment has a typo/spacing issue. Consider changing to “weights are updated in place” for clarity.

Suggested change
//weights is updated inplace
// weights are updated in place

Copilot uses AI. Check for mistakes.
expect(smooth[50]).toBeGreaterThan(-0.2);
});

test('two peaks with sine baseline by thoma method', () => {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

Test name has a typo: “thoma” should be “thomas”.

Suggested change
test('two peaks with sine baseline by thoma method', () => {
test('two peaks with sine baseline by thomas method', () => {

Copilot uses AI. Check for mistakes.
Comment on lines 22 to 28
const upperTriangularNonZeros: number[][] = [];
const last = dimension - 1;
for (let i = 0; i < last; i++) {
upperTriangularNonZeros.push([i, i, lambda * 2], [i, i + 1, -1 * lambda]);
}
upperTriangularNonZeros[0][2] = lambda;
upperTriangularNonZeros.push([last, last, lambda]);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

createSystemMatrix throws for dimension <= 1 because upperTriangularNonZeros[0] is undefined when the loop doesn’t run, but the code still does upperTriangularNonZeros[0][2] = lambda. Please add an early return/guard for small dimensions (and return a valid matrix/permutation) so the Cholesky path can handle length-1 inputs safely.

Copilot uses AI. Check for mistakes.
Comment on lines 8 to 26
@@ -12,13 +18,17 @@
export function createSystemMatrix(
dimension: number,
lambda: number,
): number[][] {
): SystemMatrix {
const upperTriangularNonZeros: number[][] = [];
const last = dimension - 1;
for (let i = 0; i < last; i++) {
upperTriangularNonZeros.push([i, i, lambda * 2], [i, i + 1, -1 * lambda]);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The JSDoc says this function generates “lower triangular non-zeros”, but the triplets built here are on/above the diagonal (e.g., [i, i+1, ...]), i.e. upper-triangular. Please align the documentation with the actual representation (and keep wording consistent with upperTriangularNonZeros).

Copilot uses AI. Check for mistakes.
Comment on lines +197 to +205
// Precompute base diagonal and constant off-diagonals (−lambda)
const baseDiag = new Float64Array(n);
if (n === 1) {
baseDiag[0] = lambda;
} else {
baseDiag[0] = lambda;
for (let i = 1; i < n - 1; i++) baseDiag[i] = 2 * lambda;
baseDiag[n - 1] = lambda;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

For n === 1, the penalty term lambda * D'D should be zero (there are no first differences), so the baseline should remain y regardless of lambda. Currently baseDiag[0] = lambda, which will shrink the solution toward 0 (w*y/(w+lambda)). Please special-case n === 1 (and potentially n === 2) to return y (or use a zero penalty) so results are mathematically consistent.

Copilot uses AI. Check for mistakes.
/**
* Iterative Whittaker smoothing implementation using the Thomas algorithm
* (tridiagonal solver). The method constructs the system
* [W + lambda * D'D] z = W * y where D is the second-difference operator
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

This block claims D is the “second-difference operator”, but the system you build/solve here is tridiagonal with off-diagonals -lambda, which corresponds to a first-difference penalty (second-difference would produce a pentadiagonal system). Please correct the description so it matches the actual formulation implemented.

Suggested change
* [W + lambda * D'D] z = W * y where D is the second-difference operator
* [W + lambda * D'D] z = W * y where D is the first-difference operator

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@targos targos left a comment

Choose a reason for hiding this comment

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

@jobo322 You should not merge non-trivial changes like this without a review by someone.
I asked Copilot to test it. Feel free to ignore its comments if they are wrong.

Comment on lines +187 to +198
const apLoc = ap as Int32Array;
const aiLoc = ai as Int32Array;
const axLoc = ax as Float64Array;
const lpLoc = lp as Int32Array;
const parentLoc = parent as Int32Array;
const lnzLoc = lnz as Int32Array;
const liLoc = li as Int32Array;
const lxLoc = lx as Float64Array;
const dLoc = d as Float64Array;
const yLoc = y as Float64Array;
const patternLoc = pattern as Int32Array;
const flagLoc = flag as Int32Array;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why did you use as everywhere instead of changing the types of the function parameters?

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.

uses cuthillMckee permutation in xWhittakerSmoother to improve speed

3 participants