Description
While preparing my GSoC 2026 proposal focused on optimizing pyGAM's matrix operations, I profiled the GAM._pirls optimization loop and isolated a severe $O(N^3)$ memory bottleneck.
When fitting overparameterized models (where the number of splines/features $p$ exceeds the number of samples $n$), the Effective Degrees of Freedom (EDoF) calculation creates a massive intermediate dense matrix that scales quadratically with $p$, causing massive RAM spikes and fatal MemoryError crashes on standard hardware.
The Bottleneck
The leak originates during the hat matrix trace extraction. The current codebase calculates this via cross-multiplication:
edof = np.diagonal(U1.dot(U1.T))
If $p = 10,000$, U1.dot(U1.T) forces the allocation of a $10,000 \times 10,000$ float64 matrix before immediately discarding everything except the diagonal.
Memory Profiling Proof
I wrote a synthetic benchmark ($n=500, p=10000$) to isolate this exact line using memory_profiler.
As shown below, the legacy operation triggers an immediate ~760 MiB memory spike. I have also profiled my proposed GSoC fix, which replaces the dense matrix creation with a vectorized $O(N)$ tensor-sum: (U1**2).sum(axis=1). This drops the overhead to near zero while preserving mathematical equivalence.
--- Running Legacy Calculation ---
Line # Mem usage Increment Occurrences Line Contents
=============================================================
22 76.4 MiB 0.0 MiB 1 start_time = time.time()
24 # The bottleneck:
25 839.5 MiB 763.1 MiB 1 edof = np.diagonal(u1_matrix.dot(u1_matrix.T))
27 839.5 MiB 0.0 MiB 1 end_time = time.time()
--- Running Proposed Vectorized Fix ---
Line # Mem usage Increment Occurrences Line Contents
=============================================================
38 76.5 MiB 0.0 MiB 1 start_time = time.time()
40 # The fix:
41 114.6 MiB 38.1 MiB 1 edof = (u1_matrix**2).sum(axis=1)
43 114.6 MiB 0.0 MiB 1 end_time = time.time()
Next Steps
I have already contributed this specific stress-test as an EDoFBenchmark class to the ASV automated benchmarking suite currently being built in PR #503.
I am opening this issue to formally track the algorithmic bottleneck itself. I plan to submit the vectorized (U1**2).sum(axis=1) fix as a core part of my GSoC 2026 project deliverables.
Description
While preparing my GSoC 2026 proposal focused on optimizing pyGAM's matrix operations, I profiled the$O(N^3)$ memory bottleneck.
GAM._pirlsoptimization loop and isolated a severeWhen fitting overparameterized models (where the number of splines/features$p$ exceeds the number of samples $n$ ), the Effective Degrees of Freedom (EDoF) calculation creates a massive intermediate dense matrix that scales quadratically with $p$ , causing massive RAM spikes and fatal
MemoryErrorcrashes on standard hardware.The Bottleneck
The leak originates during the hat matrix trace extraction. The current codebase calculates this via cross-multiplication:
edof = np.diagonal(U1.dot(U1.T))If$p = 10,000$ , $10,000 \times 10,000$ float64 matrix before immediately discarding everything except the diagonal.
U1.dot(U1.T)forces the allocation of aMemory Profiling Proof
I wrote a synthetic benchmark ($n=500, p=10000$ ) to isolate this exact line using
memory_profiler.As shown below, the legacy operation triggers an immediate ~760 MiB memory spike. I have also profiled my proposed GSoC fix, which replaces the dense matrix creation with a vectorized$O(N)$ tensor-sum:
(U1**2).sum(axis=1). This drops the overhead to near zero while preserving mathematical equivalence.Next Steps
I have already contributed this specific stress-test as an
EDoFBenchmarkclass to the ASV automated benchmarking suite currently being built in PR #503.I am opening this issue to formally track the algorithmic bottleneck itself. I plan to submit the vectorized
(U1**2).sum(axis=1)fix as a core part of my GSoC 2026 project deliverables.