mmaelicke / scikit-gstat

Geostatistical variogram estimation expansion in the scipy style
https://mmaelicke.github.io/scikit-gstat/
MIT License
223 stars 52 forks source link

Sum models break the docs #177

Open mmaelicke opened 8 months ago

mmaelicke commented 8 months ago

Hey @rhugonnet,

The docs are not compiling anymore and I finally found the time to have a look at that. To me it seems like the the standard plotting routine has an issue, when calling the Variogram.fitted_model through Variogram.transform. It is one of the V.plot(); statements in docs/userguide/variogram.rst. It started failing after we implemented the sum of models. I can't really tell which of the plot statements causes the error, but we added to that file, so I suspect it is one of the newly added plotting of sum of models. Here is the traceback of building the docs locally:

>>>-------------------------------------------------------------------------
Exception in /Users/mirko/Library/CloudStorage/Dropbox/python/scikit-gstat/docs/userguide/variogram.rst at block ending on line None
Specify :okexcept: as an option in the ipython:: block to suppress this message
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[129], line 1
----> 1 V.plot();

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/Variogram.py:2966, in Variogram.plot(self, axes, grid, show, hist)
   2963 used_backend = plotting.backend()
   2965 if used_backend == 'matplotlib':
-> 2966     return plotting.matplotlib_variogram_plot(
   2967         self,
   2968         axes=axes,
   2969         grid=grid,
   2970         show=show,
   2971         hist=hist
   2972     )
   2973 elif used_backend == 'plotly':
   2974     return plotting.plotly_variogram_plot(
   2975         self,
   2976         fig=axes,
   (...)
   2979         hist=hist
   2980     )

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/plotting/variogram_plot.py:38, in matplotlib_variogram_plot(variogram, axes, grid, show, hist)
     30 def matplotlib_variogram_plot(
     31     variogram,
     32     axes=None,
   (...)
     36 ):
     37     # get the plotting data
---> 38     x, y, _bins, _exp = __calculate_plot_data(variogram)
     40     # do the plotting
     41     if axes is None:

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/plotting/variogram_plot.py:18, in __calculate_plot_data(variogram)
     15 x = np.linspace(0, np.nanmax(_bins), 100)
     17 # apply the model
---> 18 y = variogram.transform(x)
     20 # handle the relative experimental variogram
     21 if variogram.normalized:

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/Variogram.py:1817, in Variogram.transform(self, x)
   1814     self.fit(force=True)
   1816 # return the result
-> 1817 return self.fitted_model(x)

File <string>:1, in <lambda>(x)

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/models.py:15, in variogram.<locals>.wrapper(*args, **kwargs)
     13     new_args = args[1:]
     14     mapping = map(lambda h: func(h, *new_args, **kwargs), args[0])
---> 15     return np.fromiter(mapping, dtype=float)
     16 else:
     17     return func(*args, **kwargs)

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/models.py:14, in variogram.<locals>.wrapper.<locals>.<lambda>(h)
     12 if hasattr(args[0], '__iter__'):
     13     new_args = args[1:]
---> 14     mapping = map(lambda h: func(h, *new_args, **kwargs), args[0])
     15     return np.fromiter(mapping, dtype=float)
     16 else:

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/Variogram.py:1070, in Variogram._build_sum_models.<locals>.sum_models(h, *args)
   1068 @models.variogram
   1069 def sum_models(h, *args):
-> 1070     return sum(list_models[i](h, *args[args_slices[i]]) for i in range(len(list_models)))

File ~/Library/CloudStorage/Dropbox/python/scikit-gstat/skgstat/Variogram.py:1070, in <genexpr>(.0)
   1068 @models.variogram
   1069 def sum_models(h, *args):
-> 1070     return sum(list_models[i](h, *args[args_slices[i]]) for i in range(len(list_models)))

TypeError: spherical() missing 1 required positional argument: 'c0'

<<<-------------------------------------------------------------------------

@rhugonnet I am a bit lost here. Do you have any idea, what exactly is going wrong here?

rhugonnet commented 8 months ago

Hi @mmaelicke, That's strange... I get the same Sphinx error locally, it is failing at the "Custom model" example V.plot(), but if I run the code of the user guide separately it works (and I had the custom_model.png figure saved locally in savefig/, so it used to pass). Looking into it in more details...

rhugonnet commented 8 months ago

Here's the code that fails in Sphinx, does it also run for you or am I missing something environment/setup-wise?

import skgstat as skg
import numpy as np

coords, vals = skg.data.pancake(N=200).get('sample')

V = skg.Variogram(
    coords,
    vals,
    n_lags=25
)
V.maxlag = 500

# Build a custom model by applying the @variogram decorator (here adding a linear term to a spherical model)
from skgstat.models import variogram, spherical

@variogram
def custom_model(h, r1, c1, a):
    return spherical(h, r1, c1) + h * a

V.model = custom_model

# We define the bounds for r1, c1 and a
bounds_custom = [(0, 0, 0), (np.max(V.bins), np.max(V.experimental), 2)]
V.fit(bounds=bounds_custom)
V.plot()
rhugonnet commented 8 months ago

Looking at the error again (third argument missing in spherical, while three are passed), it looks like the @variogram decorator might be the cause and behaving differently in the ipython code cell than elsewhere (similarly as @savefig that is used to record the figures?).

I don't have much experience with ipython in Sphinx... Do you have any idea how to address this?

I also double-checked and I did add tests for the plotting of custom models, so should in principle always work: https://github.com/mmaelicke/scikit-gstat/blob/main/skgstat/tests/test_variogram.py#L1460

mmaelicke commented 8 months ago

I'll check the code and run the sphinx build in a clean build. Possible, that I also had some images cached...

I will try to free my calendar this week a bit and investigate more into this. A possible fix would be to shift the examples to sphinx_gallery examples, which I implemented way later into the docs (the tutorials are written that way). For the case, that the ipython directive in sphinx was the cause.... We'll see

rhugonnet commented 8 months ago

It seems to be related to this: https://github.com/ipython/ipython/issues/13531 which was fixed in ipython 8.15 or 8.16 and above. But using a clean sphinx install with ipython 8.20, still getting the error...