Benchmark: Chebyshev vs. Direct Solvers
This benchmark demonstrates the performance trade-offs between polynomial approximation and sparse direct solvers. In most cases, the optimized DyConvolve solver outperforms high-order Chebyshev approximations on large sparse graphs.
Chebyshev vs. Analytical Timing
SCALES = [0.01, 0.1, 1, 10]
ORDER = 450
N_ITER = 200
X = impulse(L, n=1200)
def f(x): return np.stack([sgwt.functions.bandpass(x, scale=s, order=1) for s in SCALES], axis=1)
lbnd = 1e-3
kernel = sgwt.ChebyKernel.from_function_on_graph(L, f, ORDER, min_lambda=lbnd)
print(f"Benchmarking on {L.shape[0]} nodes...")
# Time Chebyshev Convolution
with ChebyConvolve(L) as conv_cheb:
_ = conv_cheb.convolve(X, kernel)
start = time.time()
for _ in range(N_ITER):
_ = conv_cheb.convolve(X, kernel)
t_cheb = (time.time() - start) / N_ITER
# Time DyConvolve, which pre-factors to time only the solve phase.
with DyConvolve(L, poles=[1.0/s for s in SCALES]) as conv_dy:
_ = conv_dy.bandpass(X)
start = time.time()
for _ in range(N_ITER):
_ = conv_dy.bandpass(X)
t_analytical = (time.time() - start) / N_ITER
# Time Convolve, which includes factorization in each call.
with Convolve(L) as conv_std:
_ = conv_std.bandpass(X, SCALES)
start = time.time()
for _ in range(N_ITER):
_ = conv_std.bandpass(X, SCALES)
t_std = (time.time() - start) / N_ITER
print(f"Chebyshev (Order {ORDER}): {t_cheb*1000:.2f} ms")
print(f"Convolve: {t_std*1000:.2f} ms")
print(f"DyConvolve: {t_analytical*1000:.2f} ms")