```python
import pyprob
from pyprob import Model
from pyprob import InferenceEngine
from pyprob.distributions import Normal
from pyprob.dis import ModelDIS

import torch
import numpy as np
import math
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure();
```

    <Figure size 432x288 with 0 Axes>

# Defining the Model

```python
class GaussianUnknownMean(Model):
    def __init__(self):
        super().__init__(name='Gaussian with unknown mean') # give the model a name
        self.prior_mean = 1
        self.prior_std = math.sqrt(5)
        self.likelihood_std = math.sqrt(2)

    def forward(self): # Needed to specifcy how the generative model is run forward
        # sample the (latent) mean variable to be inferred:
        mu = pyprob.sample(Normal(self.prior_mean, self.prior_std)) # NOTE: sample -> denotes latent variables

        # define the likelihood
        likelihood = Normal(mu, self.likelihood_std)

        # Lets add two observed variables
        # -> the 'name' argument is used later to assignment values:
        #pyprob.observe(likelihood, name='obs0') # NOTE: observe -> denotes observable variables

        # return the latent quantity of interest
        return mu

class GaussianDIS(GaussianUnknownMean, ModelDIS):
    def __init__(self):
        super().__init__()
        self.obs = 8
        #self.bool_func = bool_func
    def dummy_bernoulli(self, x):
        return True
    def forward(self):
        x = super().forward()
        likelihood = Normal(x, self.likelihood_std)
        sample_obs = pyprob.sample(likelihood)
        euclidean_dist = np.abs(sample_obs - self.obs)
        delta_val = self.dummy_bernoulli(x)
        bool_func_dist = pyprob.distributions.Bernoulli(delta_val)
        pyprob.observe(bool_func_dist, name = "bool_func")
        dummy = pyprob.distributions.Normal(euclidean_dist,0.1)
        pyprob.observe(dummy, euclidean_dist, name = "distance") # Any other way to do this?
        return x

model = GaussianUnknownMean()
simulator = GaussianDIS()
```

# Finding the correct posterior analytically
Since all distributions are gaussians in this model, we can analytically compute the posterior and we can compare the true posterior to the inferenced one.

Assuming that the prior and likelihood are $p(x) = \mathcal{N}(\mu_0, \sigma_0)$ and $p(y|x) = \mathcal{N}(x, \sigma)$ respectively and, $y_1, y_2, \ldots y_n$ are the observed values, the posterior would be $p(x|y) = \mathcal{N}(\mu_p, \sigma_p)$ where,
$$
\begin{align}
\sigma_{p}^{2} & = \frac{1}{\frac{n}{\sigma^2} + \frac{1}{\sigma_{0}^{2}}} \\
\mu_p & = \sigma_{p}^{2} \left( \frac{\mu_0}{\sigma_{0}^{2}} + \frac{n\overline{y}}{\sigma^2} \right)
\end{align}
$$
The following class implements computing this posterior distribution. We also implement some helper functions and variables for plotting the correct posterior and prior.

```python
def plot_function(min_val, max_val, func, *args, **kwargs):
        x = np.linspace(min_val,max_val,int((max_val-min_val)*50))
        plt.plot(x, np.vectorize(func)(x), *args, **kwargs)

def get_dist_pdf(dist):
    return lambda x: math.exp(dist.log_prob(x))

class CorrectDistributions:
    def __init__(self, model):
        self.prior_mean = model.prior_mean
        self.prior_std = model.prior_std
        self.likelihood_std = model.likelihood_std
        self.prior_dist = Normal(self.prior_mean, self.prior_std)

    @property
    def observed_list(self):
        return self.__observed_list

    @observed_list.setter
    def observed_list(self, new_observed_list):
        self.__observed_list = new_observed_list
        self.construct_correct_posterior()

    def construct_correct_posterior(self):
        n = len(self.observed_list)
        posterior_var = 1/(n/self.likelihood_std**2 + 1/self.prior_std**2)
        posterior_mu = posterior_var * (self.prior_mean/self.prior_std**2 + n*np.mean(self.observed_list)/self.likelihood_std**2)
        self.posterior_dist = Normal(posterior_mu, math.sqrt(posterior_var))

    def prior_pdf(self, model, x):
        p = Normal(model.prior_mean,model.prior_stdd)
        return math.exp(p.log_prob(x))

    def plot_posterior(self, min_val, max_val):
        if not hasattr(self, 'posterior_dist'):
            raise AttributeError('observed values are not set yet, and posterior is not defined.')
        plot_function(min_val, max_val, get_dist_pdf(self.posterior_dist), label='correct posterior', color='orange')

    def plot_prior(self, min_val, max_val):
        plot_function(min_val, max_val, get_dist_pdf(self.prior_dist), label='prior', color='green')

```

```python
correct_dists = CorrectDistributions(model)
correct_dists.observed_list=[8]
```

# Training

For a sanity check, start with one training round.

```python
simulator.train(iterations = 1,
    importance_sample_size=5000,
    proposal_mixture_components=5,
    observe_embeddings={'bool_func': {'dim': 1, 'depth': 1}} # Dummy value as we currently have to observe something
)
```

    Creating new inference network...
    Observable bool_func: reshape not specified, using shape torch.Size([]).
    Observable bool_func: using embedding dim torch.Size([1]).
    Observable bool_func: observe embedding not specified, using the default FEEDFORWARD.
    Observable bool_func: using embedding depth 1.
    Observe embedding dimension: 1
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    New layers, address: 16__forward__mu__Normal__1, distribution: Normal
    New layers, address: 28__forward__sample_obs__Normal__1, distribution: Normal
    Total addresses: 2, parameters: 308
    0d:00:00:01 | 1    | 400       | +4.09e+00 | +4.09e+00 | +4.29e+00 | 0d:00:00:00 | +1.00e-03 | 360.3
    Stop condition reached. num_traces: 500
    0d:00:00:01 | 1    | 500       | +4.09e+00 | +4.01e+00 | +4.01e+00 | 0d:00:00:00 | +1.00e-03 | 313.9
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:48 | 0d:00:00:00 | #################### | 5000/5000 | 4017.68 | 102.12
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:01 | 0d:00:00:00 | #################### | 500/500 | 408.69
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:02 | 1    | 800       | +4.09e+00 | +4.01e+00 | +5.20e+00 | 0d:00:00:00 | +1.00e-03 | 382.7
    Stop condition reached. num_traces: 500
    0d:00:00:02 | 1    | 1,000     | +4.09e+00 | +4.01e+00 | +5.34e+00 | 0d:00:00:00 | +1.00e-03 | 481.5
    Training iterations 1  epsilon 1.58  ESS 500.0

```python
prior = simulator.prior_results(num_traces = 2000)
posterior_check = simulator.posterior(num_traces = 2000,
inference_engine = InferenceEngine.IMPORTANCE_SAMPLING_WITH_INFERENCE_NETWORK,
observe={"bool_func": 1})
posterior_check = simulator.update_DIS_posterior_weights(posterior_check)
results_check = np.array([x.result for x in posterior_check.values])
```

    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:08 | 0d:00:00:00 | #################### | 2000/2000 | 2000.00 | 243.65
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:31 | 0d:00:00:00 | #################### | 2000/2000 | 1658.50 | 62.54

Now do 50 more iterations.

```python
simulator.train(iterations = 50,
    importance_sample_size=5000,
    proposal_mixture_components=5,
    observe_embeddings={'bool_func': {'dim': 1, 'depth': 1}} # Dummy value as we currently have to observe something
)
```

    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:01:09 | 0d:00:00:00 | #################### | 5000/5000 | 3320.45 | 71.86
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:01 | 0d:00:00:00 | #################### | 500/500 | 453.10
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:16 | 1    | 6,400     | +4.09e+00 | +4.01e+00 | +4.52e+00 | 0d:00:00:01 | +1.00e-03 | 350.3
    Stop condition reached. num_traces: 500
    0d:00:00:17 | 1    | 6,500     | +4.09e+00 | +4.01e+00 | +4.68e+00 | 0d:00:00:01 | +1.00e-03 | 330.9
    Training iterations 1  epsilon 0.92  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:51 | 0d:00:00:00 | #################### | 5000/5000 | 3153.07 | 96.25
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:01 | 0d:00:00:00 | #################### | 500/500 | 362.58
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:19 | 1    | 6,900     | +4.09e+00 | +4.01e+00 | +4.45e+00 | 0d:00:00:02 | +1.00e-03 | 191.8
    Stop condition reached. num_traces: 500
    0d:00:00:19 | 1    | 7,000     | +4.09e+00 | +4.01e+00 | +4.63e+00 | 0d:00:00:02 | +1.00e-03 | 477.7
    Training iterations 2  epsilon 0.86  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:42 | 0d:00:00:00 | #################### | 5000/5000 | 3025.67 | 118.04
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 508.63
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:20 | 1    | 7,300     | +4.09e+00 | +4.01e+00 | +4.60e+00 | 0d:00:00:00 | +1.00e-03 | 376.6
    Stop condition reached. num_traces: 500
    0d:00:00:21 | 1    | 7,500     | +4.09e+00 | +4.01e+00 | +4.39e+00 | 0d:00:00:01 | +1.00e-03 | 398.8
    Training iterations 3  epsilon 0.83  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:42 | 0d:00:00:00 | #################### | 5000/5000 | 2854.48 | 118.99
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 538.06
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:21 | 1    | 7,800     | +4.09e+00 | +4.01e+00 | +4.36e+00 | 0d:00:00:00 | +1.00e-03 | 485.2
    Stop condition reached. num_traces: 500
    0d:00:00:22 | 1    | 8,000     | +4.09e+00 | +4.01e+00 | +4.54e+00 | 0d:00:00:00 | +1.00e-03 | 445.3
    Training iterations 4  epsilon 0.81  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:16:08 | 0d:00:00:00 | #################### | 5000/5000 | 2626.30 | 5.17
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:01 | 0d:00:00:00 | #################### | 500/500 | 441.01
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:22 | 1    | 8,300     | +4.09e+00 | +4.01e+00 | +4.33e+00 | 0d:00:00:00 | +1.00e-03 | 441.3
    Stop condition reached. num_traces: 500
    0d:00:00:23 | 1    | 8,500     | +4.09e+00 | +4.01e+00 | +4.30e+00 | 0d:00:00:01 | +1.00e-03 | 372.7
    Training iterations 5  epsilon 0.69  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:48 | 0d:00:00:00 | #################### | 5000/5000 | 2528.60 | 103.51
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 752.00
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:23 | 1    | 8,900     | +4.09e+00 | +4.01e+00 | +4.26e+00 | 0d:00:00:00 | +1.00e-03 | 957.9
    Stop condition reached. num_traces: 500
    0d:00:00:23 | 1    | 9,000     | +4.09e+00 | +4.01e+00 | +4.49e+00 | 0d:00:00:00 | +1.00e-03 | 641.1
    Training iterations 6  epsilon 0.69  ESS 460.8
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 2362.27 | 331.32
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,318.48
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:24 | 1    | 9,400     | +4.09e+00 | +4.01e+00 | +4.24e+00 | 0d:00:00:00 | +1.00e-03 | 1,240.8
    Stop condition reached. num_traces: 500
    0d:00:00:24 | 1    | 9,500     | +4.09e+00 | +4.01e+00 | +4.41e+00 | 0d:00:00:00 | +1.00e-03 | 1,121.4
    Training iterations 7  epsilon 0.65  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 2244.56 | 324.14
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,313.68
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:24 | 1    | 9,900     | +4.09e+00 | +4.01e+00 | +4.39e+00 | 0d:00:00:00 | +1.00e-03 | 1,121.7
    Stop condition reached. num_traces: 500
    0d:00:00:24 | 1    | 10,000    | +4.09e+00 | +4.01e+00 | +4.17e+00 | 0d:00:00:00 | +1.00e-03 | 1,082.3
    Training iterations 8  epsilon 0.64  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 2142.10 | 318.55
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,223.17
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:25 | 1    | 10,400    | +4.09e+00 | +4.01e+00 | +4.16e+00 | 0d:00:00:00 | +1.00e-03 | 1,187.5
    Stop condition reached. num_traces: 500
    0d:00:00:25 | 1    | 10,500    | +4.09e+00 | +4.01e+00 | +4.38e+00 | 0d:00:00:00 | +1.00e-03 | 1,085.1
    Training iterations 9  epsilon 0.63  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 1983.67 | 316.74
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,238.65
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:25 | 1    | 10,900    | +4.09e+00 | +4.01e+00 | +4.31e+00 | 0d:00:00:00 | +1.00e-03 | 1,086.6
    Stop condition reached. num_traces: 500
    0d:00:00:25 | 1    | 11,000    | +4.09e+00 | +4.01e+00 | +4.29e+00 | 0d:00:00:00 | +1.00e-03 | 1,091.1
    Training iterations 10  epsilon 0.61  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 1889.90 | 303.12
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,266.06
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:26 | 1    | 11,400    | +4.09e+00 | +4.01e+00 | +4.27e+00 | 0d:00:00:00 | +1.00e-03 | 1,063.9
    Stop condition reached. num_traces: 500
    0d:00:00:26 | 1    | 11,500    | +4.09e+00 | +4.01e+00 | +4.04e+00 | 0d:00:00:00 | +1.00e-03 | 1,094.0
    Training iterations 11  epsilon 0.58  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 1782.14 | 302.23
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,183.57
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:26 | 1    | 11,900    | +4.09e+00 | +4.01e+00 | +4.05e+00 | 0d:00:00:00 | +1.00e-03 | 1,209.8
    Stop condition reached. num_traces: 500
    0d:00:00:26 | 1    | 12,000    | +4.09e+00 | +4.01e+00 | +4.01e+00 | 0d:00:00:00 | +1.00e-03 | 1,102.1
    Training iterations 12  epsilon 0.55  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 1695.06 | 283.90
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,232.17
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:27 | 1    | 12,400    | +4.09e+00 | +3.99e+00 | +4.20e+00 | 0d:00:00:00 | +1.00e-03 | 911.4
    Stop condition reached. num_traces: 500
    0d:00:00:27 | 1    | 12,500    | +4.09e+00 | +3.99e+00 | +4.00e+00 | 0d:00:00:00 | +1.00e-03 | 828.0
    Training iterations 13  epsilon 0.54  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 1528.25 | 317.23
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,282.13
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:27 | 1    | 12,900    | +4.09e+00 | +3.94e+00 | +3.97e+00 | 0d:00:00:00 | +1.00e-03 | 1,109.1
    Stop condition reached. num_traces: 500
    0d:00:00:27 | 1    | 13,000    | +4.09e+00 | +3.94e+00 | +4.15e+00 | 0d:00:00:00 | +1.00e-03 | 1,095.7
    Training iterations 14  epsilon 0.54  ESS 499.8
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 1447.86 | 321.26
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,271.01
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:28 | 1    | 13,400    | +4.09e+00 | +3.90e+00 | +3.93e+00 | 0d:00:00:00 | +1.00e-03 | 1,117.5
    Stop condition reached. num_traces: 500
    0d:00:00:28 | 1    | 13,500    | +4.09e+00 | +3.90e+00 | +4.09e+00 | 0d:00:00:00 | +1.00e-03 | 1,077.0
    Training iterations 15  epsilon 0.53  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 1286.61 | 320.26
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,266.66
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:28 | 1    | 13,700    | +4.09e+00 | +3.90e+00 | +4.07e+00 | 0d:00:00:00 | +1.00e-03 | 383.3
    Stop condition reached. num_traces: 500
    0d:00:00:28 | 1    | 14,000    | +4.09e+00 | +3.85e+00 | +4.13e+00 | 0d:00:00:00 | +1.00e-03 | 1,190.1
    Training iterations 16  epsilon 0.50  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:15 | 0d:00:00:00 | #################### | 5000/5000 | 1273.39 | 318.78
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,282.99
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:29 | 1    | 14,400    | +4.09e+00 | +3.83e+00 | +4.10e+00 | 0d:00:00:00 | +1.00e-03 | 1,065.1
    Stop condition reached. num_traces: 500
    0d:00:00:29 | 1    | 14,500    | +4.09e+00 | +3.83e+00 | +3.85e+00 | 0d:00:00:00 | +1.00e-03 | 1,037.5
    Training iterations 17  epsilon 0.49  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 1192.40 | 310.16
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,233.45
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:29 | 1    | 14,900    | +4.09e+00 | +3.78e+00 | +4.02e+00 | 0d:00:00:00 | +1.00e-03 | 1,111.0
    Stop condition reached. num_traces: 500
    0d:00:00:29 | 1    | 15,000    | +4.09e+00 | +3.78e+00 | +3.81e+00 | 0d:00:00:00 | +1.00e-03 | 1,143.2
    Training iterations 18  epsilon 0.47  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 1027.75 | 278.29
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,069.76
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:30 | 1    | 15,400    | +4.09e+00 | +3.74e+00 | +4.03e+00 | 0d:00:00:00 | +1.00e-03 | 1,151.5
    Stop condition reached. num_traces: 500
    0d:00:00:30 | 1    | 15,500    | +4.09e+00 | +3.74e+00 | +3.92e+00 | 0d:00:00:00 | +1.00e-03 | 999.6
    Training iterations 19  epsilon 0.46  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 992.14 | 299.70
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,214.39
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:30 | 1    | 15,900    | +4.09e+00 | +3.74e+00 | +3.99e+00 | 0d:00:00:00 | +1.00e-03 | 375.9
    Stop condition reached. num_traces: 500
    0d:00:00:30 | 1    | 16,000    | +4.09e+00 | +3.70e+00 | +3.70e+00 | 0d:00:00:00 | +1.00e-03 | 1,074.9
    Training iterations 20  epsilon 0.46  ESS 490.5
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 938.56 | 298.76
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,225.71
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:31 | 1    | 16,400    | +4.09e+00 | +3.69e+00 | +3.92e+00 | 0d:00:00:00 | +1.00e-03 | 1,097.4
    Stop condition reached. num_traces: 500
    0d:00:00:31 | 1    | 16,500    | +4.09e+00 | +3.69e+00 | +3.72e+00 | 0d:00:00:00 | +1.00e-03 | 1,022.4
    Training iterations 21  epsilon 0.42  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 880.39 | 302.13
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,240.07
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:31 | 1    | 16,700    | +4.09e+00 | +3.69e+00 | +3.71e+00 | 0d:00:00:00 | +1.00e-03 | 362.9
    Stop condition reached. num_traces: 500
    0d:00:00:31 | 1    | 17,000    | +4.09e+00 | +3.65e+00 | +3.93e+00 | 0d:00:00:00 | +1.00e-03 | 1,138.0
    Training iterations 22  epsilon 0.40  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:19 | 0d:00:00:00 | #################### | 5000/5000 | 837.92 | 255.40
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,046.56
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:32 | 1    | 17,400    | +4.09e+00 | +3.62e+00 | +3.87e+00 | 0d:00:00:00 | +1.00e-03 | 888.6
    Stop condition reached. num_traces: 500
    0d:00:00:32 | 1    | 17,500    | +4.09e+00 | +3.62e+00 | +3.77e+00 | 0d:00:00:00 | +1.00e-03 | 901.6
    Training iterations 23  epsilon 0.39  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 739.02 | 280.70
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,160.36
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:32 | 1    | 17,900    | +4.09e+00 | +3.62e+00 | +3.85e+00 | 0d:00:00:00 | +1.00e-03 | 1,054.5
    Stop condition reached. num_traces: 500
    0d:00:00:32 | 1    | 18,000    | +4.09e+00 | +3.59e+00 | +3.59e+00 | 0d:00:00:00 | +1.00e-03 | 1,070.4
    Training iterations 24  epsilon 0.39  ESS 488.2
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 707.38 | 299.43
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,184.83
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:33 | 1    | 18,400    | +4.09e+00 | +3.59e+00 | +3.83e+00 | 0d:00:00:00 | +1.00e-03 | 1,052.5
    Stop condition reached. num_traces: 500
    0d:00:00:33 | 1    | 18,500    | +4.09e+00 | +3.59e+00 | +3.65e+00 | 0d:00:00:00 | +1.00e-03 | 987.1
    Training iterations 25  epsilon 0.36  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 680.71 | 280.31
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,150.72
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:33 | 1    | 18,900    | +4.09e+00 | +3.56e+00 | +3.56e+00 | 0d:00:00:00 | +1.00e-03 | 1,012.9
    Stop condition reached. num_traces: 500
    0d:00:00:34 | 1    | 19,000    | +4.09e+00 | +3.56e+00 | +3.84e+00 | 0d:00:00:00 | +1.00e-03 | 350.7
    Training iterations 26  epsilon 0.36  ESS 473.8
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 630.40 | 294.83
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,194.07
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:34 | 1    | 19,400    | +4.09e+00 | +3.55e+00 | +3.80e+00 | 0d:00:00:00 | +1.00e-03 | 1,091.6
    Stop condition reached. num_traces: 500
    0d:00:00:34 | 1    | 19,500    | +4.09e+00 | +3.55e+00 | +3.69e+00 | 0d:00:00:00 | +1.00e-03 | 998.5
    Training iterations 27  epsilon 0.33  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 591.38 | 294.53
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,163.28
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:35 | 1    | 19,900    | +4.09e+00 | +3.54e+00 | +3.68e+00 | 0d:00:00:00 | +1.00e-03 | 375.3
    Stop condition reached. num_traces: 500
    0d:00:00:35 | 1    | 20,000    | +4.09e+00 | +3.54e+00 | +3.61e+00 | 0d:00:00:00 | +1.00e-03 | 1,044.0
    Training iterations 28  epsilon 0.33  ESS 486.5
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 561.07 | 294.13
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,159.41
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:35 | 1    | 20,400    | +4.09e+00 | +3.53e+00 | +3.60e+00 | 0d:00:00:00 | +1.00e-03 | 1,063.9
    Stop condition reached. num_traces: 500
    0d:00:00:35 | 1    | 20,500    | +4.09e+00 | +3.53e+00 | +3.77e+00 | 0d:00:00:00 | +1.00e-03 | 1,076.4
    Training iterations 29  epsilon 0.33  ESS 486.1
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 548.13 | 298.65
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,162.47
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:35 | 1    | 20,700    | +4.09e+00 | +3.53e+00 | +3.66e+00 | 0d:00:00:00 | +1.00e-03 | 360.9
    Stop condition reached. num_traces: 500
    0d:00:00:36 | 1    | 21,000    | +4.09e+00 | +3.51e+00 | +3.59e+00 | 0d:00:00:00 | +1.00e-03 | 1,085.8
    Training iterations 30  epsilon 0.29  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:16 | 0d:00:00:00 | #################### | 5000/5000 | 397.66 | 294.77
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,230.32
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:36 | 1    | 21,400    | +4.09e+00 | +3.51e+00 | +3.78e+00 | 0d:00:00:00 | +1.00e-03 | 1,113.6
    Stop condition reached. num_traces: 500
    0d:00:00:36 | 1    | 21,500    | +4.09e+00 | +3.51e+00 | +3.65e+00 | 0d:00:00:00 | +1.00e-03 | 993.2
    Training iterations 31  epsilon 0.29  ESS 480.1
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 444.58 | 290.04
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,142.60
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:37 | 1    | 21,900    | +4.09e+00 | +3.51e+00 | +3.58e+00 | 0d:00:00:00 | +1.00e-03 | 1,010.3
    Stop condition reached. num_traces: 500
    0d:00:00:37 | 1    | 22,000    | +4.09e+00 | +3.49e+00 | +3.49e+00 | 0d:00:00:00 | +1.00e-03 | 1,084.0
    Training iterations 32  epsilon 0.29  ESS 480.9
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 292.59 | 293.23
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,182.49
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:37 | 1    | 22,400    | +4.09e+00 | +3.49e+00 | +3.64e+00 | 0d:00:00:00 | +1.00e-03 | 1,098.6
    Stop condition reached. num_traces: 500
    0d:00:00:37 | 1    | 22,500    | +4.09e+00 | +3.49e+00 | +3.73e+00 | 0d:00:00:00 | +1.00e-03 | 993.3
    Training iterations 33  epsilon 0.28  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 386.18 | 292.96
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,150.77
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:38 | 1    | 22,900    | +4.09e+00 | +3.48e+00 | +3.63e+00 | 0d:00:00:00 | +1.00e-03 | 1,022.4
    Stop condition reached. num_traces: 500
    0d:00:00:38 | 1    | 23,000    | +4.09e+00 | +3.48e+00 | +3.72e+00 | 0d:00:00:00 | +1.00e-03 | 353.4
    Training iterations 34  epsilon 0.28  ESS 488.4
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 325.80 | 288.08
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,149.33
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:38 | 1    | 23,400    | +4.09e+00 | +3.48e+00 | +3.72e+00 | 0d:00:00:00 | +1.00e-03 | 1,006.4
    Stop condition reached. num_traces: 500
    0d:00:00:38 | 1    | 23,500    | +4.09e+00 | +3.48e+00 | +3.62e+00 | 0d:00:00:00 | +1.00e-03 | 985.3
    Training iterations 35  epsilon 0.28  ESS 488.4
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 248.08 | 291.97
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,095.63
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:39 | 1    | 23,900    | +4.09e+00 | +3.47e+00 | +3.71e+00 | 0d:00:00:00 | +1.00e-03 | 371.9
    Stop condition reached. num_traces: 500
    0d:00:00:39 | 1    | 24,000    | +4.09e+00 | +3.47e+00 | +3.75e+00 | 0d:00:00:00 | +1.00e-03 | 1,006.6
    Training iterations 36  epsilon 0.28  ESS 461.2
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:17 | 0d:00:00:00 | #################### | 5000/5000 | 250.22 | 292.67
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,176.37
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:39 | 1    | 24,400    | +4.09e+00 | +3.46e+00 | +3.61e+00 | 0d:00:00:00 | +1.00e-03 | 1,113.7
    Stop condition reached. num_traces: 500
    0d:00:00:39 | 1    | 24,500    | +4.09e+00 | +3.46e+00 | +3.70e+00 | 0d:00:00:00 | +1.00e-03 | 1,004.1
    Training iterations 37  epsilon 0.28  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:18 | 0d:00:00:00 | #################### | 5000/5000 | 115.59 | 265.15
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,153.58
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:40 | 1    | 24,700    | +4.09e+00 | +3.46e+00 | +3.53e+00 | 0d:00:00:00 | +1.00e-03 | 366.0
    Stop condition reached. num_traces: 500
    0d:00:00:40 | 1    | 25,000    | +4.09e+00 | +3.45e+00 | +3.60e+00 | 0d:00:00:00 | +1.00e-03 | 1,086.6
    Training iterations 38  epsilon 0.28  ESS 436.8
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:19 | 0d:00:00:00 | #################### | 5000/5000 | 367.81 | 254.07
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 1,159.10
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:41 | 1    | 25,400    | +4.09e+00 | +3.45e+00 | +3.45e+00 | 0d:00:00:00 | +1.00e-03 | 1,043.8
    Stop condition reached. num_traces: 500
    0d:00:00:41 | 1    | 25,500    | +4.09e+00 | +3.45e+00 | +3.69e+00 | 0d:00:00:00 | +1.00e-03 | 1,015.7
    Training iterations 39  epsilon 0.25  ESS 500.0
    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:18 | 0d:00:00:00 | #################### | 5000/5000 | 283.52 | 269.26
    OfflineDataset at: .
    Num. traces      : 500
    Sorted on disk   : False
    No pre-computed hashes found, generating: ./pyprob_hashes
    Hashing offline dataset for sorting
    Time spent  | Time remain.| Progress             | Traces  | Traces/sec
    0d:00:00:00 | 0d:00:00:00 | #################### | 500/500 | 996.59
    Sorting offline dataset
    Sorting done
    Num. trace types : 1
    Trace hash	Count
    2.12636209	500

    Continuing to train existing inference network...
    Total number of parameters: 308
    Train. time | Epoch| Trace     | Init. loss| Min. loss | Curr. loss| T.since min | Learn.rate| Traces/sec
    0d:00:00:41 | 1    | 25,900    | +4.09e+00 | +3.44e+00 | +3.59e+00 | 0d:00:00:00 | +1.00e-03 | 699.7
    Stop condition reached. num_traces: 500
    0d:00:00:41 | 1    | 26,000    | +4.09e+00 | +3.44e+00 | +3.52e+00 | 0d:00:00:00 | +1.00e-03 | 804.9
    Training iterations 40  epsilon 0.25  ESS 437.0

```python
posterior = simulator.posterior(num_traces = 2000,
inference_engine = InferenceEngine.IMPORTANCE_SAMPLING_WITH_INFERENCE_NETWORK,
observe={"bool_func": 1})
posterior = simulator.update_DIS_posterior_weights(posterior)
results = np.array([x.result for x in posterior.values])
```

    Time spent  | Time remain.| Progress             | Trace     | ESS    | Traces/sec
    0d:00:00:06 | 0d:00:00:00 | #################### | 2000/2000 |  99.34 | 296.71

# Plots

First do checks for single training round. This is to make sure that the algorithm isn't somehow 'cheating'

```python
posterior_check = simulator.update_DIS_posterior_weights(posterior_check)
results_check = np.array([x.result for x in posterior_check.values])
```

```python
plt.hist(results_check, bins=50, weights=np.array(posterior_check.weights), label='posterior',density=True)
plt.hist(results_check, bins=50, label='proposal',density = True)
correct_dists.plot_prior(min(prior.values_numpy()),max(prior.values_numpy()))
correct_dists.plot_posterior(min(results), max(results))
plt.legend()
```

Now check full results

```python
plt.hist(results, bins=50, weights=np.array(posterior.weights), label='posterior',density=True)
plt.hist(results, bins=50, label='proposal',density = True)
correct_dists.plot_prior(min(prior.values_numpy()),max(prior.values_numpy()))
correct_dists.plot_posterior(min(results), max(results))
plt.legend()
```

# Roughs

```python
importance_weights = np.exp(np.array([x.log_importance_weight for x in posterior.values]))
importance_weights = importance_weights/sum(importance_weights)
weights = posterior.weights
probs = posterior._categorical.probs
print(importance_weights[:10])
print(np.array(weights[:10].detach()))
print(np.array(probs[:10].detach()))
```

    [4.43934743e-04 1.12809762e-03 6.99781531e-05 3.08431125e-04
     2.07236835e-04 1.65965433e-04 2.79483327e-04 1.51536002e-03
     4.46125723e-04 3.05139416e-04]
    [5.16142642e-11 2.43493937e-16 3.47589046e-11 2.37514305e-08
     7.04764763e-05 8.20254264e-05 1.54813143e-05 2.09902897e-24
     4.43681561e-06 7.53436269e-09]
    [5.16142642e-11 2.43493937e-16 3.47589046e-11 2.37514305e-08
     7.04764763e-05 8.20254264e-05 1.54813143e-05 2.09902897e-24
     4.43681561e-06 7.53436269e-09]

```python
print(importance_weights.sum())
print(weights.sum())
```

    1.0000000000000004
    tensor(0.0158, dtype=torch.float64)

```python
plt.hist(results, bins=20, weights=importance_weights, label='posterior')
plt.hist(results, bins=20, label='proposal',density = True)
correct_dists.plot_prior(min(prior.values_numpy()),max(prior.values_numpy()))
correct_dists.plot_posterior(min(results), max(results))
plt.legend()
```

Why do importance weights bring the posterior back to the prior?


# Kernel-Time-Based Regression (KTR)

In this section, we will cover:

- KTRLite model structure
- Syntax to call KTRLite class
- Utilities to check/visualize knot fitting results

We introduce a kernel-time-based regression model to capture trend and complex seasonality through time-varying
coefficients.

For more details, please refer to our white paper at arxiv [to be added].

```python
import pandas as pd
import numpy as np

import orbit
from orbit.models.ktrlite import KTRLiteMAP
from orbit.estimators.pyro_estimator import PyroEstimatorVI, PyroEstimatorMAP
from orbit.estimators.stan_estimator import StanEstimatorMCMC, StanEstimatorMAP

from orbit.utils.features import make_fourier_series_df, make_fourier_series
from orbit.diagnostics.plot import plot_predicted_data, plot_predicted_components, plot_ktr_lev_knots
from orbit.diagnostics.metrics import smape
from orbit.utils.dataset import load_iclaims, load_electricity_demand

import matplotlib.pyplot as plt
plt.style.use("fivethirtyeight")

%matplotlib inline
```

```python
assert orbit.__version__ == '1.0.14dev'
```

```python
pd.set_option('display.float_format', lambda x: '%.5f' % x)
```

## Model Structure

Our focus is a multiple regression model with time-varying coefficients of the following form

$$y_t = l_t + s_t + X_t^T\beta_t + \epsilon_t,  ~ t = 1,\cdots, T,$$

where $y_t$ is the response variable, $l_t$ is the trend term,  $s_t$ is the seasonality term,  $\beta_t = \beta(t)$ is a $p$-dimensional vector of coefficients which vary over time,  $X_t$ is a $p$-dimensional covariate, and $\epsilon_t$ is a stationary random error process.

We assume

\begin{equation}
\beta_{i,t} = \sum_{j=1}^{m} w_j(t) \cdot b_{i,j},
\end{equation}

where $i=1,\cdots,p, ~t=1,\cdots, T,~j=1,\cdots, m$. $p$ denotes the number of predictors,  $T$ the number of time points, $m$ the number of local weighting variables.  The local variables $b_{i,j}$ can be viewed as the ``knots" from the perspective of spline regression located at time $t_j, ~j=1,  \cdots,  m$.  $[t_1,  \cdots, t_m]$ is a subset of $[1, \cdots, T]$.  We can write the coefficient function for the $i$-th covariate as

$$\beta_i(t) = \beta(t, b_i),$$

where $b_i=[b_{i1}, b_{i2}, \cdots, b_{im}]$.  Or, simply in matrix form as

$$\beta=Kb^T,$$

where $\beta$ is the coefficient matrix with size $T\times p$, $K$ is the kernel matrix with size $T\times m$, and $b$ is the knot matrix with size $p\times m$.  Specifically,  the entry of matrix $K$ is the weight $w_j(t)$. We use kernel function as the weighting function,  i.e.,

$$w_j(t) = k_h(t_j, t)/\sum_{j=1}^{m} k_h(t_j, t),$$

where $k_h(\cdot, \cdot)$ is the kernel function, $h$ is the bandwidth.  The denominator is in order to have normalized weights that sum up to one.  In practice, we could use different kernels,  such as Gaussian kernel,  quadratic kernel  or any other customized kernel.

For the level/trend,

$$l_t = \beta_{\text{lev}},$$

$$\beta_{\text{lev}} = K_{\text{lev}}b^T_{\text{lev}}.$$

The trend term can be viewed as a dynamic intercept.

For the seasonality,

$$s = X_{\text{seas}}\beta_{\text{seas}},$$

$$\beta_{\text{seas}} = K_{\text{seas}}b^T_{\text{seas}}.$$

We use Fourier series to handle the seasonality such that seasonality terms can be expressed in a matrix $X_{seas}$.

For the regression,

$$r_\text{reg} = X_\text{reg}\beta_\text{reg},$$

$$\beta_\text{reg} = K_\text{reg}b^T_\text{reg}.$$

Instead of estimating the local knots (i.e., $b_\text{lev}$,  $b_\text{seas}$,  and $b_\text{reg}$ in the above equations) directly by optimizing an objective function,  we introduce the Bayesian priors into the framework by assuming probabilistic distributions for the local knots.

Specifically,

$$b^j_\text{lev} \sim \text{Laplace}(b^{j-1}_\text{lev},  \sigma_\text{lev}).$$

$$b_\text{seas} \sim \mathcal{N}(0,  \sigma^2_\text{seas}),$$

$$\mu_\text{reg} \sim \mathcal{N}(\mu_\text{pool}, \sigma^2_\text{pool}),$$

$$b_\text{reg} \sim \mathcal{N}(\mu_\text{reg},  \sigma^2_\text{reg}),$$

where $\mathcal{N}$ denotes the Gaussian distribution.

In KTRLite, there is no regression term, which is handled by KTRX. We will introduce KTRX in a separate notebook.

## Data

To illustrate the usage of KTRLite, we use the daily series of electricity demand in Turkey from year 2000 to year 2008. It is good to model the dual seasonality (weekly and yearly), since it's a daily series.

```python
# from 2000-01-01 to 2008-12-31
df = load_electricity_demand()

date_col = 'date'
response_col = 'electricity'

df[response_col] = np.log(df[response_col])

print(df.shape)
df.head()
```

    (3288, 2)

      date
      electricity

      0
      2000-01-01
      9.43760

      1
      2000-01-02
      9.50130

      2
      2000-01-03
      9.63565

      3
      2000-01-04
      9.65392

      4
      2000-01-05
      9.66089

```python
print(f'starts with {df[date_col].min()}\nends with {df[date_col].max()}\nshape: {df.shape}')
```

    starts with 2000-01-01 00:00:00
    ends with 2008-12-31 00:00:00
    shape: (3288, 2)

### Train / Test Split

Split the data into training set and test set.

```python
test_size=365
train_df=df[:-test_size]
test_df=df[-test_size:]
```

## KTRLite

In orbit, we mainly provide one type of KTRLite model, `KTRLiteMAP`, using Stan as the underlying sampling engine.

Similar to other model types in Orbit, it follows a sklearn style model API. We can create an instance of the Orbit class and then call its fit and predict methods.

Multiple flexible ways are available to control the number of knots in the model.
* span_level: relative window width between two knots.
* level_knot_dates: a list of dates corresponding to the knots.
* level_knot_length: the distance between every two knots.

```python
ktrlite = KTRLiteMAP(
    response_col=response_col,
    date_col=date_col,
    # seasonality
    seasonality=[7, 365.25],
    seasonality_fs_order=[2, 5],
    level_knot_scale=.1,
    span_level=.05,
    span_coefficients=.3,
    estimator_type=StanEstimatorMAP,
)
```

```python
ktrlite.fit(train_df)
```

```python
predicted_df = ktrlite.predict(df=test_df, decompose=True)
predicted_df.head()
```

      date
      prediction_5
      prediction
      prediction_95
      trend_5
      trend
      trend_95
      seasonality_7_5
      seasonality_7
      seasonality_7_95
      seasonality_365.25_5
      seasonality_365.25
      seasonality_365.25_95

      0
      2008-01-02
      9.89904
      10.01165
      10.12095
      9.90536
      9.99455
      10.08227
      0.02478
      0.02478
      0.02478
      -0.00820
      -0.00820
      -0.00820

      1
      2008-01-03
      9.90303
      10.01374
      10.12522
      9.90376
      9.99454
      10.08382
      0.02745
      0.02745
      0.02745
      -0.00879
      -0.00879
      -0.00879

      2
      2008-01-04
      9.91232
      10.02801
      10.14023
      9.90216
      9.99453
      10.08538
      0.04215
      0.04215
      0.04215
      -0.00922
      -0.00922
      -0.00922

      3
      2008-01-05
      9.83879
      9.95544
      10.06654
      9.90055
      9.99452
      10.08694
      -0.03015
      -0.03015
      -0.03015
      -0.00949
      -0.00949
      -0.00949

      4
      2008-01-06
      9.78003
      9.89762
      10.01268
      9.89895
      9.99451
      10.08849
      -0.08785
      -0.08785
      -0.08785
      -0.00961
      -0.00961
      -0.00961

```python
'{:.2%}'.format(smape(predicted_df['prediction'].values, test_df['electricity'].values))
```

    '0.46%'

```python
_ = plot_predicted_data(training_actual_df=train_df, predicted_df=predicted_df,
                        date_col=date_col, actual_col=response_col,
                        test_actual_df=test_df, markersize=20, lw=.5)
```

We can use `plot_predicted_components` to visualize each component.

```python
_ = plot_predicted_components(predicted_df=predicted_df, date_col=date_col,
                              plot_components=['trend', 'seasonality_7', 'seasonality_365.25'])
```

Moreover, we have `plot_ktr_lev_knots` in Orbit to inspect the fitted level knots against the actual series. It could help diagnose the potential problem of the knot fitting.

```python
lev_knots = ktrlite._aggregate_posteriors['map']['lev_knot']
lev_knot_dates = ktrlite._level_knot_dates
decomp_df = ktrlite.predict(df=train_df, decompose=True)
```

```python
_ = plot_ktr_lev_knots(train_df, decomp_df, date_col,
                       response_col, lev_knot_dates, lev_knots)
```