14. Optimization

There are three main computational steps when doing a Gaussian process fit with lsqfitgp:

  • Compute the prior covariance matrix using the kernel. This is \(O((n + m)^2)\) where n is the number of datapoints and m the number of additional points where the posterior is computed.

  • Solve a linear system of size n. This is \(O(n^3)\).

  • Take random samples from the posterior. This is \(O(m^3 + s)\) where s is the number of samples taken.

Since usually \(m \gg n\) because the plot is done on a finely spaced grid, the typical bottleneck is taking the samples, i.e., calling gvar.raniter(). This problem can be bypassed by plotting only the standard deviation band instead of taking samples, but it is less informative. To make gvar.raniter() faster, use its eps option: gvar.raniter(x, eps=1e-12). This forces it to use a Cholesky decomposition instead of a diagonalization.

In general the GP methods have options for doing everything without gvar, but don’t try to use all of them mindlessly before profiling the code to know where the bottleneck actually is. Python has the module cProfile for that, and in an IPython shell you can use %run -p. If you opt out of gvars, you can use lsqfitgp.raniter() to draw samples from an explicit mean vector and covariance matrix instead of gvar.raniter().

Once you have solved eventual gvar-related issues, if you have at least some hundreds of datapoints the next bottleneck is probably in GP.predfromdata(). Making it faster is quick: select a solver different from the default one when initializing the GP object, like GP(kernel, solver='chol'). This applies also when using empbayes_fit. And don’t forget to disable the positivity check: GP(kernel, solver='chol', checkpos=False).

Finally, if you have written a custom kernel, it may become a bottleneck. For example the letter counting kernel in A custom kernel: text classification was very slow. A quick way to get a 2x improvement is disabling the symmetry check in GP: GP(kernel, checksym=False).