Repository: pymc-devs/pymc
Pull request: add n_draws and t_sampling report to smc
Adds `trace.report_n_draw` mainly to allow ArviZ to work with a trace from SMC, also adds `_t_sampling`, because having access to the sampling time is nice :-)

Fixes #3928


Problem statement:
Attributes of trace.report are None when using sample_smc—Converting to arviz fails
When I generate samples with the sample_smc method, the attributes `n_draws`, `n_tune` and `t_sampling` of the trace's report are all None.
This leads to an error when I try to convert the trace to an arviz inference data object (I don't know if it causes problems elsewhere).
This does not happen when sampling with NUTS.

```python
import numpy as np
import pymc3 as pm
import arviz as az

x_obs = np.random.rand(3)
with pm.Model() as model:
    x = pm.Normal('x')
    pm.Normal('x_obs', mu=x, observed=x_obs)
    trace_smc = pm.sample_smc(draws=100)

trace_smc.report.n_draws is None
trace_smc.report.n_tune is None
trace_smc.report.t_sampling is None

# This fails
data = az.from_pymc3(trace_smc)
```
The error actually occurs in the arviz code but is due to `trace.report.n_draws` being `None`.

```python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-41-042acf6338ba> in <module>
     14 trace_smc.report.t_sampling is None
     15 
---> 16 data = az.from_pymc3(trace_smc)

~/miniconda3/envs/sunode/lib/python3.7/site-packages/arviz/data/io_pymc3.py in from_pymc3(trace, prior, posterior_predictive, log_likelihood, coords, dims, model, save_warmup)
    480         dims=dims,
    481         model=model,
--> 482         save_warmup=save_warmup,
    483     ).to_inference_data()
    484 

~/miniconda3/envs/sunode/lib/python3.7/site-packages/arviz/data/io_pymc3.py in to_inference_data(self)
    413         """
    414         id_dict = {
--> 415             "posterior": self.posterior_to_xarray(),
    416             "sample_stats": self.sample_stats_to_xarray(),
    417             "log_likelihood": self.log_likelihood_to_xarray(),

~/miniconda3/envs/sunode/lib/python3.7/site-packages/arviz/data/base.py in wrapped(cls, *args, **kwargs)
     35                 if all([getattr(cls, prop_i) is None for prop_i in prop]):
     36                     return None
---> 37             return func(cls, *args, **kwargs)
     38 
     39         return wrapped

~/miniconda3/envs/sunode/lib/python3.7/site-packages/arviz/data/io_pymc3.py in posterior_to_xarray(self)
    207                 )
    208             data[var_name] = np.array(
--> 209                 self.trace[-self.ndraws :].get_values(var_name, combine=False, squeeze=False)
    210             )
    211         return (

TypeError: bad operand type for unary -: 'NoneType'
```

## Versions and main components

* PyMC3 Version: 3.8
* Theano Version: 1.0.4
* Python Version: 3.7.6
* Operating system: Archlinux
* How did you install PyMC3: conda
* ArviZ Version: 0.7.0

Hints from issue discussion:

Commit message: add n_draws and t_sampling report to smc

Patch to review:
diff --git a/pymc3/smc/sample_smc.py b/pymc3/smc/sample_smc.py
index 28bfa035ae..c5e22902ca 100644
--- a/pymc3/smc/sample_smc.py
+++ b/pymc3/smc/sample_smc.py
@@ -12,8 +12,9 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from .smc import SMC
+import time
 import logging
+from .smc import SMC
 
 
 def sample_smc(
@@ -144,6 +145,7 @@ def sample_smc(
         random_seed=random_seed,
     )
 
+    t1 = time.time()
     _log = logging.getLogger("pymc3")
     _log.info("Sample initial stage: ...")
     stage = 0
@@ -170,5 +172,6 @@ def sample_smc(
         smc.pool.join()
 
     trace = smc.posterior_to_trace()
-
+    trace.report._n_draws = smc.draws
+    trace.report._t_sampling = time.time() - t1
     return trace


Code review comments:
diff_hunk: @@ -170,5 +172,6 @@ def sample_smc(
         smc.pool.join()
 
     trace = smc.posterior_to_trace()
-
+    trace.report._n_draws = smc.draws
+    trace.report._t_sampling = time.time() - t1
path: pymc3/smc/sample_smc.py
text: @user1:
`trace.report._n_tune = n_tune` also needs to be set

@user1:

done
 I broke it :(

@author:
there is actually no n_tune in SMC, tuning for stage t is done based on the results from stage t-1.