# The Result class

The `Result` class stores the output of a `Sampler` run, namely a collection of samples. It contains several methods for operating on the samples, including for **importance sampling**, **plotting**, and **density recovery**: 

```{eval-rst}
.. autoclass:: dingo.gw.result.Result
    :members:
    :inherited-members:
    :show-inheritance:
```

Following a sampler run, a `Result` can be obtained using `Sampler.to_result()`. Since `Result` inherits from `DingoDataset` it also possesses `to_file()` and `to_dictionary()` methods for saving samples and associated metadata (including context data, namely event data and ASDs).


## Density recovery

When sampling with GNPE, there is no direct access to the probability density $q(\theta|d)$. This is because of the Gibbs iterations: one only has access to the probability density of the entire chain, not just the final samples. The probability density is, however, needed for importance sampling, since this is the proposal distribution.

The `Result` class contains methods to enable *recovery* of the probability density for a collection of samples. The approach is as follows:
1. Start from the samples $\{(\theta_i, \hat\theta_i)\}_{i=1}^N$ from the final Gibbs iteration, including parameters $\theta$ and proxy parameters $\hat\theta$. By default these are included in the `samples` attribute generated by the `Sampler`.
2. Train an *unconditional* density estimator $q(\hat\theta)$ to model the proxy parameters. This is done by (1) using `parameter_subset()` to produce a new `Result` containing just the proxies, and (2) using `train_unconditional_flow()` on this subset.
3. Generate new samples $(\theta, \hat\theta) \sim q(\theta, \hat\theta | d) = q(\theta | d, \hat\theta) q(\hat\theta)$. This can be accomplished using `GNPESampler.sample()` with `num_iterations = 1` and setting the initial sampler to be the unconditional flow trained in the previous step. Since this does not involve multiple iterations, the density is obtained as well, so importance sampling can be performed.

```{note}
Density recovery can also be achieved using an unconditional density estimator for $\theta$ (trained on samples $\{\theta_i\}_{i=1}^N$ from GNPE). Since $\theta$ typically comprises 14 parameters (versus 2 or 3 for $\hat\theta$) it is usually more straightforward to learn the proxies.
```

## Synthetic phase

It is often challenging for Dingo to learn to model the `phase` parameter $\phi_c$. For this reason, we usually marginalize over it in training by excluding it from the list of `inference_parameters`. The phase is, however, required for importance sampling unless using also a phase-marginalized likelihood (which is approximate except under special circumstances).

The Dingo `gw.Result` class includes a method `sample_synthetic_phase()` which produces a $\phi_c$ sample from a $\phi_c$-marginalized sample. It does so by evaluating the likelihood on a $\phi_c$-grid and then sampling from the associated 1D distribution. The `log_prob` value for the sample is also corrected to reflect the sampled $\phi_c$. Speed is ensured by caching waveform modes and evaluating the polarizations for different $\phi_c$. For further details, see the Supplemental Material of {cite:p}`Dax:2022pxd`.

This method should be run *after* recovering the density, since in particular it applies a correction to the density.

### Configuration

The method `sample_synthetic_phase()` takes a kwargs argument. An example configuration is
```yaml
approximation_22_mode: false
n_grid: 5001
uniform_weight: 0.01
num_processes: 100
```
approximation_22_mode
: Whether to make the approximation that only the $(l, m) = (2, 2)$ mode is present, i.e., waveforms transform as $\exp{2\pi i \phi_c}$. This simplifies computations since it does not require caching of waveform modes.

n_grid
: Specifies the phase grid on which the likelihoods are evaluated.

uniform_weight
: Base probability level to add to ensure mass coverage.

num_processes
: For parallelization of synthetic phase sampling. This is usually the most expensive part of importance sampling, so it is advantageous to perform calculations in parallel.

## Importance sampling

Once samples are in the right form---including all relevant parameters *and* the log probability---importance sampling is carried out using the `importance_sample()` method. It allows to specify options for using a marginalized likelihood. (Time and phase marginalization are separately supported; see the documentation of {py:class}`dingo.gw.likelihood.StationaryGaussianGWLikelihood`.)

As with the synthetic phase, importance sampling allows for parallelization.

## Plotting

The plotting methods included here are intended for quick plots for evaluating results. They include

* **corner plots** comparing importance sampled and proposal results;
* **weights plots** to evaluate performance of importance sampling; and
* **log probability plots** comparing target and proposal log probability.
