From c323cf14b237f27882afa2c138700b7e6eec5382 Mon Sep 17 00:00:00 2001
From: Gregory Ashton <gregory.ashton@ligo.org>
Date: Wed, 2 Nov 2016 17:22:52 +0100
Subject: [PATCH] Adds basic functionality to compute the Bayes factor and an
 example

Note - There appears to be some difference between the integration plot
and the output value. This may be related to the Riemann sum issue in
emcee itself. Eventually, we should have a case where we calculate the
Bayes factor directly and compare this.
---
 examples/computing_the_Bayes_factor.py | 34 ++++++++++++++++++
 pyfstat.py                             | 49 ++++++++++++++++++++++++++
 2 files changed, 83 insertions(+)
 create mode 100644 examples/computing_the_Bayes_factor.py

diff --git a/examples/computing_the_Bayes_factor.py b/examples/computing_the_Bayes_factor.py
new file mode 100644
index 0000000..76d9256
--- /dev/null
+++ b/examples/computing_the_Bayes_factor.py
@@ -0,0 +1,34 @@
+from pyfstat import MCMCSearch
+
+F0 = 30.0
+F1 = -1e-10
+F2 = 0
+Alpha = 5e-3
+Delta = 6e-2
+tref = 362750407.0
+
+tstart = 1000000000
+duration = 100*86400
+tend = tstart + duration
+
+theta_prior = {'F0': {'type': 'unif', 'lower': F0*(1-1e-6), 'upper': F0*(1+1e-6)},
+               'F1': {'type': 'unif', 'lower': F1*(1+1e-2), 'upper': F1*(1-1e-2)},
+               'F2': F2,
+               'Alpha': Alpha,
+               'Delta': Delta
+               }
+
+ntemps = 20
+log10temperature_min = -2
+nwalkers = 100
+nsteps = [500, 500]
+
+mcmc = MCMCSearch(label='computing_the_Bayes_factor', outdir='data', 
+                  sftfilepath='data/*basic*sft', theta_prior=theta_prior,
+                  tref=tref, tstart=tstart, tend=tend, nsteps=nsteps,
+                  nwalkers=nwalkers, ntemps=ntemps,
+                  log10temperature_min=log10temperature_min)
+mcmc.run()
+mcmc.plot_corner(add_prior=True)
+mcmc.print_summary()
+mcmc.compute_evidence()
diff --git a/pyfstat.py b/pyfstat.py
index 8494d8b..973b162 100755
--- a/pyfstat.py
+++ b/pyfstat.py
@@ -1399,6 +1399,55 @@ class MCMCSearch(BaseSearchClass):
         print('p-value = {}'.format(p_val))
         return p_val
 
+    def compute_evidence(self):
+        """ Computes the evidence/marginal likelihood for the model """
+        fburnin = float(self.nsteps[-2])/np.sum(self.nsteps[-2:])
+        print fburnin
+        lnev, lnev_err = self.sampler.thermodynamic_integration_log_evidence(
+            fburnin=fburnin)
+
+        log10evidence = lnev/np.log(10)
+        log10evidence_err = lnev_err/np.log(10)
+        betas = self.betas
+        alllnlikes = self.sampler.lnlikelihood[:, :, self.nsteps[-2]:]
+        mean_lnlikes = np.mean(np.mean(alllnlikes, axis=1), axis=1)
+
+        mean_lnlikes = mean_lnlikes[::-1]
+        betas = betas[::-1]
+
+        fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6, 8))
+
+        if any(np.isinf(mean_lnlikes)):
+            print("WARNING mean_lnlikes contains inf: recalculating without"
+                  " the {} infs".format(len(betas[np.isinf(mean_lnlikes)])))
+            idxs = np.isinf(mean_lnlikes)
+            mean_lnlikes = mean_lnlikes[~idxs]
+            betas = betas[~idxs]
+            log10evidence = np.trapz(mean_lnlikes, betas)/np.log(10)
+            z1 = np.trapz(mean_lnlikes, betas)
+            z2 = np.trapz(mean_lnlikes[::-1][::2][::-1],
+                          betas[::-1][::2][::-1])
+            log10evidence_err = np.abs(z1 - z2) / np.log(10)
+
+        ax1.semilogx(betas, mean_lnlikes, "-o")
+        ax1.set_xlabel(r"$\beta$")
+        ax1.set_ylabel(r"$\langle \log(\mathcal{L}) \rangle$")
+        print("log10 evidence for {} = {} +/- {}".format(
+              self.label, log10evidence, log10evidence_err))
+        min_betas = []
+        evidence = []
+        for i in range(len(betas)/2):
+            min_betas.append(betas[i])
+            lnZ = np.trapz(mean_lnlikes[i:], betas[i:])
+            evidence.append(lnZ/np.log(10))
+
+        ax2.semilogx(min_betas, evidence, "-o")
+        ax2.set_ylabel(r"$\int_{\beta_{\textrm{Min}}}^{\beta=1}" +
+                       r"\langle \log(\mathcal{L})\rangle d\beta$", size=16)
+        ax2.set_xlabel(r"$\beta_{\textrm{min}}$")
+        plt.tight_layout()
+        fig.savefig("{}/{}_beta_lnl.png".format(self.outdir, self.label))
+
 
 class MCMCGlitchSearch(MCMCSearch):
     """ MCMC search using the SemiCoherentGlitchSearch """
-- 
GitLab