Package pylal :: Module bayespputils
[hide private]
[frames] | no frames]

Source Code for Module pylal.bayespputils

   1  # -*- coding: utf-8 -*- 
   2  # 
   3  #       bayespputils.py 
   4  # 
   5  #       Copyright 2010 
   6  #       Benjamin Aylott <benjamin.aylott@ligo.org>, 
   7  #       Benjamin Farr <bfarr@u.northwestern.edu>, 
   8  #       Will M. Farr <will.farr@ligo.org>, 
   9  #       John Veitch <john.veitch@ligo.org>, 
  10  #       Salvatore Vitale <salvatore.vitale@ligo.org>, 
  11  #       Vivien Raymond <vivien.raymond@ligo.org> 
  12  # 
  13  #       This program is free software; you can redistribute it and/or modify 
  14  #       it under the terms of the GNU General Public License as published by 
  15  #       the Free Software Foundation; either version 2 of the License, or 
  16  #       (at your option) any later version. 
  17  # 
  18  #       This program is distributed in the hope that it will be useful, 
  19  #       but WITHOUT ANY WARRANTY; without even the implied warranty of 
  20  #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  21  #       GNU General Public License for more details. 
  22  # 
  23  #       You should have received a copy of the GNU General Public License 
  24  #       along with this program; if not, write to the Free Software 
  25  #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
  26  #       MA 02110-1301, USA. 
  27   
  28  #=============================================================================== 
  29  # Preamble 
  30  #=============================================================================== 
  31   
  32  """ 
  33  This module contains classes and functions for post-processing the output 
  34  of the Bayesian parameter estimation codes. 
  35  """ 
  36   
  37  #standard library imports 
  38  import os 
  39  import sys 
  40  from math import cos,ceil,floor,sqrt,pi as pi_constant 
  41  import xml 
  42  from xml.dom import minidom 
  43  from operator import itemgetter 
  44   
  45  #related third party imports 
  46  from lalinference.io import read_samples 
  47  import healpy as hp 
  48  import astropy.table 
  49  import lalinference.plot.cmap 
  50  import numpy as np 
  51  from numpy import fmod 
  52  import matplotlib 
  53  from matplotlib import pyplot as plt,cm as mpl_cm,lines as mpl_lines 
  54  from scipy import stats 
  55  from scipy import special 
  56  from scipy import signal 
  57  from scipy.optimize import newton 
  58  from scipy import interpolate 
  59  from numpy import linspace 
  60  import random 
  61  import socket 
  62  from itertools import combinations 
  63  from lalinference import LALInferenceHDF5PosteriorSamplesDatasetName as posterior_grp_name 
  64  import re 
  65   
  66  try: 
  67      import lalsimulation as lalsim 
  68  except ImportError: 
  69      print('Cannot import lalsimulation SWIG bindings') 
  70      raise 
  71   
  72  try: 
  73      from lalinference.imrtgr.nrutils import bbh_final_mass_non_spinning_Panetal, bbh_final_spin_non_spinning_Panetal, bbh_final_spin_non_precessing_Healyetal, bbh_final_mass_non_precessing_Healyetal, bbh_final_spin_projected_spin_Healyetal, bbh_final_mass_projected_spin_Healyetal, bbh_aligned_Lpeak_6mode_SHXJDK 
  74  except ImportError: 
  75      print('Cannot import lalinference.imrtgr.nrutils. Will suppress final parameter calculations.') 
  76   
  77  from matplotlib.ticker import FormatStrFormatter,ScalarFormatter,AutoMinorLocator 
  78   
  79  try: 
  80    hostname_short=socket.gethostbyaddr(socket.gethostname())[0].split('.',1)[1] 
  81  except: 
  82    hostname_short='Unknown' 
  83  if hostname_short=='ligo.caltech.edu' or hostname_short=='cluster.ldas.cit': #The CIT cluster has troubles with the default 'cm' font. 'custom' has the least troubles, but does not include \odot 
  84    matplotlib.rcParams.update( 
  85                               {'mathtext.fontset' : "custom", 
  86                               'mathtext.fallback_to_cm' : True 
  87                               }) 
  88   
  89  from xml.etree.cElementTree import Element, SubElement, ElementTree, Comment, tostring, XMLParser 
  90   
  91  #local application/library specific imports 
  92  import pylal 
  93  import lal 
  94  from glue.ligolw import lsctables 
  95  from glue.ligolw import utils 
  96  from pylal import git_version 
  97  #C extensions 
  98  from _bayespputils import _skyhist_cart,_burnin 
  99   
 100  __author__="Ben Aylott <benjamin.aylott@ligo.org>, Ben Farr <bfarr@u.northwestern.edu>, Will M. Farr <will.farr@ligo.org>, John Veitch <john.veitch@ligo.org>, Vivien Raymond <vivien.raymond@ligo.org>" 
 101  __version__= "git id %s"%git_version.id 
 102  __date__= git_version.date 
103 104 -def replace_column(table, old, new):
105 """Workaround for missing astropy.table.Table.replace_column method, 106 which was added in Astropy 1.1. 107 108 FIXME: remove this function when LALSuite depends on Astropy >= 1.1.""" 109 index = table.colnames.index(old) 110 table.remove_column(old) 111 table.add_column(astropy.table.Column(new, name=old), index=index)
112
113 -def as_array(table):
114 """Workaround for missing astropy.table.Table.as_array method, 115 which was added in Astropy 1.0. 116 117 FIXME: remove this function when LALSuite depends on Astropy >= 1.0.""" 118 try: 119 return table.as_array() 120 except: 121 return table._data
122 123 #=============================================================================== 124 # Constants 125 #=============================================================================== 126 #Parameters which are not to be exponentiated when found 127 logParams=['logl','loglh1','loglh2','logll1','loglv1','deltalogl','deltaloglh1','deltalogll1','deltaloglv1','logw','logprior','logpost','nulllogl','chain_log_evidence','chain_delta_log_evidence','chain_log_noise_evidence','chain_log_bayes_factor'] 128 #Parameters known to cbcBPP 129 relativePhaseParams=[ a+b+'_relative_phase' for a,b in combinations(['h1','l1','v1'],2)] 130 snrParams=['snr','optimal_snr','matched_filter_snr'] + ['%s_optimal_snr'%(i) for i in ['h1','l1','v1']] + ['%s_cplx_snr_amp'%(i) for i in ['h1','l1','v1']] + ['%s_cplx_snr_arg'%(i) for i in ['h1', 'l1', 'v1']] + relativePhaseParams 131 calAmpParams=['calamp_%s'%(ifo) for ifo in ['h1','l1','v1']] 132 calPhaseParams=['calpha_%s'%(ifo) for ifo in ['h1','l1','v1']] 133 calParams = calAmpParams + calPhaseParams 134 # Masses 135 massParams=['m1','m2','chirpmass','mchirp','mc','eta','q','massratio','asym_massratio','mtotal','mf'] 136 #Spins 137 spinParamsPrec=['a1','a2','phi1','theta1','phi2','theta2','costilt1','costilt2','costheta_jn','cosbeta','tilt1','tilt2','phi_jl','theta_jn','phi12','af'] 138 spinParamsAli=['spin1','spin2','a1z','a2z'] 139 spinParamsEff=['chi','effectivespin','chi_eff','chi_tot','chi_p'] 140 spinParams=spinParamsPrec+spinParamsEff+spinParamsAli 141 # Source frame params 142 cosmoParam=['m1_source','m2_source','mtotal_source','mc_source','redshift','mf_source'] 143 #Strong Field 144 ppEParams=['ppEalpha','ppElowera','ppEupperA','ppEbeta','ppElowerb','ppEupperB','alphaPPE','aPPE','betaPPE','bPPE'] 145 tigerParams=['dchi%i'%(i) for i in range(8)] + ['dchi%il'%(i) for i in [5,6] ] + ['dxi%d'%(i+1) for i in range(6)] + ['dalpha%i'%(i+1) for i in range(5)] + ['dbeta%i'%(i+1) for i in range(3)] + ['dsigma%i'%(i+1) for i in range(4)] 146 bransDickeParams=['omegaBD','ScalarCharge1','ScalarCharge2'] 147 massiveGravitonParams=['lambdaG'] 148 tidalParams=['lambda1','lambda2','lam_tilde','dlam_tilde','lambdat','dlambdat'] 149 energyParams=['e_rad', 'l_peak'] 150 strongFieldParams=ppEParams+tigerParams+bransDickeParams+massiveGravitonParams+tidalParams+energyParams 151 152 #Extrinsic 153 distParams=['distance','distMPC','dist'] 154 incParams=['iota','inclination','cosiota'] 155 polParams=['psi','polarisation','polarization'] 156 skyParams=['ra','rightascension','declination','dec'] 157 phaseParams=['phase', 'phi0','phase_maxl'] 158 #Times 159 timeParams=['time','time_mean'] 160 endTimeParams=['l1_end_time','h1_end_time','v1_end_time'] 161 #others 162 statsParams=['logprior','logl','deltalogl','deltaloglh1','deltalogll1','deltaloglv1','deltaloglh2','deltaloglg1'] 163 calibParams=['calpha_l1','calpha_h1','calpha_v1','calamp_l1','calamp_h1','calamp_v1'] 164 165 ## Greedy bin sizes for cbcBPP and confidence leves used for the greedy bin intervals 166 confidenceLevels=[0.67,0.9,0.95,0.99] 167 168 greedyBinSizes={'mc':0.025,'m1':0.1,'m2':0.1,'mass1':0.1,'mass2':0.1,'mtotal':0.1,'mc_source':0.025,'m1_source':0.1,'m2_source':0.1,'mtotal_source':0.1,'eta':0.001,'q':0.01,'asym_massratio':0.01,'iota':0.01,'cosiota':0.02,'time':1e-4,'time_mean':1e-4,'distance':1.0,'dist':1.0,'redshift':0.01,'mchirp':0.025,'chirpmass':0.025,'spin1':0.04,'spin2':0.04,'a1z':0.04,'a2z':0.04,'a1':0.02,'a2':0.02,'phi1':0.05,'phi2':0.05,'theta1':0.05,'theta2':0.05,'ra':0.05,'dec':0.05,'chi':0.05,'chi_eff':0.05,'chi_tot':0.05,'chi_p':0.05,'costilt1':0.02,'costilt2':0.02,'thatas':0.05,'costheta_jn':0.02,'beta':0.05,'omega':0.05,'cosbeta':0.02,'ppealpha':1.0,'ppebeta':1.0,'ppelowera':0.01,'ppelowerb':0.01,'ppeuppera':0.01,'ppeupperb':0.01,'polarisation':0.04,'rightascension':0.05,'declination':0.05,'massratio':0.001,'inclination':0.01,'phase':0.05,'tilt1':0.05,'tilt2':0.05,'phi_jl':0.05,'theta_jn':0.05,'phi12':0.05,'flow':1.0,'phase_maxl':0.05,'calamp_l1':0.01,'calamp_h1':0.01,'calamp_v1':0.01,'calpha_h1':0.01,'calpha_l1':0.01,'calpha_v1':0.01,'logdistance':0.1,'psi':0.1,'costheta_jn':0.1,'mf':0.1,'mf_source':0.1,'af':0.02,'e_rad':0.1,'l_peak':0.1} 169 for s in snrParams: 170 greedyBinSizes[s]=0.05 171 for derived_time in ['h1_end_time','l1_end_time','v1_end_time','h1l1_delay','l1v1_delay','h1v1_delay']: 172 greedyBinSizes[derived_time]=greedyBinSizes['time'] 173 for derived_phase in relativePhaseParams: 174 greedyBinSizes[derived_phase]=0.05 175 for param in tigerParams + bransDickeParams + massiveGravitonParams: 176 greedyBinSizes[param]=0.01 177 for param in tidalParams: 178 greedyBinSizes[param]=2.5 179 #Confidence levels 180 for loglname in statsParams: 181 greedyBinSizes[loglname]=0.1 182 183 #Pre-defined ordered list of line styles for use in matplotlib contour plots. 184 __default_line_styles=['solid', 'dashed', 'dashdot', 'dotted'] 185 #Pre-defined ordered list of matplotlib colours for use in plots. 186 __default_color_lst=['r','b','y','g','c','m'] 187 #A default css string for use in html results pages. 188 __default_css_string=""" 189 p,h1,h2,h3,h4,h5 190 { 191 font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; 192 } 193 194 p 195 { 196 font-size:14px; 197 } 198 199 h1 200 { 201 font-size:20px; 202 } 203 204 h2 205 { 206 font-size:18px; 207 } 208 209 h3 210 { 211 font-size:16px; 212 } 213 214 215 216 table 217 { 218 font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; 219 width:100%; 220 border-collapse:collapse; 221 } 222 td,th 223 { 224 font-size:12px; 225 border:1px solid #B5C1CF; 226 padding:3px 7px 2px 7px; 227 } 228 th 229 { 230 font-size:14px; 231 text-align:left; 232 padding-top:5px; 233 padding-bottom:4px; 234 background-color:#B3CEEF; 235 color:#ffffff; 236 } 237 #postable tr:hover 238 { 239 background: #DFF4FF; 240 } 241 #covtable tr:hover 242 { 243 background: #DFF4FF; 244 } 245 #statstable tr:hover 246 { 247 background: #DFF4FF; 248 } 249 250 img { 251 max-width: 510px; 252 max-height: 510px; 253 width:100%; 254 eight:100%; 255 } 256 257 .ppsection 258 { 259 border-bottom-style:double; 260 } 261 262 """ 263 __default_javascript_string=''' 264 //<![CDATA[ 265 function toggle_visibility(tbid,lnkid) 266 { 267 268 if(document.all){document.getElementById(tbid).style.display = document.getElementById(tbid).style.display == 'block' ? 'none' : 'block';} 269 270 else{document.getElementById(tbid).style.display = document.getElementById(tbid).style.display == 'table' ? 'none' : 'table';} 271 272 document.getElementById(lnkid).value = document.getElementById(lnkid).value == '[-] Collapse' ? '[+] Expand' : '[-] Collapse'; 273 274 } 275 //]]> 276 277 ''' 278 279 #import sim inspiral table content handler 280 from pylal.SimInspiralUtils import ExtractSimInspiralTableLIGOLWContentHandler 281 from glue.ligolw import lsctables 282 lsctables.use_in(ExtractSimInspiralTableLIGOLWContentHandler)
283 284 #=============================================================================== 285 # Function to return the correct prior distribution for selected parameters 286 #=============================================================================== 287 -def get_prior(name):
288 distributions={ 289 'm1':'uniform', 290 'm2':'uniform', 291 'mc':None, 292 'eta':None, 293 'q':None, 294 'mtotal':'uniform', 295 'm1_source':None, 296 'm2_source':None, 297 'mtotal_source':None, 298 'mc_source':None, 299 'redshift':None, 300 'mf':None, 301 'mf_source':None, 302 'af':None, 303 'e_rad':None, 304 'l_peak':None, 305 'spin1':'uniform', 306 'spin2':'uniform', 307 'a1':'uniform', 308 'a2':'uniform', 309 'a1z':'uniform', 310 'a2z':'uniform', 311 'theta1':'uniform', 312 'theta2':'uniform', 313 'phi1':'uniform', 314 'phi2':'uniform', 315 'chi_eff':None, 316 'chi_tot':None, 317 'chi_p':None, 318 'tilt1':None, 319 'tilt2':None, 320 'costilt1':'uniform', 321 'costilt2':'uniform', 322 'iota':'np.cos', 323 'cosiota':'uniform', 324 'time':'uniform', 325 'time_mean':'uniform', 326 'dist':'x**2', 327 'ra':'uniform', 328 'dec':'np.cos', 329 'phase':'uniform', 330 'psi':'uniform', 331 'theta_jn':'np.sin', 332 'costheta_jn':'uniform', 333 'beta':None, 334 'cosbeta':None, 335 'phi_jl':None, 336 'phi12':None, 337 'logl':None, 338 'h1_end_time':None, 339 'l1_end_time':None, 340 'v1_end_time':None, 341 'h1l1_delay':None, 342 'h1v1_delay':None, 343 'l1v1_delay':None, 344 'lambdat' :None, 345 'dlambdat':None, 346 'lambda1' : 'uniform', 347 'lambda2': 'uniform', 348 'lam_tilde' : None, 349 'dlam_tilde': None, 350 'calamp_h1' : 'uniform', 351 'calamp_l1' : 'uniform', 352 'calpha_h1' : 'uniform', 353 'calpha_l1' : 'uniform', 354 'polar_eccentricity':'uniform', 355 'polar_angle':'uniform', 356 'alpha':'uniform' 357 } 358 try: 359 return distributions(name) 360 except: 361 return None
362
363 #=============================================================================== 364 # Function used to generate plot labels. 365 #=============================================================================== 366 -def plot_label(param):
367 """ 368 A lookup table for plot labels. 369 """ 370 m1_names = ['mass1', 'm1'] 371 m2_names = ['mass2', 'm2'] 372 mc_names = ['mc','mchirp','chirpmass'] 373 eta_names = ['eta','massratio','sym_massratio'] 374 q_names = ['q','asym_massratio'] 375 iota_names = ['iota','incl','inclination'] 376 dist_names = ['dist','distance'] 377 ra_names = ['rightascension','ra'] 378 dec_names = ['declination','dec'] 379 phase_names = ['phi_orb', 'phi', 'phase', 'phi0'] 380 gr_test_names = ['dchi%d'%i for i in range(8)]+['dchil%d'%i for i in [5,6]]+['dxi%d'%(i+1) for i in range(6)]+['dalpha%d'%(i+1) for i in range(5)]+['dbeta%d'%(i+1) for i in range(3)]+['dsigma%d'%(i+1) for i in range(4)] 381 382 labels={ 383 'm1':r'$m_1\,(\mathrm{M}_\odot)$', 384 'm2':r'$m_2\,(\mathrm{M}_\odot)$', 385 'mc':r'$\mathcal{M}\,(\mathrm{M}_\odot)$', 386 'eta':r'$\eta$', 387 'q':r'$q$', 388 'mtotal':r'$M_\mathrm{total}\,(\mathrm{M}_\odot)$', 389 'm1_source':r'$m_{1}^\mathrm{source}\,(\mathrm{M}_\odot)$', 390 'm2_source':r'$m_{2}^\mathrm{source}\,(\mathrm{M}_\odot)$', 391 'mtotal_source':r'$M_\mathrm{total}^\mathrm{source}\,(\mathrm{M}_\odot)$', 392 'mc_source':r'$\mathcal{M}^\mathrm{source}\,(\mathrm{M}_\odot)$', 393 'redshift':r'$z$', 394 'mf':r'$M_\mathrm{final}\,(\mathrm{M}_\odot)$', 395 'mf_source':r'$M_\mathrm{final}^\mathrm{source}\,(\mathrm{M}_\odot)$', 396 'af':r'$a_\mathrm{final}$', 397 'e_rad':r'$E_\mathrm{rad}\,(\mathrm{M}_\odot)$', 398 'l_peak':r'$L_\mathrm{peak}\,(10^{56}\,\mathrm{ergs}\,\mathrm{s}^{-1})$', 399 'spin1':r'$S_1$', 400 'spin2':r'$S_2$', 401 'a1':r'$a_1$', 402 'a2':r'$a_2$', 403 'a1z':r'$a_{1z}$', 404 'a2z':r'$a_{2z}$', 405 'theta1':r'$\theta_1\,(\mathrm{rad})$', 406 'theta2':r'$\theta_2\,(\mathrm{rad})$', 407 'phi1':r'$\phi_1\,(\mathrm{rad})$', 408 'phi2':r'$\phi_2\,(\mathrm{rad})$', 409 'chi_eff':r'$\chi_\mathrm{eff}$', 410 'chi_tot':r'$\chi_\mathrm{total}$', 411 'chi_p':r'$\chi_\mathrm{P}$', 412 'tilt1':r'$t_1\,(\mathrm{rad})$', 413 'tilt2':r'$t_2\,(\mathrm{rad})$', 414 'costilt1':r'$\mathrm{cos}(t_1)$', 415 'costilt2':r'$\mathrm{cos}(t_2)$', 416 'iota':r'$\iota\,(\mathrm{rad})$', 417 'cosiota':r'$\mathrm{cos}(\iota)$', 418 'time':r'$t_\mathrm{c}\,(\mathrm{s})$', 419 'time_mean':r'$<t>\,(\mathrm{s})$', 420 'dist':r'$d_\mathrm{L}\,(\mathrm{Mpc})$', 421 'ra':r'$\alpha$', 422 'dec':r'$\delta$', 423 'phase':r'$\phi\,(\mathrm{rad})$', 424 'psi':r'$\psi\,(\mathrm{rad})$', 425 'theta_jn':r'$\theta_\mathrm{JN}\,(\mathrm{rad})$', 426 'costheta_jn':r'$\mathrm{cos}(\theta_\mathrm{JN})$', 427 'beta':r'$\beta\,(\mathrm{rad})$', 428 'cosbeta':r'$\mathrm{cos}(\beta)$', 429 'phi_jl':r'$\phi_\mathrm{JL}\,(\mathrm{rad})$', 430 'phi12':r'$\phi_\mathrm{12}\,(\mathrm{rad})$', 431 'logl':r'$\mathrm{log}(\mathcal{L})$', 432 'h1_end_time':r'$t_\mathrm{H}$', 433 'l1_end_time':r'$t_\mathrm{L}$', 434 'v1_end_time':r'$t_\mathrm{V}$', 435 'h1l1_delay':r'$\Delta t_\mathrm{HL}$', 436 'h1v1_delay':r'$\Delta t_\mathrm{HV}$', 437 'l1v1_delay':r'$\Delta t_\mathrm{LV}$', 438 'lambdat' : r'$\tilde{\Lambda}$', 439 'dlambdat': r'$\delta \tilde{\Lambda}$', 440 'lambda1' : r'$\lambda_1$', 441 'lambda2': r'$\lambda_2$', 442 'lam_tilde' : r'$\tilde{\Lambda}$', 443 'dlam_tilde': r'$\delta \tilde{\Lambda}$', 444 'calamp_h1' : r'$\delta A_{H1}$', 445 'calamp_l1' : r'$\delta A_{L1}$', 446 'calpha_h1' : r'$\delta \phi_{H1}$', 447 'calpha_l1' : r'$\delta \phi_{L1}$', 448 'polar_eccentricity':r'$\epsilon_{polar}$', 449 'polar_angle':r'$\alpha_{polar}$', 450 'alpha':r'$\alpha_{polar}$', 451 'dchi0':r'$d\chi_0$', 452 'dchi1':r'$d\chi_1$', 453 'dchi2':r'$d\chi_2$', 454 'dchi3':r'$d\chi_3$', 455 'dchi4':r'$d\chi_4$', 456 'dchi5':r'$d\chi_5$', 457 'dchi5l':r'$d\chi_{5}^{(l)}$', 458 'dchi6':r'$d\chi_6$', 459 'dchi6l':r'$d\chi_{6}^{(l)}$', 460 'dchi7':r'$d\chi_7$', 461 'dxi1':r'$d\xi_1$', 462 'dxi2':r'$d\xi_2$', 463 'dxi3':r'$d\xi_3$', 464 'dxi4':r'$d\xi_4$', 465 'dxi5':r'$d\xi_5$', 466 'dxi6':r'$d\xi_6$', 467 'dalpha1':r'$d\alpha_1$', 468 'dalpha2':r'$d\alpha_2$', 469 'dalpha3':r'$d\alpha_3$', 470 'dalpha4':r'$d\alpha_4$', 471 'dalpha5':r'$d\alpha_5$', 472 'dbeta1':r'$d\beta_1$', 473 'dbeta2':r'$d\beta_2$', 474 'dbeta3':r'$d\beta_3$', 475 'dsigma1':r'$d\sigma_1$', 476 'dsigma2':r'$d\sigma_2$', 477 'dsigma3':r'$d\sigma_3$', 478 'dsigma4':r'$d\sigma_4$', 479 } 480 481 # Handle cases where multiple names have been used 482 if param in m1_names: 483 param = 'm1' 484 elif param in m2_names: 485 param = 'm2' 486 elif param in mc_names: 487 param = 'mc' 488 elif param in eta_names: 489 param = 'eta' 490 elif param in q_names: 491 param = 'q' 492 elif param in iota_names: 493 param = 'iota' 494 elif param in dist_names: 495 param = 'dist' 496 elif param in ra_names: 497 param = 'ra' 498 elif param in dec_names: 499 param = 'dec' 500 elif param in phase_names: 501 param = 'phase' 502 503 try: 504 label = labels[param] 505 except KeyError: 506 # Use simple string if no formated label is available for param 507 label = param 508 509 return label
510
511 #=============================================================================== 512 # Class definitions 513 #=============================================================================== 514 515 -class PosteriorOneDPDF(object):
516 """ 517 A data structure representing one parameter in a chain of posterior samples. 518 The Posterior class generates instances of this class for pivoting onto a given 519 parameter (the Posterior class is per-Sampler oriented whereas this class represents 520 the same one parameter in successive samples in the chain). 521 """
522 - def __init__(self,name,posterior_samples,injected_value=None,injFref=None,trigger_values=None,prior=None):
523 """ 524 Create an instance of PosteriorOneDPDF based on a table of posterior_samples. 525 526 @param name: A literal string name for the parameter. 527 @param posterior_samples: A 1D array of the samples. 528 @keyword injected_value: The injected or real value of the parameter. 529 @type injected_value: glue.ligolw.lsctables.SimInspiral 530 @keyword trigger_values: The trigger values of the parameter (dictionary w/ IFOs as keys). 531 @keyword prior: The prior value corresponding to each sample. 532 """ 533 self.__name=name 534 self.__posterior_samples=np.array(posterior_samples) 535 536 self.__injFref=injFref 537 self.__injval=injected_value 538 self.__trigvals=trigger_values 539 self.__prior=prior 540 541 return
542
543 - def __len__(self):
544 """ 545 Container method. Defined as number of samples. 546 """ 547 return len(self.__posterior_samples)
548
549 - def __getitem__(self,idx):
550 """ 551 Container method . Returns posterior containing sample idx (allows slicing). 552 """ 553 return PosteriorOneDPDF(self.__name, self.__posterior_samples[idx], injected_value=self.__injval, f_ref=self.__f_ref, trigger_values=self.__trigvals)
554 555 @property
556 - def name(self):
557 """ 558 Return the string literal name of the parameter. 559 560 @rtype: string 561 """ 562 return self.__name
563 564 @property
565 - def mean(self):
566 """ 567 Return the arithmetic mean for the marginal PDF on the parameter. 568 569 @rtype: number 570 """ 571 return np.mean(self.__posterior_samples)
572 573 @property
574 - def median(self):
575 """ 576 Return the median value for the marginal PDF on the parameter. 577 578 @rtype: number 579 """ 580 return np.median(self.__posterior_samples)
581 582 @property
583 - def stdev(self):
584 """ 585 Return the standard deviation of the marginal PDF on the parameter. 586 587 @rtype: number 588 """ 589 try: 590 stdev = sqrt(np.var(self.__posterior_samples)) 591 if not np.isfinite(stdev): 592 raise OverflowError 593 except OverflowError: 594 mean = np.mean(self.__posterior_samples) 595 stdev = mean * sqrt(np.var(self.__posterior_samples/mean)) 596 return stdev
597 598 @property
599 - def stacc(self):
600 """ 601 Return the 'standard accuracy statistic' (stacc) of the marginal 602 posterior of the parameter. 603 604 stacc is a standard deviant incorporating information about the 605 accuracy of the waveform recovery. Defined as the mean of the sum 606 of the squared differences between the points in the PDF 607 (x_i - sampled according to the posterior) and the true value 608 (x_{\rm true}). So for a marginalized one-dimensional PDF: 609 stacc = \sqrt{\frac{1}{N}\sum_{i=1}^N (x_i-x_{\rm true})2} 610 611 @rtype: number 612 """ 613 if self.__injval is None: 614 return None 615 else: 616 return np.sqrt(np.mean((self.__posterior_samples - self.__injval)**2.0))
617 618 @property
619 - def injval(self):
620 """ 621 Return the injected value set at construction . If no value was set 622 will return None . 623 624 @rtype: undefined 625 """ 626 return self.__injval
627 628 @property
629 - def trigvals(self):
630 """ 631 Return the trigger values set at construction. If no value was set 632 will return None . 633 634 @rtype: undefined 635 """ 636 return self.__trigvals
637 638 #@injval.setter #Python 2.6+
639 - def set_injval(self,new_injval):
640 """ 641 Set the injected/real value of the parameter. 642 643 @param new_injval: The injected/real value to set. 644 @type new_injval: glue.ligolw.lsctables.SimInspiral 645 """ 646 647 self.__injval=new_injval
648
649 - def set_trigvals(self,new_trigvals):
650 """ 651 Set the trigger values of the parameter. 652 653 @param new_trigvals: Dictionary containing trigger values with IFO keys. 654 @type new_trigvals: glue.ligolw.lsctables.SnglInspiral 655 """ 656 657 self.__trigvals=new_trigvals
658 659 @property
660 - def samples(self):
661 """ 662 Return a 1D numpy.array of the samples. 663 664 @rtype: numpy.array 665 """ 666 return self.__posterior_samples
667
668 - def delete_samples_by_idx(self,samples):
669 """ 670 Remove samples from posterior, analagous to numpy.delete but opperates in place. 671 672 @param samples: A list containing the indexes of the samples to be removed. 673 @type samples: list 674 """ 675 self.__posterior_samples=np.delete(self.__posterior_samples,samples).reshape(-1,1)
676 677 @property
678 - def gaussian_kde(self):
679 """ 680 Return a SciPy gaussian_kde (representing a Gaussian KDE) of the samples. 681 682 @rtype: scipy.stats.kde.gaussian_kde 683 """ 684 from numpy import seterr as np_seterr 685 from scipy import seterr as sp_seterr 686 687 np_seterr(under='ignore') 688 sp_seterr(under='ignore') 689 try: 690 return_value=stats.kde.gaussian_kde(np.transpose(self.__posterior_samples)) 691 except: 692 exfile=open('exception.out','w') 693 np.savetxt(exfile,self.__posterior_samples) 694 exfile.close() 695 raise 696 697 return return_value
698 699 @property
700 - def KL(self):
701 """Returns the KL divergence between the prior and the posterior. 702 It measures the relative information content in nats. The prior is evaluated 703 at run time. It defaults to None. If None is passed, it just returns the information content 704 of the posterior." 705 """ 706 707 def uniform(x): 708 return np.array([1./(np.max(x)-np.min(x)) for _ in x])
709 710 posterior, dx = np.histogram(self.samples,bins=36,normed=True) 711 from scipy.stats import entropy 712 # check the kind of prior and process the string accordingly 713 prior = get_prior(self.name) 714 if prior is None: 715 raise ValueError 716 elif prior=='uniform': 717 prior+='(self.samples)' 718 elif 'x' in prior: 719 prior.replace('x','self.samples') 720 elif not(prior.startswith('np.')): 721 prior = 'np.'+prior 722 prior+='(self.samples)' 723 else: 724 raise ValueError 725 726 try: 727 prior_dist = eval(prior) 728 except: 729 raise ValueError 730 731 return entropy(posterior, qk=prior_dist)
732
733 - def prob_interval(self,intervals):
734 """ 735 Evaluate probability intervals. 736 737 @param intervals: A list of the probability intervals [0-1] 738 @type intervals: list 739 """ 740 list_of_ci=[] 741 samples_temp=np.sort(np.squeeze(self.samples)) 742 743 for interval in intervals: 744 if interval<1.0: 745 samples_temp 746 N=np.size(samples_temp) 747 #Find index of lower bound 748 lower_idx=int(floor((N/2.0)*(1-interval))) 749 if lower_idx<0: 750 lower_idx=0 751 #Find index of upper bound 752 upper_idx=N-int(floor((N/2.0)*(1-interval))) 753 if upper_idx>N: 754 upper_idx=N-1 755 756 list_of_ci.append((float(samples_temp[lower_idx]),float(samples_temp[upper_idx]))) 757 else: 758 list_of_ci.append((None,None)) 759 760 return list_of_ci
761
762 -class Posterior(object):
763 """ 764 Data structure for a table of posterior samples . 765 """
766 - def __init__(self,commonResultsFormatData,SimInspiralTableEntry=None,inj_spin_frame='OrbitalL', injFref=100,SnglInpiralList=None,name=None,description=None,votfile=None):
767 """ 768 Constructor. 769 770 @param commonResultsFormatData: A 2D array containing the posterior 771 samples and related data. The samples chains form the columns. 772 @type commonResultsFormatData: custom type 773 @param SimInspiralTableEntry: A SimInspiralTable row containing the injected values. 774 @type SimInspiralTableEntry: glue.ligolw.lsctables.SimInspiral 775 @param SnglInspiralList: A list of SnglInspiral objects containing the triggers. 776 @type SnglInspiralList: list 777 778 """ 779 common_output_table_header,common_output_table_raw =commonResultsFormatData 780 self._posterior={} 781 self._injFref=injFref 782 self._injection=SimInspiralTableEntry 783 784 self._triggers=SnglInpiralList 785 self._loglaliases=['deltalogl', 'posterior', 'logl','logL','likelihood'] 786 self._logpaliases=['logp', 'logP','prior','logprior','Prior','logPrior'] 787 self._votfile=votfile 788 789 common_output_table_header=[i.lower() for i in common_output_table_header] 790 791 # Define XML mapping 792 self._injXMLFuncMap={ 793 'mchirp':lambda inj:inj.mchirp, 794 'chirpmass':lambda inj:inj.mchirp, 795 'mc':lambda inj:inj.mchirp, 796 'mass1':lambda inj:inj.mass1, 797 'm1':lambda inj:inj.mass1, 798 'mass2':lambda inj:inj.mass2, 799 'm2':lambda inj:inj.mass2, 800 'mtotal':lambda inj:float(inj.mass1)+float(inj.mass2), 801 'eta':lambda inj:inj.eta, 802 'q':self._inj_q, 803 'asym_massratio':self._inj_q, 804 'massratio':lambda inj:inj.eta, 805 'sym_massratio':lambda inj:inj.eta, 806 'time': lambda inj:float(inj.get_end()), 807 'time_mean': lambda inj:float(inj.get_end()), 808 'end_time': lambda inj:float(inj.get_end()), 809 'phi0':lambda inj:inj.phi0, 810 'phi_orb': lambda inj: inj.coa_phase, 811 'dist':lambda inj:inj.distance, 812 'distance':lambda inj:inj.distance, 813 'ra':self._inj_longitude, 814 'rightascension':self._inj_longitude, 815 'long':self._inj_longitude, 816 'longitude':self._inj_longitude, 817 'dec':lambda inj:inj.latitude, 818 'declination':lambda inj:inj.latitude, 819 'lat':lambda inj:inj.latitude, 820 'latitude':lambda inj:inj.latitude, 821 'psi': lambda inj: np.mod(inj.polarization, np.pi), 822 'f_ref': lambda inj: self._injFref, 823 'polarisation':lambda inj:inj.polarization, 824 'polarization':lambda inj:inj.polarization, 825 'h1_end_time':lambda inj:float(inj.get_end('H')), 826 'l1_end_time':lambda inj:float(inj.get_end('L')), 827 'v1_end_time':lambda inj:float(inj.get_end('V')), 828 'lal_amporder':lambda inj:inj.amp_order} 829 830 # Add on all spin parameterizations 831 for key, val in self._inj_spins(self._injection, frame=inj_spin_frame).items(): 832 self._injXMLFuncMap[key] = val 833 834 for one_d_posterior_samples,param_name in zip(np.hsplit(common_output_table_raw,common_output_table_raw.shape[1]),common_output_table_header): 835 836 self._posterior[param_name]=PosteriorOneDPDF(param_name.lower(),one_d_posterior_samples,injected_value=self._getinjpar(param_name),injFref=self._injFref,trigger_values=self._gettrigpar(param_name)) 837 838 if 'mchirp' in common_output_table_header and 'eta' in common_output_table_header \ 839 and (not 'm1' in common_output_table_header) and (not 'm2' in common_output_table_header): 840 try: 841 print 'Inferring m1 and m2 from mchirp and eta' 842 (m1,m2)=mc2ms(self._posterior['mchirp'].samples, self._posterior['eta'].samples) 843 self._posterior['m1']=PosteriorOneDPDF('m1',m1,injected_value=self._getinjpar('m1'),trigger_values=self._gettrigpar('m1')) 844 self._posterior['m2']=PosteriorOneDPDF('m2',m2,injected_value=self._getinjpar('m2'),trigger_values=self._gettrigpar('m2')) 845 except KeyError: 846 print 'Unable to deduce m1 and m2 from input columns' 847 848 849 logLFound=False 850 851 for loglalias in self._loglaliases: 852 853 if loglalias in common_output_table_header: 854 try: 855 self._logL=self._posterior[loglalias].samples 856 except KeyError: 857 print "No '%s' column in input table!"%loglalias 858 continue 859 logLFound=True 860 861 if not logLFound: 862 raise RuntimeError("No likelihood/posterior values found!") 863 self._logP=None 864 865 for logpalias in self._logpaliases: 866 if logpalias in common_output_table_header: 867 try: 868 self._logP=self._posterior[logpalias].samples 869 except KeyError: 870 print "No '%s' column in input table!"%logpalias 871 continue 872 if not 'log' in logpalias: 873 self._logP=[np.log(i) for i in self._logP] 874 875 if name is not None: 876 self.__name=name 877 878 if description is not None: 879 self.__description=description 880 881 return
882
883 - def extend_posterior(self):
884 """ 885 Add some usefule derived parameters (such as tilt angles, time delays, etc) in the Posterior object 886 """ 887 injection=self._injection 888 pos=self 889 # Generate component mass posterior samples (if they didnt exist already) 890 if 'mc' in pos.names: 891 mchirp_name = 'mc' 892 elif 'chirpmass' in pos.names: 893 mchirp_name = 'chirpmass' 894 else: 895 mchirp_name = 'mchirp' 896 897 if 'asym_massratio' in pos.names: 898 q_name = 'asym_massratio' 899 else: 900 q_name = 'q' 901 902 if 'sym_massratio' in pos.names: 903 eta_name= 'sym_massratio' 904 elif 'massratio' in pos.names: 905 eta_name= 'massratio' 906 else: 907 eta_name='eta' 908 909 if 'mass1' in pos.names and 'mass2' in pos.names : 910 pos.append_mapping(('m1','m2'),lambda x,y:(x,y) ,('mass1','mass2')) 911 912 if (mchirp_name in pos.names and eta_name in pos.names) and \ 913 ('mass1' not in pos.names or 'm1' not in pos.names) and \ 914 ('mass2' not in pos.names or 'm2' not in pos.names): 915 916 pos.append_mapping(('m1','m2'),mc2ms,(mchirp_name,eta_name)) 917 918 if (mchirp_name in pos.names and q_name in pos.names) and \ 919 ('mass1' not in pos.names or 'm1' not in pos.names) and \ 920 ('mass2' not in pos.names or 'm2' not in pos.names): 921 922 pos.append_mapping(('m1','m2'),q2ms,(mchirp_name,q_name)) 923 pos.append_mapping('eta',q2eta,(mchirp_name,q_name)) 924 925 if ('m1' in pos.names and 'm2' in pos.names and not 'mtotal' in pos.names ): 926 pos.append_mapping('mtotal', lambda m1,m2: m1+m2, ('m1','m2') ) 927 928 if('a_spin1' in pos.names): pos.append_mapping('a1',lambda a:a,'a_spin1') 929 if('a_spin2' in pos.names): pos.append_mapping('a2',lambda a:a,'a_spin2') 930 if('phi_spin1' in pos.names): pos.append_mapping('phi1',lambda a:a,'phi_spin1') 931 if('phi_spin2' in pos.names): pos.append_mapping('phi2',lambda a:a,'phi_spin2') 932 if('theta_spin1' in pos.names): pos.append_mapping('theta1',lambda a:a,'theta_spin1') 933 if('theta_spin2' in pos.names): pos.append_mapping('theta2',lambda a:a,'theta_spin2') 934 935 my_ifos=['h1','l1','v1'] 936 for ifo1,ifo2 in combinations(my_ifos,2): 937 p1=ifo1+'_cplx_snr_arg' 938 p2=ifo2+'_cplx_snr_arg' 939 if p1 in pos.names and p2 in pos.names: 940 delta=np.mod(pos[p1].samples - pos[p2].samples + np.pi ,2.0*np.pi)-np.pi 941 pos.append(PosteriorOneDPDF(ifo1+ifo2+'_relative_phase',delta)) 942 943 # Ensure that both theta_jn and inclination are output for runs 944 # with zero tilt (for runs with tilt, this will be taken care of 945 # below when the old spin angles are computed as functions of the 946 # new ones 947 # Disabled this since the parameters are degenerate and causing problems 948 #if ('theta_jn' in pos.names) and (not 'tilt1' in pos.names) and (not 'tilt2' in pos.names): 949 # pos.append_mapping('iota', lambda t:t, 'theta_jn') 950 951 # Compute time delays from sky position 952 try: 953 if ('ra' in pos.names or 'rightascension' in pos.names) \ 954 and ('declination' in pos.names or 'dec' in pos.names) \ 955 and 'time' in pos.names: 956 from pylal import xlal,inject 957 from pylal import date 958 from pylal.date import XLALTimeDelayFromEarthCenter 959 from pylal.xlal.datatypes.ligotimegps import LIGOTimeGPS 960 import itertools 961 from numpy import array 962 detMap = {'H1': 'LHO_4k', 'H2': 'LHO_2k', 'L1': 'LLO_4k', 963 'G1': 'GEO_600', 'V1': 'VIRGO', 'T1': 'TAMA_300'} 964 if 'ra' in pos.names: 965 ra_name='ra' 966 else: ra_name='rightascension' 967 if 'dec' in pos.names: 968 dec_name='dec' 969 else: dec_name='declination' 970 ifo_times={} 971 my_ifos=['H1','L1','V1'] 972 for ifo in my_ifos: 973 inj_time=None 974 if injection: 975 inj_time=float(injection.get_end(ifo[0])) 976 location=inject.cached_detector[detMap[ifo]].location 977 ifo_times[ifo]=array(map(lambda ra,dec,time: array([time[0]+XLALTimeDelayFromEarthCenter(location,ra[0],dec[0],LIGOTimeGPS(float(time[0])))]), pos[ra_name].samples,pos[dec_name].samples,pos['time'].samples)) 978 loc_end_time=PosteriorOneDPDF(ifo.lower()+'_end_time',ifo_times[ifo],injected_value=inj_time) 979 pos.append(loc_end_time) 980 for ifo1 in my_ifos: 981 for ifo2 in my_ifos: 982 if ifo1==ifo2: continue 983 delay_time=ifo_times[ifo2]-ifo_times[ifo1] 984 if injection: 985 inj_delay=float(injection.get_end(ifo2[0])-injection.get_end(ifo1[0])) 986 else: 987 inj_delay=None 988 time_delay=PosteriorOneDPDF(ifo1.lower()+ifo2.lower()+'_delay',delay_time,inj_delay) 989 pos.append(time_delay) 990 except ImportError: 991 print 'Warning: Could not import lal python bindings, check you ./configured with --enable-swig-python' 992 print 'This means I cannot calculate time delays' 993 994 #Calculate new spin angles 995 new_spin_params = ['tilt1','tilt2','theta_jn','beta'] 996 if not set(new_spin_params).issubset(set(pos.names)): 997 old_params = ['f_ref',mchirp_name,'eta','iota','a1','theta1','phi1'] 998 if 'a2' in pos.names: old_params += ['a2','theta2','phi2'] 999 try: 1000 pos.append_mapping(new_spin_params, spin_angles, old_params) 1001 except KeyError: 1002 print "Warning: Cannot find spin parameters. Skipping spin angle calculations." 1003 1004 #Calculate effective precessing spin magnitude 1005 if ('a1' in pos.names and 'tilt1' in pos.names and 'm1' in pos.names ) and ('a2' in pos.names and 'tilt2' in pos.names and 'm2' in pos.names): 1006 pos.append_mapping('chi_p', chi_precessing, ['a1', 'tilt1', 'm1', 'a2', 'tilt2', 'm2']) 1007 1008 1009 # Calculate redshift from luminosity distance measurements 1010 if('distance' in pos.names): 1011 pos.append_mapping('redshift', calculate_redshift, 'distance') 1012 1013 # Calculate source mass parameters 1014 if ('m1' in pos.names) and ('redshift' in pos.names): 1015 pos.append_mapping('m1_source', source_mass, ['m1', 'redshift']) 1016 1017 if ('m2' in pos.names) and ('redshift' in pos.names): 1018 pos.append_mapping('m2_source', source_mass, ['m2', 'redshift']) 1019 1020 if ('mtotal' in pos.names) and ('redshift' in pos.names): 1021 pos.append_mapping('mtotal_source', source_mass, ['mtotal', 'redshift']) 1022 1023 if ('mc' in pos.names) and ('redshift' in pos.names): 1024 pos.append_mapping('mc_source', source_mass, ['mc', 'redshift']) 1025 1026 #Store signed spin magnitudes in separate parameters and make a1,a2 magnitudes 1027 if 'a1' in pos.names: 1028 if 'tilt1' in pos.names: 1029 pos.append_mapping('a1z', lambda a, tilt: a*np.cos(tilt), ('a1','tilt1')) 1030 else: 1031 pos.append_mapping('a1z', lambda x: x, 'a1') 1032 inj_az = None 1033 if injection is not None: 1034 inj_az = injection.spin1z 1035 pos['a1z'].set_injval(inj_az) 1036 pos.pop('a1') 1037 pos.append_mapping('a1', lambda x: np.abs(x), 'a1z') 1038 1039 if 'a2' in pos.names: 1040 if 'tilt2' in pos.names: 1041 pos.append_mapping('a2z', lambda a, tilt: a*np.cos(tilt), ('a2','tilt2')) 1042 else: 1043 pos.append_mapping('a2z', lambda x: x, 'a2') 1044 inj_az = None 1045 if injection is not None: 1046 inj_az = injection.spin2z 1047 pos['a2z'].set_injval(inj_az) 1048 pos.pop('a2') 1049 pos.append_mapping('a2', lambda x: np.abs(x), 'a2z') 1050 1051 #Calculate effective spin parallel to L 1052 if ('m1' in pos.names and 'a1z' in pos.names) and ('m2' in pos.names and 'a2z' in pos.names): 1053 pos.append_mapping('chi_eff', lambda m1,a1z,m2,a2z: (m1*a1z + m2*a2z) / (m1 + m2), ('m1','a1z','m2','a2z')) 1054 1055 #If precessing spins calculate total effective spin 1056 if ('m1' in pos.names and 'a1' in pos.names and 'tilt1' in pos.names) and ('m2' in pos.names and 'a2' in pos.names and 'tilt2' in pos.names): 1057 pos.append_mapping('chi_tot', lambda m1,a1,m2,a2: (m1*a1 + m2*a2) / (m1 + m2), ('m1','a1','m2','a2')) 1058 1059 #Calculate effective precessing spin magnitude 1060 if ('m1' in pos.names and 'a1' in pos.names and 'tilt1' in pos.names) and ('m2' in pos.names and 'a2' in pos.names and 'tilt2' in pos.names): 1061 pos.append_mapping('chi_p', chi_precessing, ['m1', 'a1', 'tilt1', 'm2', 'a2', 'tilt2']) 1062 1063 # Calculate redshift from luminosity distance measurements 1064 if('distance' in pos.names): 1065 pos.append_mapping('redshift', calculate_redshift, 'distance') 1066 1067 # Calculate source mass parameters 1068 if ('m1' in pos.names) and ('redshift' in pos.names): 1069 pos.append_mapping('m1_source', source_mass, ['m1', 'redshift']) 1070 1071 if ('m2' in pos.names) and ('redshift' in pos.names): 1072 pos.append_mapping('m2_source', source_mass, ['m2', 'redshift']) 1073 1074 if ('mtotal' in pos.names) and ('redshift' in pos.names): 1075 pos.append_mapping('mtotal_source', source_mass, ['mtotal', 'redshift']) 1076 1077 if ('mc' in pos.names) and ('redshift' in pos.names): 1078 pos.append_mapping('mc_source', source_mass, ['mc', 'redshift']) 1079 1080 #Calculate new tidal parameters 1081 new_tidal_params = ['lam_tilde','dlam_tilde'] 1082 old_tidal_params = ['lambda1','lambda2','eta'] 1083 if 'lambda1' in pos.names or 'lambda2' in pos.names: 1084 try: 1085 pos.append_mapping(new_tidal_params, symm_tidal_params, old_tidal_params) 1086 except KeyError: 1087 print "Warning: Cannot find tidal parameters. Skipping tidal calculations." 1088 1089 #If new spin params present, calculate old ones 1090 old_spin_params = ['iota', 'theta1', 'phi1', 'theta2', 'phi2', 'beta'] 1091 new_spin_params = ['theta_jn', 'phi_jl', 'tilt1', 'tilt2', 'phi12', 'a1', 'a2', 'm1', 'm2', 'f_ref'] 1092 try: 1093 if pos['f_ref'].samples[0][0]==0.0: 1094 for name in ['flow','f_lower']: 1095 if name in pos.names: 1096 new_spin_params = ['theta_jn', 'phi_jl', 'tilt1', 'tilt2', 'phi12', 'a1', 'a2', 'm1', 'm2', name] 1097 except: 1098 print "No f_ref for SimInspiralTransformPrecessingNewInitialConditions()." 1099 if set(new_spin_params).issubset(set(pos.names)) and not set(old_spin_params).issubset(set(pos.names)): 1100 pos.append_mapping(old_spin_params, physical2radiationFrame, new_spin_params) 1101 1102 #Calculate spin magnitudes for aligned runs 1103 if 'spin1' in pos.names: 1104 inj_a1 = inj_a2 = None 1105 if injection: 1106 inj_a1 = sqrt(injection.spin1x*injection.spin1x + injection.spin1y*injection.spin1y + injection.spin1z*injection.spin1z) 1107 inj_a2 = sqrt(injection.spin2x*injection.spin2x + injection.spin2y*injection.spin2y + injection.spin2z*injection.spin2z) 1108 1109 try: 1110 a1_samps = abs(pos['spin1'].samples) 1111 a1_pos = PosteriorOneDPDF('a1',a1_samps,injected_value=inj_a1) 1112 pos.append(a1_pos) 1113 except KeyError: 1114 print "Warning: problem accessing spin1 values." 1115 1116 try: 1117 a2_samps = abs(pos['spin2'].samples) 1118 a2_pos = PosteriorOneDPDF('a2',a2_samps,injected_value=inj_a2) 1119 pos.append(a2_pos) 1120 except KeyError: 1121 print "Warning: no spin2 values found." 1122 1123 # Calculate mass and spin of final merged system 1124 if ('m1' in pos.names) and ('m2' in pos.names): 1125 if ('tilt1' in pos.names) or ('tilt2' in pos.names): 1126 print "A precessing fit formula is not available yet. Using a non-precessing fit formula on the aligned-spin components." 1127 if ('a1z' in pos.names) and ('a2z' in pos.names): 1128 print "Using non-precessing fit formula [Healy at al (2014)] for final mass and spin (on masses and projected spin components)." 1129 try: 1130 pos.append_mapping('af', bbh_final_spin_non_precessing_Healyetal, ['m1', 'm2', 'a1z', 'a2z']) 1131 pos.append_mapping('mf', lambda m1, m2, chi1z, chi2z, chif: bbh_final_mass_non_precessing_Healyetal(m1, m2, chi1z, chi2z, chif=chif), ['m1', 'm2', 'a1z', 'a2z', 'af']) 1132 except Exception,e: 1133 print "Could not calculate final parameters. The error was: %s"%(str(e)) 1134 elif ('a1' in pos.names) and ('a2' in pos.names): 1135 if ('tilt1' in pos.names) and ('tilt2' in pos.names): 1136 print "Projecting spin and using non-precessing fit formula [Healy at al (2014)] for final mass and spin." 1137 try: 1138 pos.append_mapping('af', bbh_final_spin_projected_spin_Healyetal, ['m1', 'm2', 'a1', 'a2', 'tilt1', 'tilt2']) 1139 pos.append_mapping('mf', bbh_final_mass_projected_spin_Healyetal, ['m1', 'm2', 'a1', 'a2', 'tilt1', 'tilt2', 'af']) 1140 except Exception,e: 1141 print "Could not calculate final parameters. The error was: %s"%(str(e)) 1142 else: 1143 print "Using non-precessing fit formula [Healy at al (2014)] for final mass and spin (on masses and spin magnitudes)." 1144 try: 1145 pos.append_mapping('af', bbh_final_spin_non_precessing_Healyetal, ['m1', 'm2', 'a1', 'a2']) 1146 pos.append_mapping('mf', lambda m1, m2, chi1, chi2, chif: bbh_final_mass_non_precessing_Healyetal(m1, m2, chi1, chi2, chif=chif), ['m1', 'm2', 'a1', 'a2', 'af']) 1147 except Exception,e: 1148 print "Could not calculate final parameters. The error was: %s"%(str(e)) 1149 else: 1150 print "Using non-spinning fit formula [Pan at al (2010)] for final mass and spin." 1151 try: 1152 pos.append_mapping('af', bbh_final_spin_non_spinning_Panetal, ['m1', 'm2']) 1153 pos.append_mapping('mf', bbh_final_mass_non_spinning_Panetal, ['m1', 'm2']) 1154 except Exception,e: 1155 print "Could not calculate final parameters. The error was: %s"%(str(e)) 1156 if ('mf' in pos.names) and ('redshift' in pos.names): 1157 try: 1158 pos.append_mapping('mf_source', source_mass, ['mf', 'redshift']) 1159 except Exception,e: 1160 print "Could not calculate final source frame mass. The error was: %s"%(str(e)) 1161 1162 # Calculate radiated energy and peak luminosity 1163 if ('mtotal_source' in pos.names) and ('mf_source' in pos.names): 1164 try: 1165 pos.append_mapping('e_rad', lambda mtot_s, mf_s: mtot_s-mf_s, ['mtotal_source', 'mf_source']) 1166 except Exception,e: 1167 print "Could not calculate radiated energy. The error was: %s"%(str(e)) 1168 1169 if ('q' in pos.names) and ('a1z' in pos.names) and ('a2z' in pos.names): 1170 try: 1171 pos.append_mapping('l_peak', bbh_aligned_Lpeak_6mode_SHXJDK, ['q', 'a1z', 'a2z']) 1172 except Exception,e: 1173 print "Could not calculate peak luminosity. The error was: %s"%(str(e))
1174
1175 - def bootstrap(self):
1176 """ 1177 Returns a new Posterior object that contains a bootstrap 1178 sample of self. 1179 1180 @rtype: Posterior 1181 """ 1182 names=[] 1183 samples=[] 1184 for name,oneDpos in self._posterior.items(): 1185 names.append(name) 1186 samples.append(oneDpos.samples) 1187 1188 samplesBlock=np.hstack(samples) 1189 1190 bootstrapSamples=samplesBlock[:,:] 1191 Nsamp=bootstrapSamples.shape[0] 1192 1193 rows=np.vsplit(samplesBlock,Nsamp) 1194 1195 for i in range(Nsamp): 1196 bootstrapSamples[i,:]=random.choice(rows) 1197 1198 return Posterior((names,bootstrapSamples),self._injection,self._triggers)
1199
1200 - def delete_samples_by_idx(self,samples):
1201 """ 1202 Remove samples from all OneDPosteriors. 1203 1204 @param samples: The indexes of the samples to be removed. 1205 @type samples: list 1206 """ 1207 for name,pos in self: 1208 pos.delete_samples_by_idx(samples) 1209 return
1210
1211 - def delete_NaN_entries(self,param_list):
1212 """ 1213 Remove samples containing NaN in request params. 1214 1215 @param param_list: The parameters to be checked for NaNs. 1216 @type param_list: list 1217 """ 1218 nan_idxs = np.array(()) 1219 nan_dict = {} 1220 for param in param_list: 1221 nan_bool_array = np.isnan(self[param].samples).any(1) 1222 idxs = np.where(nan_bool_array == True)[0] 1223 if len(idxs) > 0: 1224 nan_dict[param]=len(idxs) 1225 nan_idxs = np.append(nan_idxs, idxs) 1226 total_samps = len(self) 1227 nan_samps = len(nan_idxs) 1228 if nan_samps is not 0: 1229 print "WARNING: removing %i of %i total samples due to NaNs:"% (nan_samps,total_samps) 1230 for param in nan_dict.keys(): 1231 print "\t%i NaNs in %s."%(nan_dict[param],param) 1232 self.delete_samples_by_idx(nan_idxs) 1233 return
1234 1235 @property
1236 - def DIC(self):
1237 """Returns the Deviance Information Criterion estimated from the 1238 posterior samples. The DIC is defined as -2*(<log(L)> - 1239 Var(log(L))); smaller values are "better." 1240 1241 """ 1242 1243 return -2.0*(np.mean(self._logL) - np.var(self._logL))
1244 1245 @property
1246 - def injection(self):
1247 """ 1248 Return the injected values. 1249 1250 @rtype: glue.ligolw.lsctables.SimInspiral 1251 """ 1252 1253 return self._injection
1254 1255 @property
1256 - def triggers(self):
1257 """ 1258 Return the trigger values . 1259 1260 @rtype: list 1261 """ 1262 1263 return self._triggers
1264
1265 - def _total_incl_restarts(self, samples):
1266 total=0 1267 last=samples[0] 1268 for x in samples[1:]: 1269 if x < last: 1270 total += last 1271 last = x 1272 total += samples[-1] 1273 return total
1274
1275 - def longest_chain_cycles(self):
1276 """ 1277 Returns the number of cycles in the longest chain 1278 1279 @rtype: number 1280 """ 1281 samps,header=self.samples() 1282 header=header.split() 1283 if not ('cycle' in header): 1284 raise RuntimeError("Cannot compute number of cycles in longest chain") 1285 1286 cycle_col=header.index('cycle') 1287 if 'chain' in header: 1288 chain_col=header.index('chain') 1289 chain_indexes=np.unique(samps[:,chain_col]) 1290 max_cycle=0 1291 for ind in chain_indexes: 1292 chain_cycle_samps=samps[ samps[:,chain_col] == ind, cycle_col ] 1293 max_cycle=max(max_cycle, self._total_incl_restarts(chain_cycle_samps)) 1294 return int(max_cycle) 1295 else: 1296 return int(self._total_incl_restarts(samps[:,cycle_col]))
1297 1298 #@injection.setter #Python 2.6+
1299 - def set_injection(self,injection):
1300 """ 1301 Set the injected values of the parameters. 1302 1303 @param injection: A SimInspiralTable row object containing the injected parameters. 1304 @type injection: glue.ligolw.lsctables.SimInspiral 1305 """ 1306 if injection is not None: 1307 self._injection=injection 1308 for name,onepos in self: 1309 new_injval=self._getinjpar(name) 1310 if new_injval is not None: 1311 self[name].set_injval(new_injval)
1312
1313 - def set_triggers(self,triggers):
1314 """ 1315 Set the trigger values of the parameters. 1316 1317 @param triggers: A list of SnglInspiral objects. 1318 @type triggers: list 1319 """ 1320 if triggers is not None: 1321 self._triggers=triggers 1322 for name,onepos in self: 1323 new_trigvals=self._gettrigpar(name) 1324 if new_trigvals is not None: 1325 self[name].set_trigvals(new_trigvals)
1326 1327
1328 - def _getinjpar(self,paramname):
1329 """ 1330 Map parameter names to parameters in a SimInspiralTable . 1331 """ 1332 if self._injection is not None: 1333 for key,value in self._injXMLFuncMap.items(): 1334 if paramname.lower().strip() == key.lower().strip(): 1335 try: 1336 return self._injXMLFuncMap[key](self._injection) 1337 except TypeError: 1338 return self._injXMLFuncMap[key] 1339 return None
1340
1341 - def _gettrigpar(self,paramname):
1342 """ 1343 Map parameter names to parameters in a SnglInspiral. 1344 """ 1345 vals = None 1346 if self._triggers is not None: 1347 for key,value in self._injXMLFuncMap.items(): 1348 if paramname.lower().strip() == key.lower().strip(): 1349 try: 1350 vals = dict([(trig.ifo,self._injXMLFuncMap[key](trig)) for trig in self._triggers]) 1351 except TypeError: 1352 return self._injXMLFuncMap[key] 1353 except AttributeError: 1354 return None 1355 return vals
1356
1357 - def __getitem__(self,key):
1358 """ 1359 Container method . Returns posterior chain,one_d_pos, with name one_d_pos.name. 1360 """ 1361 return self._posterior[key.lower()]
1362
1363 - def __len__(self):
1364 """ 1365 Container method. Defined as number of samples. 1366 """ 1367 return len(self._logL)
1368
1369 - def __iter__(self):
1370 """ 1371 Container method. Returns iterator from self.forward for use in 1372 for (...) in (...) etc. 1373 """ 1374 return self.forward()
1375
1376 - def forward(self):
1377 """ 1378 Generate a forward iterator (in sense of list of names) over Posterior 1379 with name,one_d_pos. 1380 """ 1381 current_item = 0 1382 while current_item < self.dim: 1383 name=self._posterior.keys()[current_item] 1384 pos=self._posterior[name] 1385 current_item += 1 1386 yield name,pos
1387
1388 - def bySample(self):
1389 """ 1390 Generate a forward iterator over the list of samples corresponding to 1391 the data stored within the Posterior instance. These are returned as 1392 ParameterSamples instances. 1393 """ 1394 current_item=0 1395 pos_array,header=self.samples 1396 while current_item < len(self): 1397 sample_array=(np.squeeze(pos_array[current_item,:])) 1398 yield PosteriorSample(sample_array, header, header) 1399 current_item += 1
1400 1401 1402 @property
1403 - def dim(self):
1404 """ 1405 Return number of parameters. 1406 """ 1407 return len(self._posterior.keys())
1408 1409 @property
1410 - def names(self):
1411 """ 1412 Return list of parameter names. 1413 """ 1414 nameslist=[] 1415 for key,value in self: 1416 nameslist.append(key) 1417 return nameslist
1418 1419 @property
1420 - def means(self):
1421 """ 1422 Return dict {paramName:paramMean} . 1423 """ 1424 meansdict={} 1425 for name,pos in self: 1426 meansdict[name]=pos.mean 1427 return meansdict
1428 1429 @property
1430 - def medians(self):
1431 """ 1432 Return dict {paramName:paramMedian} . 1433 """ 1434 mediansdict={} 1435 for name,pos in self: 1436 mediansdict[name]=pos.median 1437 return mediansdict
1438 1439 @property
1440 - def stdevs(self):
1441 """ 1442 Return dict {paramName:paramStandardDeviation} . 1443 """ 1444 stdsdict={} 1445 for name,pos in self: 1446 stdsdict[name]=pos.stdev 1447 return stdsdict
1448 1449 @property
1450 - def name(self):
1451 """ 1452 Return qualified string containing the 'name' of the Posterior instance. 1453 """ 1454 return self.__name
1455 1456 @property
1457 - def description(self):
1458 """ 1459 Return qualified string containing a 'description' of the Posterior instance. 1460 """ 1461 return self.__description
1462
1463 - def append(self,one_d_posterior):
1464 """ 1465 Container method. Add a new OneDParameter to the Posterior instance. 1466 """ 1467 self._posterior[one_d_posterior.name]=one_d_posterior 1468 return
1469
1470 - def pop(self,param_name):
1471 """ 1472 Container method. Remove PosteriorOneDPDF from the Posterior instance. 1473 """ 1474 return self._posterior.pop(param_name)
1475
1476 - def append_mapping(self, new_param_names, func, post_names):
1477 """ 1478 Append posteriors pos1,pos2,...=func(post_names) 1479 """ 1480 # deepcopy 1D posteriors to ensure mapping function doesn't modify the originals 1481 import copy 1482 #1D input 1483 if isinstance(post_names, str): 1484 old_post = copy.deepcopy(self[post_names]) 1485 old_inj = old_post.injval 1486 old_trigs = old_post.trigvals 1487 if old_inj: 1488 new_inj = func(old_inj) 1489 else: 1490 new_inj = None 1491 if old_trigs: 1492 new_trigs = {} 1493 for IFO in old_trigs.keys(): 1494 new_trigs[IFO] = func(old_trigs[IFO]) 1495 else: 1496 new_trigs = None 1497 1498 samps = func(old_post.samples) 1499 new_post = PosteriorOneDPDF(new_param_names, samps, injected_value=new_inj, trigger_values=new_trigs) 1500 if new_post.samples.ndim is 0: 1501 print "WARNING: No posterior calculated for %s ..." % post.name 1502 else: 1503 self.append(new_post) 1504 #MultiD input 1505 else: 1506 old_posts = [copy.deepcopy(self[post_name]) for post_name in post_names] 1507 old_injs = [post.injval for post in old_posts] 1508 old_trigs = [post.trigvals for post in old_posts] 1509 samps = func(*[post.samples for post in old_posts]) 1510 #1D output 1511 if isinstance(new_param_names, str): 1512 if None not in old_injs: 1513 inj = func(*old_injs) 1514 else: 1515 inj = None 1516 if None not in old_trigs: 1517 new_trigs = {} 1518 for IFO in old_trigs[0].keys(): 1519 oldvals = [param[IFO] for param in old_trigs] 1520 new_trigs[IFO] = func(*oldvals) 1521 else: 1522 new_trigs = None 1523 new_post = PosteriorOneDPDF(new_param_names, samps, injected_value=inj, trigger_values=new_trigs) 1524 self.append(new_post) 1525 #MultiD output 1526 else: 1527 if None not in old_injs: 1528 injs = func(*old_injs) 1529 else: 1530 injs = [None for name in new_param_names] 1531 if None not in old_trigs: 1532 new_trigs = [{} for param in range(len(new_param_names))] 1533 for IFO in old_trigs[0].keys(): 1534 oldvals = [param[IFO] for param in old_trigs] 1535 newvals = func(*oldvals) 1536 for param,newval in enumerate(newvals): 1537 new_trigs[param][IFO] = newval 1538 else: 1539 new_trigs = [None for param in range(len(new_param_names))] 1540 if not samps: return() # Something went wrong 1541 new_posts = [PosteriorOneDPDF(new_param_name,samp,injected_value=inj,trigger_values=new_trigs) for (new_param_name,samp,inj,new_trigs) in zip(new_param_names,samps,injs,new_trigs)] 1542 for post in new_posts: 1543 if post.samples.ndim is 0: 1544 print "WARNING: No posterior calculated for %s ..." % post.name 1545 else: 1546 self.append(post) 1547 return
1548
1549 - def _average_posterior(self, samples, post_name):
1550 """ 1551 Returns the average value of the 'post_name' column of the 1552 given samples. 1553 """ 1554 ap = 0.0 1555 for samp in samples: 1556 ap = ap + samp[post_name] 1557 return ap / len(samples)
1558
1559 - def _average_posterior_like_prior(self, samples, logl_name, prior_name, log_bias = 0):
1560 """ 1561 Returns the average value of the posterior assuming that the 1562 'logl_name' column contains log(L) and the 'prior_name' column 1563 contains the prior (un-logged). 1564 """ 1565 ap = 0.0 1566 for samp in samples: 1567 ap += np.exp(samp[logl_name]-log_bias)*samp[prior_name] 1568 return ap / len(samples)
1569
1570 - def _bias_factor(self):
1571 """ 1572 Returns a sensible bias factor for the evidence so that 1573 integrals are representable as doubles. 1574 """ 1575 return np.mean(self._logL)
1576
1577 - def di_evidence(self, boxing=64):
1578 """ 1579 Returns the log of the direct-integration evidence for the 1580 posterior samples. 1581 """ 1582 allowed_coord_names=["spin1", "spin2", "a1", "phi1", "theta1", "a2", "phi2", "theta2", 1583 "iota", "psi", "ra", "dec", 1584 "phi_orb", "phi0", "dist", "time", "mc", "mchirp", "chirpmass", "q"] 1585 samples,header=self.samples() 1586 header=header.split() 1587 coord_names=[name for name in allowed_coord_names if name in header] 1588 coordinatized_samples=[PosteriorSample(row, header, coord_names) for row in samples] 1589 tree=KDTree(coordinatized_samples) 1590 1591 if "prior" in header and "logl" in header: 1592 bf = self._bias_factor() 1593 return bf + np.log(tree.integrate(lambda samps: self._average_posterior_like_prior(samps, "logl", "prior", bf), boxing)) 1594 elif "prior" in header and "likelihood" in header: 1595 bf = self._bias_factor() 1596 return bf + np.log(tree.integrate(lambda samps: self._average_posterior_like_prior(samps, "likelihood", "prior", bf), boxing)) 1597 elif "post" in header: 1598 return np.log(tree.integrate(lambda samps: self._average_posterior(samps, "post"), boxing)) 1599 elif "posterior" in header: 1600 return np.log(tree.integrate(lambda samps: self._average_posterior(samps, "posterior"), boxing)) 1601 else: 1602 raise RuntimeError("could not find 'post', 'posterior', 'logl' and 'prior', or 'likelihood' and 'prior' columns in output to compute direct integration evidence")
1603
1605 """Returns an approximation to the log(evidence) obtained by 1606 fitting an ellipse around the highest-posterior samples and 1607 performing the harmonic mean approximation within the ellipse. 1608 Because the ellipse should be well-sampled, this provides a 1609 better approximation to the evidence than the full-domain HM.""" 1610 allowed_coord_names=["spin1", "spin2", "a1", "phi1", "theta1", "a2", "phi2", "theta2", 1611 "iota", "psi", "ra", "dec", 1612 "phi_orb", "phi0", "dist", "time", "mc", "mchirp", "chirpmass", "q"] 1613 samples,header=self.samples() 1614 header=header.split() 1615 1616 n=int(0.05*samples.shape[0]) 1617 if not n > 1: 1618 raise IndexError 1619 1620 coord_names=[name for name in allowed_coord_names if name in header] 1621 indexes=np.argsort(self._logL[:,0]) 1622 1623 my_samples=samples[indexes[-n:], :] # The highest posterior samples. 1624 my_samples=np.array([PosteriorSample(sample,header,coord_names).coord() for sample in my_samples]) 1625 1626 mu=np.mean(my_samples, axis=0) 1627 cov=np.cov(my_samples, rowvar=0) 1628 1629 d0=None 1630 for mysample in my_samples: 1631 d=np.dot(mysample-mu, np.linalg.solve(cov, mysample-mu)) 1632 if d0 is None: 1633 d0 = d 1634 else: 1635 d0=max(d0,d) 1636 1637 ellipse_logl=[] 1638 ellipse_samples=[] 1639 for sample,logl in zip(samples, self._logL): 1640 coord=PosteriorSample(sample, header, coord_names).coord() 1641 d=np.dot(coord-mu, np.linalg.solve(cov, coord-mu)) 1642 1643 if d <= d0: 1644 ellipse_logl.append(logl) 1645 ellipse_samples.append(sample) 1646 1647 if len(ellipse_samples) > 5*n: 1648 print 'WARNING: ellpise evidence region encloses significantly more samples than %d'%n 1649 1650 ellipse_samples=np.array(ellipse_samples) 1651 ellipse_logl=np.array(ellipse_logl) 1652 1653 ndim = len(coord_names) 1654 ellipse_volume=np.pi**(ndim/2.0)*d0**(ndim/2.0)/special.gamma(ndim/2.0+1)*np.sqrt(np.linalg.det(cov)) 1655 1656 try: 1657 prior_index=header.index('prior') 1658 pmu=np.mean(ellipse_samples[:,prior_index]) 1659 pstd=np.std(ellipse_samples[:,prior_index]) 1660 if pstd/pmu > 1.0: 1661 print 'WARNING: prior variation greater than 100\% over elliptical volume.' 1662 approx_prior_integral=ellipse_volume*pmu 1663 except KeyError: 1664 # Maybe prior = 1? 1665 approx_prior_integral=ellipse_volume 1666 1667 ll_bias=np.mean(ellipse_logl) 1668 ellipse_logl = ellipse_logl - ll_bias 1669 1670 return np.log(approx_prior_integral) - np.log(np.mean(1.0/np.exp(ellipse_logl))) + ll_bias
1671
1672 - def harmonic_mean_evidence(self):
1673 """ 1674 Returns the log of the harmonic mean evidence for the set of 1675 posterior samples. 1676 """ 1677 bf = self._bias_factor() 1678 return bf + np.log(1/np.mean(1/np.exp(self._logL-bf)))
1679
1680 - def _posMaxL(self):
1681 """ 1682 Find the sample with maximum likelihood probability. Returns value 1683 of likelihood and index of sample . 1684 """ 1685 logl_vals=self._logL 1686 max_i=0 1687 max_logl=logl_vals[0] 1688 for i in range(len(logl_vals)): 1689 if logl_vals[i] > max_logl: 1690 max_logl=logl_vals[i] 1691 max_i=i 1692 return max_logl,max_i
1693
1694 - def _posMap(self):
1695 """ 1696 Find the sample with maximum a posteriori probability. Returns value 1697 of posterior and index of sample . 1698 """ 1699 logl_vals=self._logL 1700 if self._logP is not None: 1701 logp_vals=self._logP 1702 else: 1703 return None 1704 1705 max_i=0 1706 max_pos=logl_vals[0]+logp_vals[0] 1707 for i in range(len(logl_vals)): 1708 if logl_vals[i]+logp_vals[i] > max_pos: 1709 max_pos=logl_vals[i]+logp_vals[i] 1710 max_i=i 1711 return max_pos,max_i
1712
1713 - def _print_table_row(self,name,entries):
1714 """ 1715 Print a html table row representation of 1716 1717 name:item1,item2,item3,... 1718 """ 1719 1720 row_str='<tr><td>%s</td>'%name 1721 for entry in entries: 1722 row_str+='<td>%s</td>'%entry 1723 row_str+='</tr>' 1724 return row_str
1725 1726 @property
1727 - def maxL(self):
1728 """ 1729 Return the maximum likelihood probability and the corresponding 1730 set of parameters. 1731 """ 1732 maxLvals={} 1733 max_logl,max_i=self._posMaxL() 1734 for param_name in self.names: 1735 maxLvals[param_name]=self._posterior[param_name].samples[max_i][0] 1736 1737 return (max_logl,maxLvals)
1738 1739 @property
1740 - def maxP(self):
1741 """ 1742 Return the maximum a posteriori probability and the corresponding 1743 set of parameters. 1744 """ 1745 maxPvals={} 1746 max_pos,max_i=self._posMap() 1747 for param_name in self.names: 1748 maxPvals[param_name]=self._posterior[param_name].samples[max_i][0] 1749 1750 return (max_pos,maxPvals)
1751 1752
1753 - def samples(self):
1754 """ 1755 Return an (M,N) numpy.array of posterior samples; M = len(self); 1756 N = dim(self) . 1757 """ 1758 header_string='' 1759 posterior_table=[] 1760 for param_name,one_pos in self: 1761 column=np.array(one_pos.samples) 1762 header_string+=param_name+'\t' 1763 posterior_table.append(column) 1764 posterior_table=tuple(posterior_table) 1765 return np.column_stack(posterior_table),header_string
1766
1767 - def write_to_file(self,fname):
1768 """ 1769 Dump the posterior table to a file in the 'common format'. 1770 """ 1771 column_list=() 1772 1773 posterior_table,header_string=self.samples() 1774 1775 fobj=open(fname,'w') 1776 1777 fobj.write(header_string+'\n') 1778 np.savetxt(fobj,posterior_table,delimiter='\t') 1779 fobj.close() 1780 1781 return
1782
1783 - def gelman_rubin(self, pname):
1784 """ 1785 Returns an approximation to the Gelman-Rubin statistic (see 1786 Gelman, A. and Rubin, D. B., Statistical Science, Vol 7, 1787 No. 4, pp. 457--511 (1992)) for the parameter given, accurate 1788 as the number of samples in each chain goes to infinity. The 1789 posterior samples must have a column named 'chain' so that the 1790 different chains can be separated. 1791 """ 1792 from numpy import seterr as np_seterr 1793 np_seterr(all='raise') 1794 1795 if "chain" in self.names: 1796 chains=np.unique(self["chain"].samples) 1797 chain_index=self.names.index("chain") 1798 param_index=self.names.index(pname) 1799 data,header=self.samples() 1800 chainData=[data[ data[:,chain_index] == chain, param_index] for chain in chains] 1801 allData=np.concatenate(chainData) 1802 chainMeans=[np.mean(data) for data in chainData] 1803 chainVars=[np.var(data) for data in chainData] 1804 BoverN=np.var(chainMeans) 1805 W=np.mean(chainVars) 1806 sigmaHat2=W + BoverN 1807 m=len(chainData) 1808 VHat=sigmaHat2 + BoverN/m 1809 try: 1810 R = VHat/W 1811 except: 1812 print "Error when computer Gelman-Rubin R statistic for %s. This may be a fixed parameter"%pname 1813 R = np.nan 1814 return R 1815 else: 1816 raise RuntimeError('could not find necessary column header "chain" in posterior samples')
1817
1818 - def healpix_map(self, resol, nest=True):
1819 """Returns a healpix map in the pixel ordering that represents the 1820 posterior density (per square degree) on the sky. The pixels 1821 will be chosen to have at least the given resolution (in 1822 degrees). 1823 1824 """ 1825 1826 # Ensure that the resolution is twice the desired 1827 nside = 2 1828 while hp.nside2resol(nside, arcmin=True) > resol*60.0/2.0: 1829 nside *= 2 1830 1831 ras = self['ra'].samples.squeeze() 1832 decs = self['dec'].samples.squeeze() 1833 1834 phis = ras 1835 thetas = np.pi/2.0 - decs 1836 1837 # Create the map in ring ordering 1838 inds = hp.ang2pix(nside, thetas, phis, nest=False) 1839 counts = np.bincount(inds) 1840 if counts.shape[0] < hp.nside2npix(nside): 1841 counts = np.concatenate((counts, np.zeros(hp.nside2npix(nside) - counts.shape[0]))) 1842 1843 # Smooth the map a bit (Gaussian sigma = resol) 1844 hpmap = hp.sphtfunc.smoothing(counts, sigma=resol*np.pi/180.0) 1845 1846 hpmap = hpmap / (np.sum(hpmap)*hp.nside2pixarea(nside, degrees=True)) 1847 1848 if nest: 1849 hpmap = hp.reorder(hpmap, r2n=True) 1850 1851 return hpmap
1852
1853 - def __str__(self):
1854 """ 1855 Define a string representation of the Posterior class ; returns 1856 a html formatted table of various properties of posteriors. 1857 """ 1858 return_val='<table border="1" id="statstable"><tr><th/>' 1859 column_names=['maP','maxL','stdev','mean','median','stacc','injection value'] 1860 IFOs = [] 1861 if self._triggers is not None: 1862 IFOs = [trig.ifo for trig in self._triggers] 1863 for IFO in IFOs: 1864 column_names.append(IFO+' trigger values') 1865 1866 for column_name in column_names: 1867 return_val+='<th>%s</th>'%column_name 1868 1869 return_val+='</tr>' 1870 1871 for name,oned_pos in self: 1872 1873 max_logl,max_i=self._posMaxL() 1874 maxL=oned_pos.samples[max_i][0] 1875 max_post,max_j=self._posMap() 1876 maP=oned_pos.samples[max_j][0] 1877 mean=str(oned_pos.mean) 1878 stdev=str(oned_pos.stdev) 1879 median=str(np.squeeze(oned_pos.median)) 1880 stacc=str(oned_pos.stacc) 1881 injval=str(oned_pos.injval) 1882 trigvals=oned_pos.trigvals 1883 1884 row = [maP,maxL,stdev,mean,median,stacc,injval] 1885 if self._triggers is not None: 1886 for IFO in IFOs: 1887 try: 1888 row.append(str(trigvals[IFO])) 1889 except TypeError: 1890 row.append(None) 1891 return_val+=self._print_table_row(name,row) 1892 1893 return_val+='</table>' 1894 1895 parser=XMLParser() 1896 parser.feed(return_val) 1897 Estr=parser.close() 1898 1899 elem=Estr 1900 rough_string = tostring(elem, 'utf-8') 1901 reparsed = minidom.parseString(rough_string) 1902 return_val=reparsed.toprettyxml(indent=" ") 1903 return return_val[len('<?xml version="1.0" ?>')+1:]
1904
1905 - def write_vot_info(self):
1906 """ 1907 Writes the information stored in the VOTtree if there is one 1908 """ 1909 target=VOT2HTML() 1910 parser=XMLParser(target=target) 1911 parser.feed(self._votfile) 1912 return parser.close()
1913 1914 #=============================================================================== 1915 # Functions used to parse injection structure. 1916 #===============================================================================
1917 - def _inj_m1(self,inj):
1918 """ 1919 Return the mapping of (mchirp,eta)->m1; m1>m2 i.e. return the greater of the mass 1920 components (m1) calculated from the chirp mass and the symmetric mass ratio. 1921 1922 @type inj: glue.ligolw.lsctables.SimInspiral 1923 @param inj: a custom type with the attributes 'mchirp' and 'eta'. 1924 @rtype: number 1925 """ 1926 (mass1,mass2)=mc2ms(inj.mchirp,inj.eta) 1927 return mass1
1928
1929 - def _inj_m2(self,inj):
1930 """ 1931 Return the mapping of (mchirp,eta)->m2; m1>m2 i.e. return the lesser of the mass 1932 components (m2) calculated from the chirp mass and the symmetric mass ratio. 1933 1934 @type inj: glue.ligolw.lsctables.SimInspiral 1935 @param inj: a custom type with the attributes 'mchirp' and 'eta'. 1936 @rtype: number 1937 """ 1938 (mass1,mass2)=mc2ms(inj.mchirp,inj.eta) 1939 return mass2
1940
1941 - def _inj_q(self,inj):
1942 """ 1943 Return the mapping of (mchirp,eta)->q; m1>m2 i.e. return the mass ratio q=m2/m1. 1944 1945 @type inj: glue.ligolw.lsctables.SimInspiral 1946 @param inj: a custom type with the attributes 'mchirp' and 'eta'. 1947 @rtype: number 1948 """ 1949 (mass1,mass2)=mc2ms(inj.mchirp,inj.eta) 1950 return mass2/mass1
1951
1952 - def _inj_longitude(self,inj):
1953 """ 1954 Return the mapping of longitude found in inj to the interval [0,2*pi). 1955 1956 @type inj: glue.ligolw.lsctables.SimInspiral 1957 @param inj: a custom type with the attribute 'longitude'. 1958 @rtype: number 1959 """ 1960 if inj.longitude>2*pi_constant or inj.longitude<0.0: 1961 maplong=2*pi_constant*(((float(inj.longitude))/(2*pi_constant)) - floor(((float(inj.longitude))/(2*pi_constant)))) 1962 print "Warning: Injected longitude/ra (%s) is not within [0,2\pi)! Angles are assumed to be in radians so this will be mapped to [0,2\pi). Mapped value is: %s."%(str(inj.longitude),str(maplong)) 1963 return maplong 1964 else: 1965 return inj.longitude
1966
1967 - def _inj_spins(self, inj, frame='OrbitalL'):
1968 spins = {} 1969 f_ref = self._injFref 1970 1971 if not inj: 1972 spins = {} 1973 1974 else: 1975 axis = lalsim.SimInspiralGetFrameAxisFromString(frame) 1976 1977 m1, m2 = inj.mass1, inj.mass2 1978 mc, eta = inj.mchirp, inj.eta 1979 1980 # Convert to radiation frame 1981 iota, s1x, s1y, s1z, s2x, s2y, s2z = \ 1982 lalsim.SimInspiralInitialConditionsPrecessingApproxs(inj.inclination, 1983 inj.spin1x, inj.spin1y, inj.spin1z, 1984 inj.spin2x, inj.spin2y, inj.spin2z, 1985 m1*lal.MSUN_SI, m2*lal.MSUN_SI, f_ref, inj.coa_phase, axis) 1986 1987 a1, theta1, phi1 = cart2sph(s1x, s1y, s1z) 1988 a2, theta2, phi2 = cart2sph(s2x, s2y, s2z) 1989 1990 spins = {'a1':a1, 'theta1':theta1, 'phi1':phi1, 1991 'a2':a2, 'theta2':theta2, 'phi2':phi2, 1992 'iota':iota} 1993 1994 # If spins are aligned, save the sign of the z-component 1995 if inj.spin1x == inj.spin1y == inj.spin2x == inj.spin2y == 0.: 1996 spins['a1z'] = inj.spin1z 1997 spins['a2z'] = inj.spin2z 1998 1999 L = orbital_momentum(f_ref, m1,m2, iota) 2000 S1 = np.hstack((s1x, s1y, s1z)) 2001 S2 = np.hstack((s2x, s2y, s2z)) 2002 2003 zhat = np.array([0., 0., 1.]) 2004 aligned_comp_spin1 = array_dot(S1, zhat) 2005 aligned_comp_spin2 = array_dot(S2, zhat) 2006 chi = aligned_comp_spin1 + aligned_comp_spin2 + \ 2007 np.sqrt(1. - 4.*eta) * (aligned_comp_spin1 - aligned_comp_spin2) 2008 2009 spins['spinchi'] = chi 2010 2011 S1 *= m1**2 2012 S2 *= m2**2 2013 J = L + S1 + S2 2014 2015 tilt1 = array_ang_sep(L, S1) 2016 tilt2 = array_ang_sep(L, S2) 2017 beta = array_ang_sep(J, L) 2018 2019 spins['tilt1'] = tilt1 2020 spins['tilt2'] = tilt2 2021 spins['beta'] = beta 2022 2023 # Need to do rotations of XLALSimInspiralTransformPrecessingInitialConditioin inverse order to go in the L frame 2024 # first rotation: bring J in the N-x plane, with negative x component 2025 phi0 = np.arctan2(J[1], J[0]) 2026 phi0 = np.pi - phi0 2027 2028 J = ROTATEZ(phi0, J[0], J[1], J[2]) 2029 L = ROTATEZ(phi0, L[0], L[1], L[2]) 2030 S1 = ROTATEZ(phi0, S1[0], S1[1], S1[2]) 2031 S2 = ROTATEZ(phi0, S2[0], S2[1], S2[2]) 2032 2033 # now J in in the N-x plane and form an angle theta_jn with N, rotate by -theta_jn around y to have J along z 2034 theta_jn = array_polar_ang(J) 2035 spins['theta_jn'] = theta_jn 2036 2037 J = ROTATEY(theta_jn, J[0], J[1], J[2]) 2038 L = ROTATEY(theta_jn, L[0], L[1], L[2]) 2039 S1 = ROTATEY(theta_jn, S1[0], S1[1], S1[2]) 2040 S2 = ROTATEY(theta_jn, S2[0], S2[1], S2[2]) 2041 2042 # J should now be || z and L should have a azimuthal angle phi_jl 2043 phi_jl = np.arctan2(L[1], L[0]) 2044 phi_jl = np.pi - phi_jl 2045 spins['phi_jl'] = phi_jl 2046 2047 # bring L in the Z-X plane, with negative x 2048 J = ROTATEZ(phi_jl, J[0], J[1], J[2]) 2049 L = ROTATEZ(phi_jl, L[0], L[1], L[2]) 2050 S1 = ROTATEZ(phi_jl, S1[0], S1[1], S1[2]) 2051 S2 = ROTATEZ(phi_jl, S2[0], S2[1], S2[2]) 2052 2053 theta0 = array_polar_ang(L) 2054 J = ROTATEY(theta0, J[0], J[1], J[2]) 2055 L = ROTATEY(theta0, L[0], L[1], L[2]) 2056 S1 = ROTATEY(theta0, S1[0], S1[1], S1[2]) 2057 S2 = ROTATEY(theta0, S2[0], S2[1], S2[2]) 2058 2059 # The last rotation is useless as it won't change the differenze in spins' azimuthal angles 2060 phi1 = np.arctan2(S1[1], S1[0]) 2061 phi2 = np.arctan2(S2[1], S2[0]) 2062 if phi2 < phi1: 2063 phi12 = phi2 - phi1 + 2.*np.pi 2064 else: 2065 phi12 = phi2 - phi1 2066 spins['phi12'] = phi12 2067 2068 return spins
2069
2070 -class BurstPosterior(Posterior):
2071 """ 2072 Data structure for a table of posterior samples . 2073 """
2074 - def __init__(self,commonResultsFormatData,SimBurstTableEntry=None,injFref=None,SnglBurstList=None,name=None,description=None,votfile=None):
2075 """ 2076 Constructor. 2077 2078 @param commonResultsFormatData: A 2D array containing the posterior 2079 samples and related data. The samples chains form the columns. 2080 @type commonResultsFormatData: custom type 2081 @param SimInspiralTableEntry: A SimInspiralTable row containing the injected values. 2082 @type SimInspiralTableEntry: glue.ligolw.lsctables.SimInspiral 2083 @param SnglInspiralList: A list of SnglInspiral objects containing the triggers. 2084 @type SnglInspiralList: list 2085 """ 2086 common_output_table_header,common_output_table_raw =commonResultsFormatData 2087 self._posterior={} 2088 self._injFref=injFref 2089 self._injection=SimBurstTableEntry 2090 self._triggers=SnglBurstList 2091 self._loglaliases=['posterior', 'logl','logL','likelihood', 'deltalogl'] 2092 self._logpaliases=['logp', 'logP','prior','logprior','Prior','logPrior'] 2093 2094 self._votfile=votfile 2095 2096 common_output_table_header=[i.lower() for i in common_output_table_header] 2097 2098 # Define XML mapping 2099 self._injXMLFuncMap={ 2100 'f0':lambda inj:inj.frequency, 2101 'frequency':lambda inj:inj.frequency, 2102 'centre_frequency':lambda inj:inj.frequency, 2103 'quality':lambda inj:inj.q, 2104 'hrss':lambda inj:inj.hrss, 2105 'loghrss':lambda inj:log(inj.hrss), 2106 'polar_angle':lambda inj:inj.pol_ellipse_angle, 2107 'pol_ellipse_angle':lambda inj:inj.pol_ellipse_angle, 2108 'pol_ellipse_e':lambda inj:inj.pol_ellipse_e, 2109 'alpha':lambda inj:inj.pol_ellipse_angle, 2110 'polar_eccentricity':lambda inj:inj.pol_ellipse_e, 2111 'eccentricity':lambda inj:inj.pol_ellipse_e, 2112 'time': lambda inj:float(inj.get_end()), 2113 'end_time': lambda inj:float(inj.get_end()), 2114 'ra':self._inj_longitude, 2115 'rightascension':self._inj_longitude, 2116 'long':self._inj_longitude, 2117 'longitude':self._inj_longitude, 2118 'dec':lambda inj:inj.dec, 2119 'declination':lambda inj:inj.dec, 2120 'lat':lambda inj:inj.dec, 2121 'latitude':lambda inj:inj.dec, 2122 'psi': lambda inj: np.mod(inj.psi, np.pi), 2123 'f_ref': lambda inj: self._injFref, 2124 'polarisation':lambda inj:inj.psi, 2125 'polarization':lambda inj:inj.psi, 2126 'duration':lambda inj:inj.duration, 2127 'h1_end_time':lambda inj:float(inj.get_end('H')), 2128 'l1_end_time':lambda inj:float(inj.get_end('L')), 2129 'v1_end_time':lambda inj:float(inj.get_end('V')), 2130 } 2131 2132 for one_d_posterior_samples,param_name in zip(np.hsplit(common_output_table_raw,common_output_table_raw.shape[1]),common_output_table_header): 2133 2134 self._posterior[param_name]=PosteriorOneDPDF(param_name.lower(),one_d_posterior_samples,injected_value=self._getinjpar(param_name),injFref=self._injFref,trigger_values=self._gettrigpar(param_name)) 2135 2136 logLFound=False 2137 2138 for loglalias in self._loglaliases: 2139 if loglalias in common_output_table_header: 2140 try: 2141 self._logL=self._posterior[loglalias].samples 2142 except KeyError: 2143 print "No '%s' column in input table!"%loglalias 2144 continue 2145 logLFound=True 2146 2147 if not logLFound: 2148 raise RuntimeError("No likelihood/posterior values found!") 2149 2150 self._logP=None 2151 for logpalias in self._logpaliases: 2152 if logpalias in common_output_table_header: 2153 try: 2154 self._logP=self._posterior[logpalias].samples 2155 except KeyError: 2156 print "No '%s' column in input table!"%logpalias 2157 continue 2158 if not 'log' in logpalias: 2159 self._logP=[np.log(i) for i in self._logP] 2160 if name is not None: 2161 self.__name=name 2162 2163 if description is not None: 2164 self.__description=description 2165 2166 return
2167 #=============================================================================== 2168 # Functions used to parse injection structure. 2169 #=============================================================================== 2170
2171 - def _inj_longitude(self,inj):
2172 """ 2173 Return the mapping of longitude found in inj to the interval [0,2*pi). 2174 2175 @type inj: glue.ligolw.lsctables.SimInspiral 2176 @param inj: a custom type with the attribute 'longitude'. 2177 @rtype: number 2178 """ 2179 if inj.ra>2*pi_constant or inj.ra<0.0: 2180 maplong=2*pi_constant*(((float(inj.ra)/(2*pi_constant)) - floor(((float(inj.ra))/(2*pi_constant))))) 2181 print "Warning: Injected longitude/ra (%s) is not within [0,2\pi)! Angles are assumed to be in radians so this will be mapped to [0,2\pi). Mapped value is: %s."%(str(inj.ra),str(maplong)) 2182 return maplong 2183 else: 2184 return inj.ra
2185
2186 -class KDTree(object):
2187 """ 2188 A kD-tree. 2189 """
2190 - def __init__(self, objects):
2191 """ 2192 Construct a kD-tree from a sequence of objects. Each object 2193 should return its coordinates using obj.coord(). 2194 """ 2195 if len(objects) == 0: 2196 raise RuntimeError("cannot construct kD-tree out of zero objects---you may have a repeated sample in your list") 2197 elif len(objects) == 1: 2198 self._objects = objects[:] 2199 coord=self._objects[0].coord() 2200 self._bounds = coord,coord 2201 elif self._same_coords(objects): 2202 # All the same coordinates 2203 self._objects = [ objects[0] ] 2204 coord=self._objects[0].coord() 2205 self._bounds = coord,coord 2206 else: 2207 self._objects = objects[:] 2208 self._bounds = self._bounds_of_objects() 2209 low,high=self._bounds 2210 self._split_dim=self._longest_dimension() 2211 longest_dim = self._split_dim 2212 sorted_objects=sorted(self._objects, key=lambda obj: (obj.coord())[longest_dim]) 2213 N = len(sorted_objects) 2214 bound=0.5*(sorted_objects[N/2].coord()[longest_dim] + sorted_objects[N/2-1].coord()[longest_dim]) 2215 low = [obj for obj in self._objects if obj.coord()[longest_dim] < bound] 2216 high = [obj for obj in self._objects if obj.coord()[longest_dim] >= bound] 2217 if len(low)==0: 2218 # Then there must be multiple values with the same 2219 # coordinate as the minimum element of high 2220 low = [obj for obj in self._objects if obj.coord()[longest_dim]==bound] 2221 high = [obj for obj in self._objects if obj.coord()[longest_dim] > bound] 2222 self._left = KDTree(low) 2223 self._right = KDTree(high)
2224
2225 - def _same_coords(self, objects):
2226 """ 2227 True if and only if all the given objects have the same 2228 coordinates. 2229 """ 2230 if len(objects) <= 1: 2231 return True 2232 coords = [obj.coord() for obj in objects] 2233 c0 = coords[0] 2234 for ci in coords[1:]: 2235 if not np.all(ci == c0): 2236 return False 2237 return True
2238
2239 - def _bounds_of_objects(self):
2240 """ 2241 Bounds of the objects contained in the tree. 2242 """ 2243 low=self._objects[0].coord() 2244 high=self._objects[0].coord() 2245 for obj in self._objects[1:]: 2246 low=np.minimum(low,obj.coord()) 2247 high=np.maximum(high,obj.coord()) 2248 return low,high
2249
2250 - def _longest_dimension(self):
2251 """ 2252 Longest dimension of the tree bounds. 2253 """ 2254 low,high = self._bounds 2255 widths = high-low 2256 return np.argmax(widths)
2257
2258 - def objects(self):
2259 """ 2260 Returns the objects in the tree. 2261 """ 2262 return self._objects[:]
2263
2264 - def __iter__(self):
2265 """ 2266 Iterator over all the objects contained in the tree. 2267 """ 2268 return self._objects.__iter__()
2269
2270 - def left(self):
2271 """ 2272 Returns the left tree. 2273 """ 2274 return self._left
2275
2276 - def right(self):
2277 """ 2278 Returns the right tree. 2279 """ 2280 return self._right
2281
2282 - def split_dim(self):
2283 """ 2284 Returns the dimension along which this level of the kD-tree 2285 splits. 2286 """ 2287 return self._split_dim
2288
2289 - def bounds(self):
2290 """ 2291 Returns the coordinates of the lower-left and upper-right 2292 corners of the bounding box for this tree: low_left, up_right 2293 """ 2294 return self._bounds
2295
2296 - def volume(self):
2297 """ 2298 Returns the volume of the bounding box of the tree. 2299 """ 2300 v = 1.0 2301 low,high=self._bounds 2302 for l,h in zip(low,high): 2303 v = v*(h - l) 2304 return v
2305
2306 - def integrate(self,f,boxing=64):
2307 """ 2308 Returns the integral of f(objects) over the tree. The 2309 optional boxing parameter determines how deep to descend into 2310 the tree before computing f. 2311 """ 2312 # if len(self._objects) <= boxing: 2313 # return self.volume()*f(self._objects) 2314 # else: 2315 # return self._left.integrate(f, boxing) + self._right.integrate(f, boxing) 2316 2317 def x(tree): 2318 return tree.volume()*f(tree._objects)
2319 2320 def y(a,b): 2321 return a+b
2322 2323 return self.operate(x,y,boxing=boxing) 2324
2325 - def operate(self,f,g,boxing=64):
2326 """ 2327 Operates on tree nodes exceeding boxing parameter depth. 2328 """ 2329 if len(self._objects) <= boxing: 2330 return f(self) 2331 else: 2332 2333 return g(self._left.operate(f,g,boxing),self._right.operate(f,g,boxing))
2334
2335 2336 -class KDTreeVolume(object):
2337 """ 2338 A kD-tree suitable for splitting parameter spaces and counting hypervolumes. 2339 Is modified from the KDTree class so that bounding boxes are stored. This means that 2340 there are no longer gaps in the hypervolume once the samples have been split into groups. 2341 """
2342 - def __init__(self, objects,boundingbox,dims=0):
2343 """ 2344 Construct a kD-tree from a sequence of objects. Each object 2345 should return its coordinates using obj.coord(). 2346 the obj should also store the bounds of the hypervolume its found in. 2347 for non-leaf objects we need the name of the dimension split and value at split. 2348 """ 2349 self._dimension = dims 2350 self._bounds = boundingbox 2351 self._weight = 1 2352 if len(objects) == 0: #for no objects - something is wrong, i think it can only happen in first call 2353 raise RuntimeError("cannot construct kD-tree out of zero objects---you may have a repeated sample in your list") 2354 elif len(objects) == 1: #1 object, have reached leaf of tree 2355 self._objects = objects[:] 2356 elif self._same_coords(objects): # When ALL samples have the same coordinates in all dimensions 2357 self._weight = len(objects) 2358 self._objects = [ objects[0] ] #need to modify kdtree_bin functions to use _weight to get correct number of samples 2359 coord=self._objects[0].coord() 2360 else: #construct next level of tree with multiple samples 2361 self._objects = objects[:] 2362 split_dim = self._dimension 2363 sorted_objects=sorted(self._objects, key=lambda obj: (obj.coord())[split_dim]) 2364 N = len(sorted_objects) 2365 self._split_value = 0.5*(sorted_objects[N/2].coord()[split_dim] + sorted_objects[N/2-1].coord()[split_dim]) 2366 bound = self._split_value 2367 low = [obj for obj in self._objects if obj.coord()[split_dim] < bound] 2368 high = [obj for obj in self._objects if obj.coord()[split_dim] >= bound] 2369 if len(low)==0: 2370 # Then there must be multiple values with the same 2371 # coordinate as the minimum element of 'high' 2372 low = [obj for obj in self._objects if obj.coord()[split_dim] == bound] 2373 high = [obj for obj in self._objects if obj.coord()[split_dim] > bound] 2374 leftBoundingbox = [] 2375 rightBoundingbox = [] 2376 for i in self._bounds: 2377 leftBoundingbox.append(list(i)) 2378 rightBoundingbox.append(list(i)) 2379 leftBoundingbox[1][split_dim] = bound 2380 rightBoundingbox[0][split_dim] = bound 2381 # designate the next dimension to use for split for sub-trees 2382 # if has got to the end of the list of dimensions then starts 2383 # again at dimension = 0 2384 if (split_dim < (len(self._objects[0].coord()) - 1)): 2385 child_dim = split_dim + 1 2386 else: 2387 child_dim = 0 2388 self._left = KDTreeVolume(low,leftBoundingbox,dims = child_dim) 2389 # added in a load of messing about incase there are repeated values in the currently checked dimension 2390 if (len(high) != 0): 2391 self._right = KDTreeVolume(high,rightBoundingbox,dims = child_dim) 2392 else: 2393 self._right = None
2394
2395 - def _same_coords(self, objects):
2396 """ 2397 True if and only if all the given objects have the same 2398 coordinates. 2399 """ 2400 if len(objects) <= 1: 2401 return True 2402 coords = [obj.coord() for obj in objects] 2403 c0 = coords[0] 2404 for ci in coords[1:]: 2405 if not np.all(ci == c0): 2406 return False 2407 return True
2408
2409 - def objects(self):
2410 """ 2411 Returns the objects in the tree. 2412 """ 2413 return self._objects[:]
2414
2415 - def __iter__(self):
2416 """ 2417 Iterator over all the objects contained in the tree. 2418 """ 2419 return self._objects.__iter__()
2420
2421 - def left(self):
2422 """ 2423 Returns the left tree. 2424 """ 2425 return self._left
2426
2427 - def right(self):
2428 """ 2429 Returns the right tree. 2430 """ 2431 return self._right
2432
2433 - def split_dim(self):
2434 """ 2435 Returns the dimension along which this level of the kD-tree 2436 splits. 2437 """ 2438 return self._split_dim
2439
2440 - def bounds(self):
2441 """ 2442 Returns the coordinates of the lower-left and upper-right 2443 corners of the bounding box for this tree: low_left, up_right 2444 """ 2445 return self._bounds
2446
2447 - def volume(self):
2448 """ 2449 Returns the volume of the bounding box of the tree. 2450 """ 2451 v = 1.0 2452 low,high=self._bounds 2453 for l,h in zip(low,high): 2454 v = v*(h - l) 2455 return v
2456
2457 - def integrate(self,f,boxing=64):
2458 """ 2459 Returns the integral of f(objects) over the tree. The 2460 optional boxing parameter determines how deep to descend into 2461 the tree before computing f. 2462 """ 2463 def x(tree): 2464 return tree.volume()*f(tree._objects)
2465 2466 def y(a,b): 2467 return a+b
2468 2469 return self.operate(x,y,boxing=boxing) 2470
2471 - def operate(self,f,g,boxing=64):
2472 """ 2473 Operates on tree nodes exceeding boxing parameter depth. 2474 """ 2475 if len(self._objects) <= boxing: 2476 return f(self) 2477 else: 2478 return g(self._left.operate(f,g,boxing),self._right.operate(f,g,boxing))
2479
2480 - def search(self,coordinates,boxing = 64):
2481 """ 2482 takes a set of coordinates and searches down through the tree untill it gets 2483 to a box with less than 'boxing' objects in it and returns the box bounds, 2484 number of objects in the box, and the weighting. 2485 """ 2486 if len(self._objects) <= boxing: 2487 return self._bounds,len(self._objects),self._weight 2488 elif coordinates[self._dimension] < self._split_value: 2489 return self._left.search(coordinates,boxing) 2490 else: 2491 return self._right.search(coordinates,boxing)
2492
2493 - def fillNewTree(self,boxing = 64, isArea = False):
2494 """ 2495 copies tree structure, but with KDSkeleton as the new nodes. 2496 """ 2497 boxN = boxing 2498 if len(self._objects) <= boxN: 2499 newNode = KDSkeleton(self.bounds(), left_child = None , right_child = None) 2500 if isArea: 2501 newNode.setImportance(len(self._objects),skyArea(self.bounds())) 2502 else: 2503 newNode.setImportance(len(self._objects),self.volume()) 2504 return newNode 2505 else: 2506 if isArea: 2507 newNode = KDSkeleton(self.bounds, left_child = self._left.fillNewTree(boxN,isArea=True), right_child = self._right.fillNewTree(boxN,isArea=True)) 2508 newNode.setImportance(len(self._objects),skyArea(self.bounds())) 2509 else: 2510 newNode = KDSkeleton(self.bounds, left_child = self._left.fillNewTree(boxN), right_child = self._right.fillNewTree(boxN)) 2511 newNode.setImportance(len(self._objects),self.volume()) 2512 newNode.setSplit(self._dimension,self._split_value) 2513 return newNode
2514
2515 -class KDSkeleton(object):
2516 """ 2517 object to store the structure of a kd tree 2518 """ 2519
2520 - def __init__(self, bounding_box, left_child = None, right_child = None):
2521 self._bounds = bounding_box 2522 #self._names = coordinate_names 2523 self._left = left_child 2524 self._right = right_child 2525 self._samples = 0 2526 self._splitValue = None 2527 self._splitDim = None 2528 self._importance = None 2529 self._volume = None
2530
2531 - def addSample(self):
2532 self._samples +=1
2533
2534 - def bounds(self):
2535 return self._bounds
2536
2537 - def search(self,coordinates):
2538 """ 2539 takes a set of coordinates and searches down through the tree untill it gets 2540 to a box with less than 'boxing' objects in it and returns the box bounds, 2541 number of objects in the box, and the weighting. 2542 """ 2543 if self._left is None: 2544 return self._bounds, self._samples, self._importance 2545 elif coordinates[self._splitDim] < self._splitValue: 2546 return self._left.search(coordinates) 2547 else: 2548 return self._right.search(coordinates)
2549
2550 - def setImportance(self, sampleNumber, volume):
2551 self._importance = sampleNumber/volume 2552 self._volume = volume
2553
2554 - def setSplit(self,dimension,value):
2555 self._splitDim = dimension 2556 self._splitValue = value
2557
2558 2559 -class PosteriorSample(object):
2560 """ 2561 A single parameter sample object, suitable for inclusion in a 2562 kD-tree. 2563 """ 2564
2565 - def __init__(self, sample_array, headers, coord_names):
2566 """ 2567 Given the sample array, headers for the values, and the names 2568 of the desired coordinates, construct a parameter sample 2569 object. 2570 """ 2571 self._samples=sample_array[:] 2572 self._headers=headers 2573 if not (len(sample_array) == len(self._headers)): 2574 print "Header length = ", len(self._headers) 2575 print "Sample length = ", len(sample_array) 2576 raise RuntimeError("parameter and sample lengths do not agree") 2577 self._coord_names=coord_names 2578 self._coord_indexes=[self._headers.index(name) for name in coord_names]
2579
2580 - def __getitem__(self, key):
2581 """ 2582 Return the element with the corresponding name. 2583 """ 2584 key=key.lower() 2585 if key in self._headers: 2586 idx=self._headers.index(key) 2587 return self._samples[idx] 2588 else: 2589 raise KeyError("key not found in posterior sample: %s"%key)
2590
2591 - def coord(self):
2592 """ 2593 Return the coordinates for the parameter sample. 2594 """ 2595 return self._samples[self._coord_indexes]
2596
2597 2598 2599 2600 2601 -class AnalyticLikelihood(object):
2602 """ 2603 Return analytic likelihood values. 2604 """ 2605
2606 - def __init__(self, covariance_matrix_files, mean_vector_files):
2607 """ 2608 Prepare analytic likelihood for the given parameters. 2609 """ 2610 # Make sure files names are in a list 2611 if isinstance(covariance_matrix_files, str): 2612 covariance_matrix_files = [covariance_matrix_files] 2613 if isinstance(mean_vector_files, str): 2614 mean_vector_files = [mean_vector_files] 2615 2616 covarianceMatrices = [np.loadtxt(csvFile, delimiter=',') for csvFile in covariance_matrix_files] 2617 num_matrices = len(covarianceMatrices) 2618 2619 if num_matrices != len(mean_vector_files): 2620 raise RuntimeError('Must give a mean vector list for every covariance matrix') 2621 2622 param_line = open(mean_vector_files[0]).readline() 2623 self._params = [param.strip() for param in param_line.split(',')] 2624 2625 converter=lambda x: eval(x.replace('pi','%.32f'%pi_constant)) # converts fractions w/ pi (e.g. 3.0*pi/2.0) 2626 self._modes = [] 2627 for i in range(num_matrices): 2628 CM = covarianceMatrices[i] 2629 vecFile = mean_vector_files[i] 2630 2631 param_line = open(vecFile).readline() 2632 params = [param.strip() for param in param_line.split(',')] 2633 if set(params)!=set(self._params): 2634 raise RuntimeError('Parameters do not agree between mean vector files.') 2635 2636 sigmas = dict(zip(params,np.sqrt(CM.diagonal()))) 2637 colNums = range(len(params)) 2638 converters = dict(zip(colNums,[converter for i in colNums])) 2639 meanVectors = np.loadtxt(vecFile, delimiter=',', skiprows=1, converters=converters) 2640 try: 2641 for vec in meanVectors: 2642 means = dict(zip(params,vec)) 2643 mode = [(param, stats.norm(loc=means[param],scale=sigmas[param])) for param in params] 2644 self._modes.append(dict(mode)) 2645 except TypeError: 2646 means = dict(zip(params,meanVectors)) 2647 mode = [(param, stats.norm(loc=means[param],scale=sigmas[param])) for param in params] 2648 self._modes.append(dict(mode)) 2649 self._num_modes = len(self._modes)
2650
2651 - def pdf(self, param):
2652 """ 2653 Return PDF function for parameter. 2654 """ 2655 pdf = None 2656 if param in self._params: 2657 pdf = lambda x: (1.0/self._num_modes) * sum([mode[param].pdf(x) for mode in self._modes]) 2658 return pdf
2659
2660 - def cdf(self, param):
2661 """ 2662 Return PDF function for parameter. 2663 """ 2664 cdf = None 2665 if param in self._params: 2666 cdf = lambda x: (1.0/self._num_modes) * sum([mode[param].cdf(x) for mode in self._modes]) 2667 return cdf
2668 2669 @property
2670 - def names(self):
2671 """ 2672 Return list of parameter names described by analytic likelihood function. 2673 """ 2674 return self._params
2675
2676 2677 2678 #=============================================================================== 2679 # Web page creation classes (wrap ElementTrees) 2680 #=============================================================================== 2681 2682 -class htmlChunk(object):
2683 """ 2684 A base class for representing web content using ElementTree . 2685 """
2686 - def __init__(self,tag,attrib=None,parent=None):
2687 2688 self._html=Element(tag)#attrib={'xmlns':"http://www.w3.org/1999/xhtml"}) 2689 if attrib: 2690 for attribname,attribvalue in attrib.items(): 2691 self._html.attrib[attribname]=attribvalue 2692 if parent: 2693 parent.append(self._html)
2694
2695 - def toprettyxml(self):
2696 """ 2697 Return a pretty-printed XML string of the htmlPage. 2698 """ 2699 elem=self._html 2700 rough_string = tostring(elem) 2701 reparsed = minidom.parseString(rough_string) 2702 return reparsed.toprettyxml(indent=" ")
2703
2704 - def __str__(self):
2705 return self.toprettyxml()
2706
2707 - def write(self,string):
2708 parser=XMLParser() 2709 parser.feed(string) 2710 Estr=parser.close() 2711 self._html.append(Estr)
2712
2713 - def p(self,pstring):
2714 Ep=Element('p') 2715 Ep.text=pstring 2716 self._html.append(Ep) 2717 return Ep
2718
2719 - def h1(self,h1string):
2720 Ep=Element('h1') 2721 Ep.text=h1string 2722 self._html.append(Ep) 2723 return Ep
2724 #
2725 - def h5(self,h1string):
2726 Ep=Element('h5') 2727 Ep.text=h1string 2728 self._html.append(Ep) 2729 return Ep
2730
2731 - def h2(self,h2string):
2732 Ep=Element('h2') 2733 Ep.text=h2string 2734 self._html.append(Ep) 2735 return Ep
2736
2737 - def h3(self,h1string):
2738 Ep=Element('h3') 2739 Ep.text=h1string 2740 self._html.append(Ep) 2741 return Ep
2742
2743 - def br(self):
2744 Ebr=Element('br') 2745 self._html.append(Ebr) 2746 return Ebr
2747
2748 - def hr(self):
2749 Ehr=Element('hr') 2750 self._html.append(Ehr) 2751 return Ehr
2752
2753 - def a(self,url,linktext):
2754 Ea=Element('a') 2755 Ea.attrib['href']=url 2756 Ea.text=linktext 2757 self._html.append(Ea) 2758 return Ea
2759
2760 - def tab(self,idtable=None):
2761 args={} 2762 if idtable is not None: 2763 args={'id':idtable} 2764 2765 Etab=Element('table',args) 2766 self._html.append(Etab) 2767 return Etab
2768
2769 - def insert_row(self,tab,label=None):
2770 2771 """ 2772 Insert row in table tab. 2773 If given, label used as id for the table tag 2774 """ 2775 2776 Etr=Element('tr') 2777 if label is not None: 2778 Etr.attrib['id']=label 2779 tab.append(Etr) 2780 return Etr
2781
2782 - def insert_td(self,row,td,label=None,legend=None):
2783 """ 2784 Insert cell td into row row. 2785 Sets id to label, if given 2786 """ 2787 2788 Etd=Element('td') 2789 2790 if type(td) is str: 2791 Etd.text=td 2792 else: 2793 td=tostring(td) 2794 td=minidom.parseString(td) 2795 td=td.toprettyxml(indent=" ") 2796 Etd.text=td 2797 if label is not None: 2798 Etd.attrib['id']=label 2799 if legend is not None: 2800 legend.a('#%s'%label,'%s'%label) 2801 legend.br() 2802 row.append(Etd) 2803 return Etd
2804
2805 - def append(self,element):
2806 self._html.append(element)
2807
2808 2809 # 2810 -class htmlPage(htmlChunk):
2811 """ 2812 A concrete class for generating an XHTML(1) document. Inherits from htmlChunk. 2813 """
2814 - def __init__(self,title=None,css=None,javascript=None,toc=False):
2815 htmlChunk.__init__(self,'html',attrib={'xmlns':"http://www.w3.org/1999/xhtml"}) 2816 self.doctype_str='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' 2817 2818 self._head=SubElement(self._html,'head') 2819 Etitle=SubElement(self._head,'title') 2820 self._body=SubElement(self._html,'body') 2821 self._css=None 2822 self._jscript=None 2823 if title is not None: 2824 Etitle.text=str(title) 2825 self._title=SubElement(self._body,'h1') 2826 self._title.text=title 2827 if javascript is not None: 2828 self._jscript=SubElement(self._head,'script') 2829 self._jscript.attrib['type']="text/javascript" 2830 self._jscript.text=str(javascript) 2831 if css is not None: 2832 self._css=SubElement(self._head,'style') 2833 self._css.attrib['type']="text/css" 2834 self._css.text=str(css)
2835
2836 - def __str__(self):
2837 return self.doctype_str+'\n'+self.toprettyxml()
2838
2839 - def add_section(self,section_name,legend=None):
2840 newSection=htmlSection(section_name) 2841 self._body.append(newSection._html) 2842 if legend is not None: 2843 legend.a('#%s'%section_name,'%s'%section_name) 2844 legend.br() 2845 return newSection
2846
2847 - def add_collapse_section(self,section_name,legend=None,innertable_id=None,start_closed=True):
2848 """ 2849 Create a section embedded into a table that can be collapsed with a button 2850 """ 2851 newSection=htmlCollapseSection(section_name,table_id=innertable_id,start_closed=start_closed) 2852 self._body.append(newSection._html) 2853 if legend is not None: 2854 legend.a('#%s'%section_name,'%s'%section_name) 2855 legend.br() 2856 return newSection
2857
2858 - def add_section_to_element(self,section_name,parent):
2859 """ 2860 Create a section which is not appended to the body of html, but to the parent Element 2861 """ 2862 newSection=htmlSection(section_name,htmlElement=parent,blank=True) 2863 parent.append(newSection._html) 2864 return newSection
2865 2866 2867 @property
2868 - def body():
2869 return self._body
2870 2871 @property
2872 - def head():
2873 return self._head
2874
2875 2876 -class htmlSection(htmlChunk):
2877 """ 2878 Represents a block of html fitting within a htmlPage. Inherits from htmlChunk. 2879 """
2880 - def __init__(self,section_name,htmlElement=None,blank=False):
2881 if not blank: 2882 htmlChunk.__init__(self,'div',attrib={'class':'ppsection','id':section_name},parent=htmlElement) 2883 else: 2884 htmlChunk.__init__(self,'div',attrib={'style':'"color:#000000"','id':section_name},parent=htmlElement) 2885 self.h2(section_name)
2886
2887 2888 -class htmlCollapseSection(htmlChunk):
2889 """ 2890 Represents a block of html fitting within a htmlPage. Inherits from htmlChunk. 2891 """ 2892
2893 - def __init__(self,section_name,htmlElement=None,table_id=None,start_closed=True):
2894 htmlChunk.__init__(self,'div',attrib={'class':'ppsection','id':section_name},parent=htmlElement) 2895 # if table id is none, generate a random id: 2896 if table_id is None: 2897 table_id=random.randint(1,10000000) 2898 self.table_id=table_id 2899 self._start_closed=start_closed
2900
2901 - def write(self,string):
2902 k=random.randint(1,10000000) 2903 if self._start_closed: 2904 st='<table border="0" align="center" cellpadding="5" cellspacing="0"><tr bgcolor="#4682B4" height="50"><td width="5%%"><font size="4" face="tahoma" color="white"><a href="#"> Top</a></font></td><td width="45%%"><font size="4" face="tahoma" color="white"><strong>%s</strong></font></td><td bgcolor="#4682B4" align="center" width="50%%"><input id="lnk%s" type="button" value="[+] Expand" onclick="toggle_visibility(\'%s\',\'lnk%s\');"></input></td></tr><tr><td colspan="7">'%(self._html.attrib['id'],k,self.table_id,k) 2905 string=string.replace('table ', 'table style="display: none" ') 2906 else: 2907 st='<table border="0" align="center" cellpadding="5" cellspacing="0"><tr bgcolor="#4682B4" height="50"><td width="5%%"><font size="4" face="tahoma" color="white"><a href="#"> Top</a></font></td><td width="45%%"><font size="4" face="tahoma" color="white"><strong>%s</strong></font></td><td bgcolor="#4682B4" align="center" width="50%%"><input id="lnk%s" type="button" value="[-] Collapse" onclick="toggle_visibility(\'%s\',\'lnk%s\');"></input></td></tr><tr><td colspan="7">'%(self._html.attrib['id'],k,self.table_id,k) 2908 string=string.replace('table ', 'table style="display: table" ') 2909 st+=string 2910 st+='</td></tr></table>' 2911 htmlChunk.write(self,st)
2912
2913 #=============================================================================== 2914 # Internal module functions 2915 #=============================================================================== 2916 2917 -def _skyhist_cart_slow(skycarts,sky_samples):
2918 """ 2919 @deprecated: This is a pure python version of the C extension function 2920 pylal._bayespputils._skyhist_cart . 2921 """ 2922 2923 N=len(skycarts) 2924 print 'operating on %d sky points'%(N) 2925 bins=np.zeros(N) 2926 for RAsample,decsample in sky_samples: 2927 sampcart=pol2cart(RAsample,decsample) 2928 maxdx=-1 2929 maxvalue=-1 2930 for i in xrange(0,N): 2931 dx=np.dot(sampcart,skycarts[i]) 2932 if dx>maxvalue: 2933 maxdx=i 2934 maxvalue=dx 2935 2936 bins[maxdx]+=1 2937 return bins
2938 #
2939 -def _sky_hist(skypoints,samples):
2940 """ 2941 @deprecated: This is an old pure python version of the C extension function 2942 pylal._bayespputils._skyhist_cart . 2943 """ 2944 N=len(skypoints) 2945 print 'operating on %d sky points' % (N) 2946 bins=zeros(N) 2947 j=0 2948 for sample in samples: 2949 seps=map(lambda s: ang_dist(sample[RAdim],sample[decdim],s[1],s[0]),skypoints) 2950 minsep=math.pi 2951 for i in range(0,N): 2952 if seps[i]<minsep: 2953 minsep=seps[i] 2954 mindx=i 2955 bins[mindx]=bins[mindx]+1 2956 j=j+1 2957 print 'Done %d/%d iterations, minsep=%f degrees'\ 2958 %(j,len(samples),minsep*(180.0/3.1415926)) 2959 return (skypoints,bins)
2960 #
2961 2962 -def _calculate_confidence_levels(hist, points, injBin, NSamples):
2963 """ 2964 Returns (injectionconf, toppoints), where injectionconf is the 2965 confidence level of the injection, contained in the injBin and 2966 toppoints is a list of (pointx, pointy, ptindex, frac), with 2967 pointx and pointy the (x,y) coordinates of the corresponding 2968 element of the points array, ptindex the index of the point in the 2969 array, and frac the cumulative fraction of points with larger 2970 posterior probability. 2971 2972 The hist argument should be a one-dimensional array that contains 2973 counts of sample points in each bin. 2974 2975 The points argument should be a 2-D array storing the sky location 2976 associated with each bin; the first index runs from 0 to NBins - 2977 1, while the second index runs from 0 to 1. 2978 2979 The injBin argument gives the bin index in which the injection is 2980 found. 2981 2982 The NSamples argument is used to normalize the histogram counts 2983 into fractional probability. 2984 """ 2985 2986 histIndices=np.argsort(hist)[::-1] # In decreasing order 2987 2988 toppoints=[] 2989 frac=0.0 2990 injConf=None 2991 for i in histIndices: 2992 frac+=float(hist[i])/float(NSamples) 2993 toppoints.append((points[i,0], points[i,1], i, frac)) 2994 if i == injBin: 2995 injConf=frac 2996 print 'Injection found at confidence level %g'%injConf 2997 2998 return (injConf, toppoints)
2999
3000 -def _greedy_bin(greedyHist,greedyPoints,injection_bin_index,bin_size,Nsamples,confidence_levels):
3001 """ 3002 An interal function representing the common, dimensionally-independent part of the 3003 greedy binning algorithms. 3004 """ 3005 3006 #Now call confidence level C extension function to determine top-ranked pixels 3007 (injectionconfidence,toppoints)=_calculate_confidence_levels(greedyHist, greedyPoints, injection_bin_index, Nsamples) 3008 3009 #Determine interval/area contained within given confidence intervals 3010 nBins=0 3011 confidence_levels.sort() 3012 reses={} 3013 toppoints=np.array(toppoints) 3014 for printcl in confidence_levels: 3015 nBins=np.searchsorted(toppoints[:,3], printcl) + 1 3016 3017 if nBins >= len(toppoints): 3018 nBins=len(toppoints)-1 3019 3020 accl=toppoints[nBins-1,3] 3021 3022 reses[printcl]=nBins*bin_size 3023 3024 #Find area 3025 injection_area=None 3026 if injection_bin_index and injectionconfidence: 3027 i=list(np.nonzero(np.asarray(toppoints)[:,2]==injection_bin_index))[0] 3028 injection_area=bin_size*(i+1) 3029 3030 return toppoints,injectionconfidence,reses,injection_area
3031 #
3032 #### functions used in 2stage kdtree 3033 3034 -def skyArea(bounds):
3035 return - (cos(pi_constant/2. - bounds[0][1])-cos(pi_constant/2. - bounds[1][1]))*(bounds[1][0] - bounds[0][0])
3036
3037 -def random_split(items, fraction):
3038 size = int(len(items)*fraction) 3039 random.shuffle(items) 3040 return items[:size], items[size:]
3041
3042 -def addSample(tree,coordinates):
3043 if tree._left is None: 3044 tree.addSample() 3045 elif coordinates[tree._splitDim] < tree._splitValue: 3046 addSample(tree._left,coordinates) 3047 else: 3048 addSample(tree._right,coordinates)
3049
3050 # 3051 #=============================================================================== 3052 # Public module functions 3053 #=============================================================================== 3054 3055 -def kdtree_bin_sky_volume(posterior,confidence_levels):
3056 3057 confidence_levels.sort() 3058 3059 class Harvester(list): 3060 3061 def __init__(self): 3062 list.__init__(self) 3063 self.unrho=0.
3064 3065 def __call__(self,tree): 3066 number_density=float(len(tree.objects()))/float(tree.volume()) 3067 self.append([number_density,tree.volume(),tree.bounds()]) 3068 self.unrho+=number_density 3069 3070 def close_ranks(self): 3071 3072 for i in range(len(self)): 3073 self[i][0]/=self.unrho 3074 3075 return sorted(self,key=itemgetter(0)) 3076 3077 def h(a,b): 3078 pass 3079 3080 samples,header=posterior.samples() 3081 header=header.split() 3082 coord_names=["ra","dec","dist"] 3083 coordinatized_samples=[PosteriorSample(row, header, coord_names) for row in samples] 3084 tree=KDTree(coordinatized_samples) 3085 3086 a=Harvester() 3087 samples_per_bin=10 3088 tree.operate(a,h,boxing=samples_per_bin) 3089 3090 b=a.close_ranks() 3091 b.reverse() 3092 3093 acc_rho=0. 3094 acc_vol=0. 3095 cl_idx=0 3096 confidence_intervals={} 3097 for rho,vol,bounds in b: 3098 acc_rho+=rho 3099 acc_vol+=vol 3100 3101 if acc_rho>confidence_levels[cl_idx]: 3102 confidence_intervals[acc_rho]=acc_vol 3103 cl_idx+=1 3104 if cl_idx==len(confidence_levels): 3105 break 3106 3107 return confidence_intervals 3108
3109 -def kdtree_bin_sky_area(posterior,confidence_levels,samples_per_bin=10):
3110 """ 3111 takes samples and applies a KDTree to them to return confidence levels 3112 returns confidence_intervals - dictionary of user_provided_CL:calculated_area 3113 b - ordered list of KD leaves 3114 injInfo - if injection values provided then returns 3115 [Bounds_of_inj_kd_leaf ,number_samples_in_box, weight_of_box,injection_CL ,injection_CL_area] 3116 Not quite sure that the repeated samples case is fixed, posibility of infinite loop. 3117 """ 3118 confidence_levels.sort() 3119 from math import cos, pi 3120 class Harvester(list): 3121 """ 3122 when called by kdtree.operate will be used to calculate the density of each bin (sky area) 3123 """ 3124 def __init__(self): 3125 list.__init__(self) 3126 self.unrho=0.
3127 3128 def __call__(self,tree): 3129 #area = (cos(tree.bounds()[0][1])-cos(tree.bounds()[1][1]))*(tree.bounds()[1][0] - tree.bounds()[0][0]) 3130 area = - (cos(pi/2. - tree.bounds()[0][1])-cos(pi/2. - tree.bounds()[1][1]))*(tree.bounds()[1][0] - tree.bounds()[0][0]) 3131 number_density=float(len(tree.objects()))/float(area) 3132 self.append([number_density,len(tree.objects()),area,tree.bounds()]) 3133 self.unrho+=number_density 3134 3135 def close_ranks(self): 3136 3137 for i in range(len(self)): 3138 self[i][0]/=self.unrho 3139 3140 return sorted(self,key=itemgetter(0)) 3141 3142 def h(a,b): 3143 pass 3144 3145 peparser=PEOutputParser('common') 3146 3147 samples,header=posterior.samples() 3148 header=header.split() 3149 coord_names=["ra","dec"] 3150 initial_dimensions = [[0.,-pi/2.],[2.*pi, pi/2.]] 3151 coordinatized_samples=[PosteriorSample(row, header, coord_names) for row in samples] 3152 tree=KDTreeVolume(coordinatized_samples,initial_dimensions) 3153 3154 a=Harvester() 3155 tree.operate(a,h,boxing=samples_per_bin) 3156 totalSamples = len(tree.objects()) 3157 b=a.close_ranks() 3158 b.reverse() 3159 samplecounter=0.0 3160 for entry in b: 3161 samplecounter += entry[1] 3162 entry[1] = float(samplecounter)/float(totalSamples) 3163 3164 acc_rho=0. 3165 acc_vol=0. 3166 cl_idx=0 3167 3168 #checks for injection and extract details of the node in the tree that the injection is found 3169 if posterior['ra'].injval is not None and posterior['dec'].injval is not None: 3170 injBound,injNum,injWeight = tree.search([posterior['ra'].injval,posterior['dec'].injval],boxing = samples_per_bin) 3171 injInfo = [injBound,injNum,injWeight] 3172 inj_area = - (cos(pi/2. - injBound[0][1])-cos(pi/2. - injBound[1][1]))*(injBound[1][0] - injBound[0][0]) 3173 inj_number_density=float(injNum)/float(inj_area) 3174 inj_rho = inj_number_density / a.unrho 3175 else: 3176 injInfo = None 3177 inj_area = None 3178 inj_number_density=None 3179 inj_rho = None 3180 3181 #finds the volume contained within the confidence levels requested by user 3182 confidence_intervals={} 3183 for rho,confidence_level,vol,bounds in b: 3184 acc_vol+=vol 3185 3186 if confidence_level>confidence_levels[cl_idx]: 3187 print str(confidence_level) 3188 print acc_vol 3189 confidence_intervals[confidence_levels[cl_idx]]=acc_vol 3190 cl_idx+=1 3191 if cl_idx==len(confidence_levels): 3192 break 3193 3194 acc_vol = 0. 3195 for rho,sample_number,vol,bounds in b: 3196 acc_vol+=vol 3197 print 'total area: ' + str(acc_vol) 3198 3199 #finds the confidence level of the injection and the volume of the associated contained region 3200 inj_confidence = None 3201 inj_confidence_area = None 3202 if inj_rho is not None: 3203 acc_vol=0. 3204 for rho,confidence_level,vol,bounds in b: 3205 acc_vol+=vol 3206 if rho <= inj_rho: 3207 inj_confidence = confidence_level 3208 inj_confidence_area = acc_vol 3209 injInfo.append(inj_confidence) 3210 injInfo.append(inj_confidence_area) 3211 print 'inj ' +str(vol) 3212 break 3213 return confidence_intervals, b, injInfo 3214
3215 -def kdtree_bin(posterior,coord_names,confidence_levels,initial_boundingbox = None,samples_per_bin = 10):
3216 """ 3217 takes samples and applies a KDTree to them to return confidence levels 3218 returns confidence_intervals - dictionary of user_provided_CL:calculated_volume 3219 b - ordered list of KD leaves 3220 initial_boundingbox - list of lists [upperleft_coords,lowerright_coords] 3221 injInfo - if injection values provided then returns 3222 [Bounds_of_inj_kd_leaf ,number_samples_in_box, weight_of_box,injection_CL ,injection_CL_volume] 3223 Not quite sure that the repeated samples case is fixed, posibility of infinite loop. 3224 """ 3225 confidence_levels.sort() 3226 print confidence_levels 3227 class Harvester(list): 3228 """ 3229 when called by kdtree.operate will be used to calculate the density of each bin 3230 """ 3231 def __init__(self): 3232 list.__init__(self) 3233 self.unrho=0.
3234 3235 def __call__(self,tree): 3236 number_density=float(len(tree.objects()))/float(tree.volume()) 3237 self.append([number_density,len(tree.objects()),tree.volume(),tree.bounds()]) 3238 self.unrho+=number_density 3239 3240 def close_ranks(self): 3241 3242 for i in range(len(self)): 3243 self[i][0]/=self.unrho 3244 3245 return sorted(self,key=itemgetter(0)) 3246 3247 def h(a,b): 3248 pass 3249 3250 peparser=PEOutputParser('common') 3251 3252 samples,header=posterior.samples() 3253 header=header.split() 3254 coordinatized_samples=[PosteriorSample(row, header, coord_names) for row in samples] 3255 3256 #if initial bounding box is not provided, create it using max/min of sample coords. 3257 if initial_boundingbox is None: 3258 low=coordinatized_samples[0].coord() 3259 high=coordinatized_samples[0].coord() 3260 for obj in coordinatized_samples[1:]: 3261 low=np.minimum(low,obj.coord()) 3262 high=np.maximum(high,obj.coord()) 3263 initial_boundingbox = [low,high] 3264 3265 tree=KDTreeVolume(coordinatized_samples,initial_boundingbox) 3266 3267 a=Harvester() 3268 tree.operate(a,h,boxing=samples_per_bin) 3269 3270 b=a.close_ranks() 3271 b.reverse() 3272 totalSamples = len(tree.objects()) 3273 samplecounter=0.0 3274 for entry in b: 3275 samplecounter += entry[1] 3276 entry[1] = float(samplecounter)/float(totalSamples) 3277 3278 acc_rho=0. 3279 acc_vol=0. 3280 cl_idx=0 3281 3282 #check that there is an injection value for all dimension names 3283 def checkNone(listoParams): 3284 for param in listoParams: 3285 if posterior[param].injval is None: 3286 return False 3287 return True 3288 3289 #checks for injection and extract details of the lnode in the tree that the injection is found 3290 if checkNone(coord_names): 3291 injBound,injNum,injWeight = tree.search([posterior[x].injval for x in coord_names],boxing = samples_per_bin) 3292 injInfo = [injBound,injNum,injWeight] 3293 #calculate volume of injections bin 3294 inj_volume = 1. 3295 low = injBound[0] 3296 high = injBound[1] 3297 for aCoord,bCoord in zip(low,high): 3298 inj_volume = inj_volume*(bCoord - aCoord) 3299 inj_number_density=float(injNum)/float(inj_volume) 3300 inj_rho = inj_number_density / a.unrho 3301 print injNum,inj_volume,inj_number_density,a.unrho,injBound 3302 else: 3303 injInfo = None 3304 inj_area = None 3305 inj_number_density=None 3306 inj_rho = None 3307 3308 #finds the volume contained within the confidence levels requested by user 3309 confidence_intervals={} 3310 for rho,sample_number,vol,bounds in b: 3311 acc_vol+=vol 3312 3313 if sample_number>confidence_levels[cl_idx]: 3314 confidence_intervals[confidence_levels[cl_idx]]=(acc_vol,sample_number) 3315 cl_idx+=1 3316 if cl_idx==len(confidence_levels): 3317 break 3318 3319 acc_vol = 0. 3320 for rho,sample_number,vol,bounds in b: 3321 acc_vol+=vol 3322 3323 #finds the confidence level of the injection and the volume of the associated contained region 3324 inj_confidence = None 3325 inj_confidence_area = None 3326 if inj_rho is not None: 3327 print 'calculating cl' 3328 acc_vol=0. 3329 for rho,confidence_level,vol,bounds in b: 3330 acc_vol+=vol 3331 if rho <= inj_rho: 3332 inj_confidence = confidence_level 3333 inj_confidence_area = acc_vol 3334 injInfo.append(inj_confidence) 3335 injInfo.append(inj_confidence_area) 3336 break 3337 3338 return confidence_intervals, b, initial_boundingbox,injInfo 3339
3340 -def kdtree_bin2Step(posterior,coord_names,confidence_levels,initial_boundingbox = None,samples_per_bin = 10,injCoords = None,alternate = False, fraction = 0.5, skyCoords=False):
3341 """ 3342 input: posterior class instance, list of confidence levels, optional choice of inital parameter space, samples per box in kdtree 3343 note initial_boundingbox is [[lowerbound of each param][upper bound of each param]], if not specified will just take limits of samples 3344 fraction is proportion of samples used for making the tree structure. 3345 returns: confidence_intervals, sorted list of kd objects, initial_boundingbox, injInfo 3346 where injInfo is [bounding box injection is found within, samples in said box, weighting of box (in case of repeated samples),inj_confidence, inj_confidence_area] 3347 """ 3348 confidence_levels.sort() 3349 3350 samples,header=posterior.samples() 3351 numberSamples = len(samples) 3352 if alternate == False: 3353 samplesStructure, samplesFill = random_split(samples,fraction) 3354 else: 3355 samplesStructure = samples[:int(numberSamples*fraction)] 3356 samplesFill = samples[int(numberSamples*fraction):] 3357 samplesFillLen = len(samplesFill) 3358 3359 header=header.split() 3360 coordinatized_samples=[PosteriorSample(row, header, coord_names) for row in samplesStructure] 3361 #if initial bounding box is not provided, create it using max/min of sample coords. 3362 if skyCoords == True: 3363 initial_boundingbox = [[0,-pi_constant/2.],[2*pi_constant,pi_constant/2.]] 3364 if initial_boundingbox is None: 3365 low=coordinatized_samples[0].coord() 3366 high=coordinatized_samples[0].coord() 3367 for obj in coordinatized_samples[1:]: 3368 low=np.minimum(low,obj.coord()) 3369 high=np.maximum(high,obj.coord()) 3370 initial_boundingbox = [low,high] 3371 tree=KDTreeVolume(coordinatized_samples,initial_boundingbox) 3372 tree2fill = tree.fillNewTree(boxing=samples_per_bin, isArea = skyCoords)#set isArea True if looking at sky coords(modifies stored volume values 3373 3374 columns = [] 3375 for name in coord_names: 3376 columns.append(header.index(name)) 3377 3378 for sample in samplesFill: 3379 tempSample=[] 3380 for column in columns: 3381 tempSample.append(sample[column]) 3382 addSample(tree2fill,tempSample) 3383 3384 def getValues(tree,listing): 3385 if tree._left is None: 3386 listing.append([tree.bounds(),tree._importance,tree._samples,tree._volume]) 3387 else: 3388 getValues(tree._left,listing) 3389 getValues(tree._right,listing)
3390 3391 listLeaves = [] 3392 getValues(tree2fill,listLeaves) 3393 3394 clSamples = [] 3395 for cl in confidence_levels: 3396 clSamples.append(samplesFillLen*cl) 3397 3398 sortedLeavesList = sorted(listLeaves, key=lambda importance: importance[1]) 3399 sortedLeavesList.reverse() 3400 runningTotalSamples = 0 3401 for i in range(len(sortedLeavesList)): 3402 runningTotalSamples += sortedLeavesList[i][2] 3403 sortedLeavesList[i].append(float(runningTotalSamples)/samplesFillLen,) 3404 3405 3406 level = 0 3407 countSamples = 0 3408 volume = 0 3409 lencl = len(clSamples) 3410 #finds confidence levels 3411 confidence_intervals={} 3412 interpConfAreas = {} 3413 countLeaves = 0 3414 for leaf in sortedLeavesList: 3415 countSamples += leaf[2] 3416 countLeaves += 1 3417 volume += leaf[3] 3418 if level < lencl and countSamples >= clSamples[level]: 3419 confidence_intervals[confidence_levels[level]]=(volume,float(countSamples)/samplesFillLen) 3420 interpConfAreas[confidence_levels[level]] = volume-leaf[3]*(countSamples-clSamples[level])/leaf[2] 3421 level +=1 3422 3423 if injCoords is not None: 3424 injBound,injNum,injImportance = tree2fill.search(injCoords) 3425 injInfo = [injBound,injNum,injImportance] 3426 else: 3427 injInfo = None 3428 3429 3430 #finds the confidence level of the injection and the volume of the associated contained region 3431 inj_confidence = None 3432 inj_confidence_area = None 3433 if injInfo is not None: 3434 acc_vol=0. 3435 acc_cl=0. 3436 for leaf in sortedLeavesList: 3437 acc_vol+=leaf[3] 3438 acc_cl+=leaf[2] 3439 if leaf[1] <= injImportance: 3440 inj_confidence = float(acc_cl)/samplesFillLen 3441 inj_confidence_area = acc_vol 3442 injInfo.append(inj_confidence) 3443 injInfo.append(inj_confidence_area) 3444 break 3445 3446 return sortedLeavesList, interpConfAreas, injInfo 3447 #check that there is an injection value for all dimension names 3448 def checkNone(listoParams): 3449 for param in listoParams: 3450 if posterior[param].injval is None: 3451 return False 3452 return True 3453
3454 -def greedy_bin_two_param(posterior,greedy2Params,confidence_levels):
3455 """ 3456 Determine the 2-parameter Bayesian Confidence Intervals using a greedy 3457 binning algorithm. 3458 3459 @param posterior: an instance of the Posterior class. 3460 3461 @param greedy2Params: a dict - {param1Name:param1binSize,param2Name:param2binSize} . 3462 3463 @param confidence_levels: A list of floats of the required confidence intervals [(0-1)]. 3464 """ 3465 3466 #Extract parameter names 3467 par1_name,par2_name=greedy2Params.keys() 3468 3469 #Set posterior array columns 3470 par1pos=posterior[par1_name.lower()].samples 3471 par2pos=posterior[par2_name.lower()].samples 3472 3473 #Extract bin sizes 3474 par1_bin=greedy2Params[par1_name] 3475 par2_bin=greedy2Params[par2_name] 3476 3477 #Extract injection information 3478 par1_injvalue=posterior[par1_name.lower()].injval 3479 par2_injvalue=posterior[par2_name.lower()].injval 3480 3481 #Create 2D bin array 3482 par1pos_min=min(par1pos)[0] 3483 par2pos_min=min(par2pos)[0] 3484 3485 par1pos_max=max(par1pos)[0] 3486 par2pos_max=max(par2pos)[0] 3487 3488 par1pos_Nbins= int(ceil((par1pos_max - par1pos_min)/par1_bin))+1 3489 3490 par2pos_Nbins= int(ceil((par2pos_max - par2pos_min)/par2_bin))+1 3491 3492 greedyHist = np.zeros(par1pos_Nbins*par2pos_Nbins,dtype='i8') 3493 greedyPoints = np.zeros((par1pos_Nbins*par2pos_Nbins,2)) 3494 3495 #Fill bin values 3496 par1_point=par1pos_min 3497 par2_point=par2pos_min 3498 for i in range(par2pos_Nbins): 3499 3500 par1_point=par1pos_min 3501 for j in range(par1pos_Nbins): 3502 3503 greedyPoints[j+par1pos_Nbins*i,0]=par1_point 3504 greedyPoints[j+par1pos_Nbins*i,1]=par2_point 3505 par1_point+=par1_bin 3506 par2_point+=par2_bin 3507 3508 3509 #If injection point given find which bin its in... 3510 injbin=None 3511 if par1_injvalue is not None and par2_injvalue is not None: 3512 3513 par1_binNumber=int(floor((par1_injvalue-par1pos_min)/par1_bin)) 3514 par2_binNumber=int(floor((par2_injvalue-par2pos_min)/par2_bin)) 3515 3516 injbin=int(par1_binNumber+par2_binNumber*par1pos_Nbins) 3517 elif par1_injvalue is None and par2_injvalue is not None: 3518 print "Injection value not found for %s!"%par1_name 3519 3520 elif par1_injvalue is not None and par2_injvalue is None: 3521 print "Injection value not found for %s!"%par2_name 3522 3523 #Bin posterior samples 3524 for par1_samp,par2_samp in zip(par1pos,par2pos): 3525 par1_samp=par1_samp[0] 3526 par2_samp=par2_samp[0] 3527 par1_binNumber=int(floor((par1_samp-par1pos_min)/par1_bin)) 3528 par2_binNumber=int(floor((par2_samp-par2pos_min)/par2_bin)) 3529 try: 3530 greedyHist[par1_binNumber+par2_binNumber*par1pos_Nbins]+=1 3531 except: 3532 raise RuntimeError("Problem binning samples: %i,%i,%i,%i,%i,%f,%f,%f,%f,%f,%f .")%(par1_binNumber,par2_binNumber,par1pos_Nbins,par2pos_Nbins,par1_binNumber+par2_binNumber*par1pos_Nbins,par1_samp,par1pos_min,par1_bin,par1_samp,par2pos_min,par2_bin) 3533 #Call greedy bins routine 3534 toppoints,injection_cl,reses,injection_area=\ 3535 _greedy_bin( 3536 greedyHist, 3537 greedyPoints, 3538 injbin, 3539 float(par1_bin*par2_bin), 3540 int(len(par1pos)), 3541 confidence_levels 3542 ) 3543 3544 return toppoints,injection_cl,reses,injection_area
3545
3546 -def pol2cart(long,lat):
3547 """ 3548 Utility function to convert longitude,latitude on a unit sphere to 3549 cartesian co-ordinates. 3550 """ 3551 3552 x=np.cos(lat)*np.cos(long) 3553 y=np.cos(lat)*np.sin(long) 3554 z=np.sin(lat) 3555 return np.array([x,y,z])
3556 #
3557 3558 -def sph2cart(r,theta,phi):
3559 """ 3560 Utiltiy function to convert r,theta,phi to cartesian co-ordinates. 3561 """ 3562 x = r*np.sin(theta)*np.cos(phi) 3563 y = r*np.sin(theta)*np.sin(phi) 3564 z = r*np.cos(theta) 3565 return x,y,z
3566
3567 3568 -def cart2sph(x,y,z):
3569 """ 3570 Utility function to convert cartesian coords to r,theta,phi. 3571 """ 3572 r = np.sqrt(x*x + y*y + z*z) 3573 theta = np.arccos(z/r) 3574 phi = np.fmod(2*pi_constant + np.arctan2(y,x), 2*pi_constant) 3575 3576 return r,theta,phi
3577
3578 3579 -def greedy_bin_sky(posterior,skyres,confidence_levels):
3580 """ 3581 Greedy bins the sky posterior samples into a grid on the sky constructed so that 3582 sky boxes have roughly equal size (determined by skyres). 3583 3584 @param posterior: Posterior class instance containing ra and dec samples. 3585 3586 @param skyres: Desired approximate size of sky pixel on one side. 3587 3588 @param confidence_levels: List of desired confidence levels [(0-1)]. 3589 """ 3590 3591 from pylal import skylocutils 3592 3593 np.seterr(under='ignore') 3594 3595 if 'ra' in posterior.names and 'dec' in posterior.names: 3596 skypos=np.column_stack([posterior['ra'].samples,posterior['dec'].samples]) 3597 raname='ra' 3598 decname='dec' 3599 elif 'rightascension' in posterior.names and 'declination' in posterior.names: 3600 skypos=np.column_stack([posterior['rightascension'].samples,posterior['declination'].samples]) 3601 raname='rightascension' 3602 decname='declination' 3603 else: 3604 raise RuntimeError('could not find ra and dec or rightascention and declination in column names for sky position') 3605 3606 injvalues=None 3607 3608 sky_injpoint=(posterior[raname].injval,posterior[decname].injval) 3609 3610 skypoints=np.array(skylocutils.gridsky(float(skyres))) 3611 skycarts=map(lambda s: pol2cart(s[1],s[0]),skypoints) 3612 skyinjectionconfidence=None 3613 3614 shist=_skyhist_cart(np.array(skycarts),skypos) 3615 3616 #shist=skyhist_cart(skycarts,list(pos)) 3617 bins=skycarts 3618 3619 # Find the bin of the injection if available 3620 injbin=None 3621 if None not in sky_injpoint: 3622 injhist=_skyhist_cart_slow(skycarts,np.array([sky_injpoint])) 3623 injbin=injhist.tolist().index(1) 3624 print 'Found injection in bin %d with co-ordinates %f,%f .'%( 3625 injbin, 3626 skypoints[injbin,0], 3627 skypoints[injbin,1] 3628 ) 3629 3630 return _greedy_bin(shist,skypoints,injbin,float(skyres)*float(skyres),len(skypos),confidence_levels)
3631
3632 -def plot_sky_map(hpmap, outdir, inj=None, nest=True):
3633 """Plots a sky map from a healpix map, optionally including an 3634 injected position. 3635 3636 :param hpmap: An array representing a healpix map (in nested 3637 ordering if ``nest = True``). 3638 3639 :param outdir: The output directory. 3640 3641 :param inj: If not ``None``, then ``[ra, dec]`` of the injection 3642 associated with the posterior map. 3643 3644 :param nest: Flag indicating the pixel ordering in healpix. 3645 3646 """ 3647 import lalinference.plot as lp 3648 3649 fig = plt.figure(frameon=False, figsize=(8,6)) 3650 ax = plt.subplot(111, projection='astro hours mollweide') 3651 ax.cla() 3652 ax.grid() 3653 lp.healpix_heatmap(hpmap, nest=nest, vmin=0.0, vmax=np.max(hpmap), cmap=plt.get_cmap('cylon')) 3654 3655 if inj is not None: 3656 ax.plot(inj[0], inj[1], '*', markerfacecolor='white', markeredgecolor='black', markersize=10) 3657 3658 plt.savefig(os.path.join(outdir, 'skymap.png')) 3659 3660 return fig
3661
3662 -def skymap_confidence_areas(hpmap, cls):
3663 """Returns the area (in square degrees) for each confidence level with 3664 a greedy binning algorithm for the given healpix map. 3665 3666 """ 3667 3668 hpmap = hpmap / np.sum(hpmap) # Normalise to sum to one. 3669 3670 hpmap = np.sort(hpmap)[::-1] # Sort from largest to smallest 3671 cum_hpmap = np.cumsum(hpmap) 3672 3673 pixarea = hp.nside2pixarea(hp.npix2nside(hpmap.shape[0])) 3674 pixarea = pixarea*(180.0/np.pi)**2 # In square degrees 3675 3676 areas = [] 3677 for cl in cls: 3678 npix = np.sum(cum_hpmap < cl) # How many pixels to sum before cl? 3679 areas.append(npix*pixarea) 3680 3681 return np.array(areas)
3682
3683 -def skymap_inj_pvalue(hpmap, inj, nest=True):
3684 """Returns the greedy p-value estimate for the given injection. 3685 3686 """ 3687 3688 nside = hp.npix2nside(hpmap.shape[0]) 3689 hpmap = hpmap / np.sum(hpmap) # Normalise to sum to one 3690 3691 injpix = hp.ang2pix(nside, np.pi/2.0-inj[1], inj[0], nest=nest) 3692 injvalue = hpmap[injpix] 3693 3694 return np.sum(hpmap[hpmap >= injvalue])
3695
3696 # 3697 3698 -def mc2ms(mc,eta):
3699 """ 3700 Utility function for converting mchirp,eta to component masses. The 3701 masses are defined so that m1>m2. The rvalue is a tuple (m1,m2). 3702 """ 3703 root = np.sqrt(0.25-eta) 3704 fraction = (0.5+root) / (0.5-root) 3705 invfraction = 1/fraction 3706 3707 m2= mc * np.power((1+fraction),0.2) / np.power(fraction,0.6) 3708 3709 m1= mc* np.power(1+invfraction,0.2) / np.power(invfraction,0.6) 3710 return (m1,m2)
3711 #
3712 # 3713 3714 -def q2ms(mc,q):
3715 """ 3716 Utility function for converting mchirp,q to component masses. The 3717 masses are defined so that m1>m2. The rvalue is a tuple (m1,m2). 3718 """ 3719 factor = mc * np.power(1+q, 1.0/5.0); 3720 m1 = factor * np.power(q, -3.0/5.0); 3721 m2 = factor * np.power(q, 2.0/5.0); 3722 return (m1,m2)
3723 #
3724 # 3725 3726 -def q2eta(mc,q):
3727 """ 3728 Utility function for converting mchirp,q to eta. The 3729 rvalue is eta. 3730 """ 3731 m1,m2 = q2ms(mc,q) 3732 eta = m1*m2/( (m1+m2)*(m1+m2) ) 3733 return eta
3734 #
3735 # 3736 3737 -def mc2q(mc,eta):
3738 """ 3739 Utility function for converting mchirp,eta to new mass ratio q (m2/m1). 3740 """ 3741 m1,m2 = mc2ms(mc,eta) 3742 q = m2/m1 3743 return q
3744 #
3745 # 3746 3747 -def ang_dist(long1,lat1,long2,lat2):
3748 """ 3749 Find the angular separation of (long1,lat1) and (long2,lat2), which are 3750 specified in radians. 3751 """ 3752 3753 x1=np.cos(lat1)*np.cos(long1) 3754 y1=np.cos(lat1)*np.sin(long1) 3755 z1=np.sin(lat1) 3756 x2=np.cos(lat2)*np.cos(long2) 3757 y2=np.cos(lat2)*np.sin(long2) 3758 z2=np.sin(lat2) 3759 sep=math.acos(x1*x2+y1*y2+z1*z2) 3760 return(sep)
3761 #
3762 # 3763 3764 -def array_dot(vec1, vec2):
3765 """ 3766 Calculate dot products between vectors in rows of numpy arrays. 3767 """ 3768 if vec1.ndim==1: 3769 product = (vec1*vec2).sum() 3770 else: 3771 product = (vec1*vec2).sum(axis=1).reshape(-1,1) 3772 return product
3773 #
3774 # 3775 3776 -def array_ang_sep(vec1, vec2):
3777 """ 3778 Find angles between vectors in rows of numpy arrays. 3779 """ 3780 vec1_mag = np.sqrt(array_dot(vec1, vec1)) 3781 vec2_mag = np.sqrt(array_dot(vec2, vec2)) 3782 return np.arccos(array_dot(vec1, vec2)/(vec1_mag*vec2_mag))
3783 #
3784 # 3785 3786 -def array_polar_ang(vec):
3787 """ 3788 Find polar angles of vectors in rows of a numpy array. 3789 """ 3790 if vec.ndim==1: 3791 z = vec[2] 3792 else: 3793 z = vec[:,2].reshape(-1,1) 3794 norm = np.sqrt(array_dot(vec,vec)) 3795 return np.arccos(z/norm)
3796 #
3797 # 3798 3799 -def rotation_matrix(angle, direction):
3800 """ 3801 Compute general rotation matrices for a given angles and direction vectors. 3802 """ 3803 cosa = np.cos(angle) 3804 sina = np.sin(angle) 3805 direction /= np.sqrt(array_dot(direction,direction)) 3806 #Assume calculating array of rotation matrices. 3807 try: 3808 nSamps = len(angle) 3809 R = np.array( [np.diag([i,i,i]) for i in cosa.flat] ) 3810 R += np.array( [np.outer(direction[i],direction[i])*(1.0-cosa[i]) for i in range(nSamps)] ) 3811 R += np.array( [np.array( [[ 0.0, -direction[i,2], direction[i,1]], 3812 [ direction[i,2], 0.0, -direction[i,0]], 3813 [-direction[i,1], direction[i,0], 0.0 ]] ) * sina[i] for i in range(nSamps)] ) 3814 #Only computing one rotation matrix. 3815 except TypeError: 3816 R = np.diag([cosa,cosa,cosa]) 3817 R += np.outer(direction,direction) * (1.0 - cosa) 3818 R += np.array( [[ 0.0, -direction[2], direction[1]], 3819 [ direction[2], 0.0, -direction[0]], 3820 [-direction[1], direction[0], 0.0 ]] ) * sina 3821 return R
3822 #
3823 # 3824 3825 -def rotate_vector(R, vec):
3826 """ 3827 Rotate vectors using the given rotation matrices. 3828 """ 3829 if vec.ndim == 1: 3830 newVec = np.dot(R[i],vec[i]) 3831 else: 3832 newVec = np.array( [np.dot(R[i],vec[i]) for i in range(len(vec))] ) 3833 return newVec
3834 #
3835 # 3836 -def ROTATEZ(angle, vx, vy, vz):
3837 # This is the ROTATEZ in LALSimInspiral.c. 3838 tmp1 = vx*np.cos(angle) - vy*np.sin(angle); 3839 tmp2 = vx*np.sin(angle) + vy*np.cos(angle); 3840 return np.asarray([tmp1,tmp2,vz])
3841
3842 -def ROTATEY(angle, vx, vy, vz):
3843 # This is the ROTATEY in LALSimInspiral.c 3844 tmp1 = vx*np.cos(angle) + vz*np.sin(angle); 3845 tmp2 = - vx*np.sin(angle) + vz*np.cos(angle); 3846 return np.asarray([tmp1,vy,tmp2])
3847
3848 -def orbital_momentum(fref, m1,m2, inclination):
3849 """ 3850 Calculate orbital angular momentum vector. 3851 Note: The units of Lmag are different than what used in lalsimulation. 3852 Mc must be called in units of Msun here. 3853 3854 Note that if one wants to build J=L+S1+S2 with L returned by this function, S1 and S2 3855 must not get the Msun^2 factor. 3856 """ 3857 eta = m1*m2/( (m1+m2)*(m1+m2) ) 3858 Lmag = orbital_momentum_mag(fref, m1,m2,eta) 3859 Lx, Ly, Lz = sph2cart(Lmag, inclination, 0.0) 3860 return np.hstack((Lx,Ly,Lz))
3861 #
3862 # 3863 -def orbital_momentum_mag(fref, m1,m2,eta):
3864 v0 = np.power((m1+m2) *pi_constant * lal.MTSUN_SI * fref, 1.0/3.0) 3865 #1 PN Mtot*Mtot*eta/v 3866 PNFirst = (((m1+m2)**2)*eta)/v0 3867 PNSecond = 1+ (v0**2) * (3.0/2.0 +eta/6.0) 3868 Lmag= PNFirst*PNSecond 3869 return Lmag
3870
3871 -def component_momentum(m, a, theta, phi):
3872 """ 3873 Calculate BH angular momentum vector. 3874 """ 3875 Sx, Sy, Sz = sph2cart(m**2 * a, theta, phi) 3876 return np.hstack((Sx,Sy,Sz))
3877 #
3878 # 3879 3880 -def symm_tidal_params(lambda1,lambda2,eta):
3881 """ 3882 Calculate best tidal parameters 3883 """ 3884 lam_tilde = (8./13.)*((1.+7.*eta-31.*eta*eta)*(lambda1+lambda2) + np.sqrt(1.-4.*eta)*(1.+9.*eta-11.*eta*eta)*(lambda1-lambda2)) 3885 dlam_tilde = (1./2.)*(np.sqrt(1.-4.*eta)*(1.-13272.*eta/1319.+8944.*eta*eta/1319.)*(lambda1+lambda2) + (1.-15910.*eta/1319.+32850.*eta*eta/1319.+3380.*eta*eta*eta/1319.)*(lambda1-lambda2)) 3886 return lam_tilde, dlam_tilde
3887
3888 -def spin_angles(fref,mc,eta,incl,a1,theta1,phi1,a2=None,theta2=None,phi2=None):
3889 """ 3890 Calculate physical spin angles. 3891 """ 3892 singleSpin = None in (a2,theta2,phi2) 3893 m1, m2 = mc2ms(mc,eta) 3894 L = orbital_momentum(fref, m1,m2, incl) 3895 S1 = component_momentum(m1, a1, theta1, phi1) 3896 if not singleSpin: 3897 S2 = component_momentum(m2, a2, theta2, phi2) 3898 else: 3899 S2 = 0.0 3900 J = L + S1 + S2 3901 tilt1 = array_ang_sep(L,S1) 3902 if not singleSpin: 3903 tilt2 = array_ang_sep(L,S2) 3904 else: 3905 tilt2 = None 3906 theta_jn = array_polar_ang(J) 3907 beta = array_ang_sep(J,L) 3908 return tilt1, tilt2, theta_jn, beta
3909 #
3910 -def chi_precessing(m1, a1, tilt1, m2, a2, tilt2):
3911 """ 3912 Calculate the magnitude of the effective precessing spin 3913 following convention from Phys. Rev. D 91, 024043 -- arXiv:1408.1810 3914 note: the paper uses naming convention where m1 < m2 3915 (and similar for associated spin parameters) and q > 1 3916 """ 3917 q_inv = m1/m2 3918 A1 = 2. + (3.*q_inv/2.) 3919 A2 = 2. + 3./(2.*q_inv) 3920 S1_perp = a1*np.sin(tilt1)*m1*m1 3921 S2_perp = a2*np.sin(tilt2)*m2*m2 3922 Sp = np.maximum(A1*S2_perp, A2*S1_perp) 3923 chi_p = Sp/(A2*m1*m1) 3924 return chi_p
3925
3926 -def calculate_redshift(distance,h=0.6790,om=0.3065,ol=0.6935,w0=-1.0):
3927 """ 3928 Calculate the redshift from the luminosity distance measurement using the 3929 Cosmology Calculator provided in LAL. 3930 By default assuming cosmological parameters from arXiv:1502.01589 - 'Planck 2015 results. XIII. Cosmological parameters' 3931 Using parameters from table 4, column 'TT+lowP+lensing+ext' 3932 This corresponds to Omega_M = 0.3065, Omega_Lambda = 0.6935, H_0 = 67.90 km s^-1 Mpc^-1 3933 Returns an array of redshifts 3934 """ 3935 def find_z_root(z,dl,omega): 3936 return dl - lal.LuminosityDistance(omega,z)
3937 3938 omega = lal.CreateCosmologicalParameters(h,om,ol,w0,0.0,0.0) 3939 if isinstance(distance,float): 3940 z = np.array([newton(find_z_root,np.random.uniform(0.0,2.0),args = (distance,omega))]) 3941 else: 3942 z = np.array([newton(find_z_root,np.random.uniform(0.0,2.0),args = (d,omega)) for d in distance[:,0]]) 3943 return z.reshape(z.shape[0],1) 3944
3945 -def source_mass(mass, redshift):
3946 """ 3947 Calculate source mass parameter for mass m as: 3948 m_source = m / (1.0 + z) 3949 For a parameter m. 3950 """ 3951 return mass / (1.0 + redshift)
3952
3953 -def physical2radiationFrame(theta_jn, phi_jl, tilt1, tilt2, phi12, a1, a2, m1, m2, fref):
3954 """ 3955 Wrapper function for SimInspiralTransformPrecessingNewInitialConditions(). 3956 Vectorizes function for use in append_mapping() methods of the posterior class. 3957 """ 3958 try: 3959 import lalsimulation as lalsim 3960 except ImportError: 3961 print 'bayespputils.py: Cannot import lalsimulation SWIG bindings to calculate physical to radiation' 3962 print 'frame angles, did you remember to use --enable-swig-python when ./configuring lalsimulation?' 3963 return None 3964 from numpy import shape 3965 transformFunc = lalsim.SimInspiralTransformPrecessingNewInitialConditions 3966 3967 # Convert component masses to SI units 3968 m1_SI = m1*lal.MSUN_SI 3969 m2_SI = m2*lal.MSUN_SI 3970 3971 # Flatten arrays 3972 ins = [theta_jn, phi_jl, tilt1, tilt2, phi12, a1, a2, m1_SI, m2_SI, fref] 3973 if len(shape(ins))>1: 3974 # ins is a list of lists (i.e. we are converting full posterior chains) 3975 try: 3976 for p,param in enumerate(ins): 3977 ins[p] = param.flatten() 3978 except: 3979 pass 3980 3981 try: 3982 results = np.array([transformFunc(t_jn, p_jl, t1, t2, p12, a1, a2, m1_SI, m2_SI, f) for (t_jn, p_jl, t1, t2, p12, a1, a2, m1_SI, m2_SI, f) in zip(*ins)]) 3983 iota = results[:,0].reshape(-1,1) 3984 spin1x = results[:,1].reshape(-1,1) 3985 spin1y = results[:,2].reshape(-1,1) 3986 spin1z = results[:,3].reshape(-1,1) 3987 spin2x = results[:,4].reshape(-1,1) 3988 spin2y = results[:,5].reshape(-1,1) 3989 spin2z = results[:,6].reshape(-1,1) 3990 a1,theta1,phi1 = cart2sph(spin1x,spin1y,spin1z) 3991 a2,theta2,phi2 = cart2sph(spin2x,spin2y,spin2z) 3992 3993 mc = np.power(m1*m2,3./5.)*np.power(m1+m2,-1./5.) 3994 L = orbital_momentum(fref, m1,m2, iota) 3995 S1 = np.hstack([m1*m1*spin1x,m1*m1*spin1y,m1*m1*spin1z]) 3996 S2 = np.hstack([m2*m2*spin2x,m2*m2*spin2y,m2*m2*spin2z]) 3997 J = L + S1 + S2 3998 beta = array_ang_sep(J,L) 3999 4000 return iota, theta1, phi1, theta2, phi2, beta 4001 except: # Catch all exceptions, including failure for the transformFunc 4002 # Something went wrong, returning None 4003 return None 4004 4005 elif len(shape(ins))<=1: 4006 # ins is a list of floats (i.e. we are converting the injected values) or empty 4007 try: 4008 for p,param in enumerate(ins): 4009 ins[p] = param 4010 except: 4011 pass 4012 4013 try: 4014 results = np.array(transformFunc(theta_jn, phi_jl, tilt1, tilt2, phi12, a1, a2, m1_SI, m2_SI, fref)) 4015 iota = results[0] 4016 spin1x = results[1] 4017 spin1y = results[2] 4018 spin1z = results[3] 4019 spin2x = results[4] 4020 spin2y = results[5] 4021 spin2z = results[6] 4022 a1,theta1,phi1 = cart2sph(spin1x,spin1y,spin1z) 4023 a2,theta2,phi2 = cart2sph(spin2x,spin2y,spin2z) 4024 4025 mc = np.power(m1*m2,3./5.)*np.power(m1+m2,-1./5.) 4026 L = orbital_momentum(fref, m1,m2, iota) 4027 S1 = m1*m1*np.hstack([spin1x,spin1y,spin1z]) 4028 S2 = m2*m2*np.hstack([spin2x,spin2y,spin2z]) 4029 J = L + S1 + S2 4030 beta = array_ang_sep(J,L) 4031 4032 return iota, theta1, phi1, theta2, phi2, beta 4033 4034 except: # Catch all exceptions, including failure for the transformFunc 4035 # Something went wrong, returning None 4036 return None
4037 #
4038 # 4039 4040 -def plot_one_param_pdf_kde(fig,onedpos):
4041 4042 from scipy import seterr as sp_seterr 4043 4044 np.seterr(under='ignore') 4045 sp_seterr(under='ignore') 4046 pos_samps=onedpos.samples 4047 try: 4048 gkde=onedpos.gaussian_kde 4049 except np.linalg.linalg.LinAlgError: 4050 print 'Singular matrix in KDE. Skipping' 4051 else: 4052 ind=np.linspace(np.min(pos_samps),np.max(pos_samps),101) 4053 kdepdf=gkde.evaluate(ind) 4054 plt.plot(ind,kdepdf,color='green') 4055 return
4056
4057 -def plot_one_param_pdf_line_hist(fig,pos_samps):
4058 plt.hist(pos_samps,kdepdf)
4059
4060 4061 -def plot_one_param_pdf(posterior,plot1DParams,analyticPDF=None,analyticCDF=None,plotkde=False):
4062 """ 4063 Plots a 1D histogram and (gaussian) kernel density estimate of the 4064 distribution of posterior samples for a given parameter. 4065 4066 @param posterior: an instance of the Posterior class. 4067 4068 @param plot1DParams: a dict; {paramName:Nbins} 4069 4070 @param analyticPDF: an analytic probability distribution function describing the distribution. 4071 4072 @param analyticCDF: an analytic cumulative distribution function describing the distribution. 4073 4074 """ 4075 4076 # matplotlib.rcParams['text.usetex']=True 4077 4078 param=plot1DParams.keys()[0].lower() 4079 histbins=plot1DParams.values()[0] 4080 4081 pos_samps=posterior[param].samples 4082 injpar=posterior[param].injval 4083 trigvals=posterior[param].trigvals 4084 4085 #myfig=plt.figure(figsize=(4,3.5),dpi=200) 4086 myfig=plt.figure(figsize=(6,4.5),dpi=150) 4087 #axes=plt.Axes(myfig,[0.2, 0.2, 0.7,0.7]) 4088 axes=plt.Axes(myfig,[0.15,0.15,0.6,0.76]) 4089 myfig.add_axes(axes) 4090 majorFormatterX=ScalarFormatter(useMathText=True) 4091 majorFormatterX.format_data=lambda data:'%.6g'%(data) 4092 majorFormatterY=ScalarFormatter(useMathText=True) 4093 majorFormatterY.format_data=lambda data:'%.6g'%(data) 4094 majorFormatterX.set_scientific(True) 4095 majorFormatterY.set_scientific(True) 4096 offset=0.0 4097 if param.find('time')!=-1: 4098 offset=floor(min(pos_samps)) 4099 pos_samps=pos_samps-offset 4100 if injpar is not None: 4101 injpar=injpar-offset 4102 ax1_name=param+' + %i'%(int(offset)) 4103 else: ax1_name=param 4104 4105 (n, bins, patches)=plt.hist(pos_samps,histbins,normed='true',facecolor='grey') 4106 Nchars=max(map(lambda d:len(majorFormatterX.format_data(d)),axes.get_xticks())) 4107 if Nchars>8: 4108 Nticks=3 4109 elif Nchars>5: 4110 Nticks=4 4111 elif Nchars>4: 4112 Nticks=6 4113 else: 4114 Nticks=6 4115 locatorX=matplotlib.ticker.MaxNLocator(nbins=Nticks) 4116 xmin,xmax=plt.xlim() 4117 if param=='rightascension' or param=='ra': 4118 locatorX=RALocator(min=xmin,max=xmax) 4119 majorFormatterX=RAFormatter() 4120 if param=='declination' or param=='dec': 4121 locatorX=DecLocator(min=xmin,max=xmax) 4122 majorFormatterX=DecFormatter() 4123 axes.xaxis.set_major_formatter(majorFormatterX) 4124 axes.yaxis.set_major_formatter(majorFormatterY) 4125 4126 locatorX.view_limits(bins[0],bins[-1]) 4127 axes.xaxis.set_major_locator(locatorX) 4128 if plotkde: plot_one_param_pdf_kde(myfig,posterior[param]) 4129 histbinSize=bins[1]-bins[0] 4130 if analyticPDF: 4131 (xmin,xmax)=plt.xlim() 4132 x = np.linspace(xmin,xmax,2*len(bins)) 4133 plt.plot(x, analyticPDF(x+offset), color='r', linewidth=2, linestyle='dashed') 4134 if analyticCDF: 4135 # KS underflows with too many samples 4136 max_samps=1000 4137 from numpy.random import permutation 4138 samps = permutation(pos_samps)[:max_samps,:].flatten() 4139 D,p = stats.kstest(samps+offset, analyticCDF, mode='asymp') 4140 plt.title("%s: ks p-value %.3f"%(param,p)) 4141 4142 rbins=None 4143 4144 if injpar is not None: 4145 # We will plot the injection if it is <5% outside the posterior 4146 delta_samps=max(pos_samps)-min(pos_samps) 4147 minrange=min(pos_samps)-0.05*delta_samps 4148 maxrange=max(pos_samps)+0.05*delta_samps 4149 if minrange<injpar and maxrange>injpar: 4150 4151 plt.axvline(injpar, color='r', linestyle='-.', linewidth=4) 4152 4153 #rkde=gkde.integrate_box_1d(min(pos[:,i]),getinjpar(injection,i)) 4154 #print "r of injected value of %s (kde) = %f"%(param,rkde) 4155 4156 #Find which bin the true value is in 4157 if min(pos_samps)<injpar and max(pos_samps)>injpar: 4158 bins_to_inj=(injpar-bins[0])/histbinSize 4159 injbinh=int(floor(bins_to_inj)) 4160 injbin_frac=bins_to_inj-float(injbinh) 4161 4162 #Integrate over the bins 4163 rbins=(sum(n[0:injbinh-1])+injbin_frac*n[injbinh])*histbinSize 4164 4165 if trigvals is not None: 4166 for IFO in [IFO for IFO in trigvals.keys()]: 4167 trigval = trigvals[IFO] 4168 if min(pos_samps)<trigval and max(pos_samps)>trigval: 4169 if IFO=='H1': color = 'r' 4170 elif IFO=='L1': color = 'g' 4171 elif IFO=='V1': color = 'm' 4172 else: color = 'c' 4173 plt.axvline(trigval, color=color, linestyle='-.') 4174 # 4175 plt.grid() 4176 plt.xlabel(plot_label(ax1_name)) 4177 plt.ylabel('Probability Density') 4178 4179 # For RA and dec set custom labels and for RA reverse 4180 if(param.lower()=='ra' or param.lower()=='rightascension'): 4181 xmin,xmax=plt.xlim() 4182 plt.xlim(xmax,xmin) 4183 #if(param.lower()=='ra' or param.lower()=='rightascension'): 4184 # locs, ticks = plt.xticks() 4185 # newlocs, newticks = formatRATicks(locs) 4186 # plt.xticks(newlocs,newticks,rotation=45) 4187 #if(param.lower()=='dec' or param.lower()=='declination'): 4188 # locs, ticks = plt.xticks() 4189 # newlocs, newticks = formatDecTicks(locs) 4190 # plt.xticks(newlocs,newticks,rotation=45) 4191 4192 return rbins,myfig#,rkde
4193 #
4194 4195 -class RALocator(matplotlib.ticker.MultipleLocator):
4196 """ 4197 RA tick locations with some intelligence 4198 """
4199 - def __init__(self,min=0.0,max=2.0*pi_constant):
4200 hour=pi_constant/12.0 4201 if(max-min)>12.0*hour: 4202 base=3.0*hour 4203 elif(max-min)>6.0*hour: 4204 base=2.0*hour 4205 # Put hour ticks if there are more than 3 hours displayed 4206 elif (max-min)>3.0*pi_constant/12.0: 4207 base=hour 4208 elif (max-min)>hour: 4209 base=hour/2.0 4210 else: 4211 base=hour/4.0 4212 4213 matplotlib.ticker.MultipleLocator.__init__(self,base=base)
4214
4215 -class DecLocator(matplotlib.ticker.MultipleLocator):
4216 """ 4217 Dec tick locations with some intelligence 4218 """
4219 - def __init__(self, min=-pi_constant/2.0,max=pi_constant/2.0):
4220 deg=pi_constant/180.0 4221 if (max-min)>60*deg: 4222 base=30.0*deg 4223 elif (max-min)>20*deg: 4224 base=10*deg 4225 elif (max-min)>10*deg: 4226 base=5*deg 4227 else: 4228 base=deg 4229 matplotlib.ticker.MultipleLocator.__init__(self,base=base)
4230
4231 -class RAFormatter(matplotlib.ticker.FuncFormatter):
4232 - def __init__(self,accuracy='auto'):
4233 matplotlib.ticker.FuncFormatter.__init__(self,getRAString)
4234
4235 -class DecFormatter(matplotlib.ticker.FuncFormatter):
4236 - def __init__(self,accuracy='auto'):
4237 matplotlib.ticker.FuncFormatter.__init__(self,getDecString)
4238
4239 -def formatRATicks(locs, accuracy='auto'):
4240 """ 4241 Format locs, ticks to RA angle with given accuracy 4242 accuracy can be 'hour', 'min', 'sec', 'all' 4243 returns (locs, ticks) 4244 'all' does no rounding, just formats the tick strings 4245 'auto' will use smallest appropriate units 4246 """ 4247 newmax=max(locs) 4248 newmin=min(locs) 4249 if(accuracy=='auto'): 4250 acc='hour' 4251 if abs(newmax-newmin)<pi_constant/12.: 4252 acc='min' 4253 if abs(newmax-newmin)<pi_constant/(12.*60.): 4254 acc='sec' 4255 else: 4256 acc=accuracy 4257 4258 if max(locs)>2*pi_constant: newmax=2.0*pi_constant 4259 if min(locs)<0.0: newmin=0.0 4260 locs=linspace(newmin,newmax,len(locs)) 4261 4262 roundlocs=map(lambda a: roundRadAngle(a, accuracy=acc), locs) 4263 4264 newlocs=filter(lambda a:a>=0 and a<=2.0*pi_constant, roundlocs) 4265 return (list(newlocs), map(getRAString, list(newlocs) ) )
4266
4267 -def formatDecTicks(locs, accuracy='auto'):
4268 """ 4269 Format locs to Dec angle with given accuracy 4270 accuracy can be 'deg', 'arcmin', 'arcsec', 'all' 4271 'all' does no rounding, just formats the tick strings 4272 """ 4273 newmax=max(locs) 4274 newmin=min(locs) 4275 if(accuracy=='auto'): 4276 acc='deg' 4277 if abs(newmax-newmin)<pi_constant/360.: 4278 acc='arcmin' 4279 if abs(newmax-newmin)<pi_constant/(360.*60.): 4280 acc='arcsec' 4281 else: 4282 acc=accuracy 4283 if newmax>0.5*pi_constant: newmax=0.5*pi_constant 4284 if newmin<-0.5*pi_constant: newmin=-0.5*pi_constant 4285 locs=linspace(newmin,newmax,len(locs)) 4286 4287 roundlocs=map(lambda a: roundRadAngle(a, accuracy=acc), locs) 4288 newlocs=filter(lambda a:a>=-pi_constant/2.0 and a<=pi_constant/2.0, roundlocs) 4289 return (list(newlocs), map(getDecString, list(newlocs) ) )
4290
4291 -def roundRadAngle(rads,accuracy='all'):
4292 """ 4293 round given angle in radians to integer hours, degrees, mins or secs 4294 accuracy can be 'hour'. 'deg', 'min', 'sec', 'all', all does nothing 4295 'arcmin', 'arcsec' 4296 """ 4297 if accuracy=='all': return locs 4298 if accuracy=='hour': mult=24 4299 if accuracy=='deg': mult=360 4300 if accuracy=='min': mult=24*60 4301 if accuracy=='sec': mult=24*60*60 4302 if accuracy=='arcmin': mult=360*60 4303 if accuracy=='arcsec': mult=360*60*60 4304 mult=mult/(2.0*pi_constant) 4305 return round(rads*mult)/mult
4306
4307 -def getRAString(radians,accuracy='auto'):
4308 secs=radians*12.0*3600/pi_constant 4309 hours, rem = divmod(secs, 3600 ) 4310 mins,rem = divmod(rem, 60 ) 4311 secs = rem 4312 if secs>=59.5: 4313 secs=secs-60 4314 mins=mins+1 4315 if mins>=59.5: 4316 mins=mins-60 4317 hours=hours+1 4318 if accuracy=='hour': return ur'%ih'%(hours) 4319 if accuracy=='min': return ur'%ih%im'%(hours,mins) 4320 if accuracy=='sec': return ur'%ih%im%2.0fs'%(hours,mins,secs) 4321 else: 4322 if abs(fmod(secs,60.0))>=0.5: return(getRAString(radians,accuracy='sec')) 4323 if abs(fmod(mins,60.0))>=0.5: return(getRAString(radians,accuracy='min')) 4324 else: return(getRAString(radians,accuracy='hour'))
4325
4326 -def getDecString(radians,accuracy='auto'):
4327 # LaTeX doesn't like unicode degree symbols etc 4328 if matplotlib.rcParams['text.usetex']: 4329 degsymb='$^\circ$' 4330 minsymb="'" 4331 secsymb="''" 4332 else: 4333 degsymb=u'\u00B0' 4334 minsymb=u'\u0027' 4335 secsymb=u'\u2033' 4336 if(radians<0): 4337 radians=-radians 4338 sign=-1 4339 else: sign=+1 4340 deg,rem=divmod(radians,pi_constant/180.0) 4341 mins, rem = divmod(rem, pi_constant/(180.0*60.0)) 4342 secs = rem * (180.0*3600.0)/pi_constant 4343 #if secs>=59.5: 4344 # secs=secs-60.0 4345 # mins=mins+1 4346 #if mins>=59.5: 4347 # mins=mins-60.0 4348 # deg=deg+1 4349 if (accuracy=='arcmin' or accuracy=='deg') and secs>30: mins=mins+1 4350 if accuracy=='deg' and mins>30: deg=deg+1 4351 if accuracy=='deg': return ur'%i'%(sign*deg)+degsymb 4352 if accuracy=='arcmin': return ur'%i%s%i%s'%(sign*deg,degsymb,mins,minsymb) 4353 if accuracy=='arcsec': return ur'%i%s%i%s%2.0f%s'%(sign*deg,degsymb,mins,minsymb,secs,secsymb) 4354 else: 4355 # if abs(fmod(secs,60.0))>=0.5 and abs(fmod(secs,60)-60)>=0.5 : return(getDecString(sign*radians,accuracy='arcsec')) 4356 # if abs(fmod(mins,60.0))>=0.5 and abs(fmod(mins,60)-60)>=0.5: return(getDecString(sign*radians,accuracy='arcmin')) 4357 # else: return(getDecString(sign*radians,accuracy='deg')) 4358 return(getDecString(sign*radians,accuracy='deg'))
4359
4360 -def plot_corner(posterior,levels,parnames=None):
4361 """ 4362 Make a corner plot using the triangle module 4363 (See http://github.com/dfm/corner.py) 4364 @param posterior: The Posterior object 4365 @param levels: a list of confidence levels 4366 @param parnames: list of parameters to include 4367 """ 4368 try: 4369 import corner 4370 except ImportError: 4371 try: 4372 import triangle as corner 4373 except ImportError: 4374 print 'Cannot load corner module. Try running\n\t$ pip install corner' 4375 return None 4376 parnames=filter(lambda x: x in posterior.names, parnames) 4377 labels = [plot_label(parname) for parname in parnames] 4378 data = np.hstack([posterior[p].samples for p in parnames]) 4379 if posterior.injection: 4380 injvals=[posterior[p].injval for p in parnames] 4381 myfig=corner.corner(data,labels=labels,truths=injvals,quantiles=levels,plot_datapoints=False,bins=20) 4382 else: 4383 myfig=corner.corner(data,labels=labels,quantiles=levels,plot_datapoints=False,bins=20) 4384 return(myfig)
4385
4386 4387 -def plot_two_param_kde_greedy_levels(posteriors_by_name,plot2DkdeParams,levels,colors_by_name,line_styles=__default_line_styles,figsize=(4,3),dpi=250,figposition=[0.2,0.2,0.48,0.75],legend='right',hatches_by_name=None,Npixels=50):
4388 """ 4389 Plots a 2D kernel density estimate of the 2-parameter marginal posterior. 4390 4391 @param posterior: an instance of the Posterior class. 4392 4393 @param plot2DkdeParams: a dict {param1Name:Nparam1Bins,param2Name:Nparam2Bins} 4394 """ 4395 4396 from scipy import seterr as sp_seterr 4397 confidence_levels=levels 4398 4399 # Reversed parameter order here for consistency with the other 4400 # plotting functions 4401 par2_name,par1_name=plot2DkdeParams.keys() 4402 xbin=plot2DkdeParams[par1_name] 4403 ybin=plot2DkdeParams[par2_name] 4404 levels= levels 4405 np.seterr(under='ignore') 4406 sp_seterr(under='ignore') 4407 4408 fig=plt.figure(1,figsize=figsize,dpi=dpi) 4409 plt.clf() 4410 axes=fig.add_axes(figposition) 4411 name_list=[] 4412 4413 #This fixes the precedence of line styles in the plot 4414 if len(line_styles)<len(levels): 4415 raise RuntimeError("Error: Need as many or more line styles to choose from as confidence levels to plot!") 4416 4417 CSlst=[] 4418 for name,posterior in posteriors_by_name.items(): 4419 print 'Plotting '+name 4420 name_list.append(name) 4421 par1_injvalue=posterior[par1_name].injval 4422 par2_injvalue=posterior[par2_name].injval 4423 4424 par_trigvalues1=posterior[par1_name].trigvals 4425 par_trigvalues2=posterior[par2_name].trigvals 4426 xdat=posterior[par1_name].samples 4427 ydat=posterior[par2_name].samples 4428 a=np.squeeze(posterior[par1_name].samples) 4429 b=np.squeeze(posterior[par2_name].samples) 4430 offset=0.0 4431 if par1_name.find('time')!=-1: 4432 offset=floor(min(a)) 4433 a=a-offset 4434 if par1_injvalue: 4435 par1_injvalue=par1_injvalue-offset 4436 ax1_name=par1_name+' + %i'%(int(offset)) 4437 else: ax1_name=par1_name 4438 4439 if par2_name.find('time')!=-1: 4440 offset=floor(min(b)) 4441 b=b-offset 4442 if par2_injvalue: 4443 par2_injvalue=par2_injvalue-offset 4444 ax2_name=par2_name+' + %i'%(int(offset)) 4445 else: ax2_name=par2_name 4446 4447 samp=np.transpose(np.column_stack((xdat,ydat))) 4448 4449 try: 4450 kde=stats.kde.gaussian_kde(samp) 4451 den=kde(samp) 4452 except: 4453 return None 4454 4455 #grid_coords = np.append(x.reshape(-1,1),y.reshape(-1,1),axis=1) 4456 Nx=Npixels 4457 Ny=Npixels 4458 # Ugly hack to produce plots centred on the main mode: 4459 # Choose 1%-99% percentile range of the samples 4460 # Then zoom out by 0.95 to encompass the contour edges 4461 xsorted=np.sort(xdat,axis=None) 4462 ysorted=np.sort(ydat,axis=None) 4463 imin=int(0.01*len(xsorted)) 4464 imax=int(0.99*len(xsorted)) 4465 xmax=xsorted[imax] 4466 xmin=xsorted[imin] 4467 ymax=ysorted[imax] 4468 ymin=ysorted[imin] 4469 xmid=0.5*(xmin+xmax) 4470 ymid=0.5*(ymin+ymax) 4471 xmin=xmid+(xmin-xmid)/0.95 4472 xmax=xmid+(xmax-xmid)/0.95 4473 ymin=ymid+(ymin-ymid)/0.95 4474 ymax=ymid+(ymax-ymid)/0.95 4475 xax=np.linspace(xmin,xmax,Nx) 4476 yax=np.linspace(ymin,ymax,Ny) 4477 #print 'Plot limits %f-%f x %f-%f'%(xmin,xmax,ymin,ymax) 4478 4479 # Original way which includes all samples 4480 #xax = np.linspace(np.min(xdat),np.max(xdat),Nx) 4481 #yax = np.linspace(np.min(ydat),np.max(ydat),Ny) 4482 4483 # Do the actual plotting 4484 x,y = np.meshgrid(xax,yax) 4485 grid_coords = np.row_stack( (x.flatten(),y.flatten()) ) 4486 z = kde(grid_coords) 4487 z = z.reshape(Nx,Ny) 4488 densort=np.sort(den)[::-1] 4489 values=[] 4490 Npts=xdat.shape[0] 4491 zvalues=[] 4492 for level in levels: 4493 ilevel = int(Npts*level + 0.5) 4494 if ilevel >= Npts: 4495 ilevel = Npts-1 4496 zvalues.append(densort[ilevel]) 4497 CS=plt.contour(x, y, z, zvalues,colors=[colors_by_name[name]],linestyles=line_styles ) 4498 CSlst.append(CS) 4499 4500 if par1_injvalue is not None and par2_injvalue is not None: 4501 plt.plot([par1_injvalue],[par2_injvalue],'b*',scalex=False,scaley=False,markersize=12) 4502 4503 if par_trigvalues1 is not None and par_trigvalues2 is not None: 4504 par1IFOs = set([IFO for IFO in par_trigvalues1.keys()]) 4505 par2IFOs = set([IFO for IFO in par_trigvalues2.keys()]) 4506 IFOs = par1IFOs.intersection(par2IFOs) 4507 for IFO in IFOs: 4508 if IFO=='H1': color = 'r' 4509 elif IFO=='L1': color = 'g' 4510 elif IFO=='V1': color = 'm' 4511 else: color = 'c' 4512 plt.plot([par_trigvalues1[IFO]],[par_trigvalues2[IFO]],color=color,marker='o',scalex=False,scaley=False) 4513 4514 plt.xlabel(plot_label(par1_name)) 4515 plt.ylabel(plot_label(par2_name)) 4516 plt.grid() 4517 4518 if len(name_list)!=len(CSlst): 4519 raise RuntimeError("Error number of contour objects does not equal number of names! Use only *one* contour from each set to associate a name.") 4520 4521 full_name_list=[] 4522 dummy_lines=[] 4523 for plot_name in name_list: 4524 full_name_list.append(plot_name) 4525 if len(confidence_levels)>1: 4526 for ls_,cl in zip(line_styles[0:len(confidence_levels)],confidence_levels): 4527 dummy_lines.append(mpl_lines.Line2D(np.array([0.,1.]),np.array([0.,1.]),ls=ls_,color='k')) 4528 full_name_list.append('%s%%'%str(int(cl*100))) 4529 4530 fig_actor_lst = [cs.collections[0] for cs in CSlst] 4531 fig_actor_lst.extend(dummy_lines) 4532 if legend is not None: 4533 try: 4534 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right',framealpha=0.1) 4535 except: 4536 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right') 4537 for text in twodcontour_legend.get_texts(): 4538 text.set_fontsize('small') 4539 4540 majorFormatterX=ScalarFormatter(useMathText=True) 4541 majorFormatterX.format_data=lambda data:'%.4g'%(data) 4542 majorFormatterY=ScalarFormatter(useMathText=True) 4543 majorFormatterY.format_data=lambda data:'%.4g'%(data) 4544 majorFormatterX.set_scientific(True) 4545 majorFormatterY.set_scientific(True) 4546 axes.xaxis.set_major_formatter(majorFormatterX) 4547 axes.yaxis.set_major_formatter(majorFormatterY) 4548 Nchars=max(map(lambda d:len(majorFormatterX.format_data(d)),axes.get_xticks())) 4549 if Nchars>8: 4550 Nticks=3 4551 elif Nchars>5: 4552 Nticks=4 4553 elif Nchars>4: 4554 Nticks=5 4555 else: 4556 Nticks=6 4557 locatorX=matplotlib.ticker.MaxNLocator(nbins=Nticks-1) 4558 if par1_name=='rightascension' or par1_name=='ra': 4559 (ramin,ramax)=plt.xlim() 4560 locatorX=RALocator(min=ramin,max=ramax) 4561 majorFormatterX=RAFormatter() 4562 if par1_name=='declination' or par1_name=='dec': 4563 (decmin,decmax)=plt.xlim() 4564 locatorX=DecLocator(min=decmin,max=decmax) 4565 majorFormatterX=DecFormatter() 4566 axes.xaxis.set_major_formatter(majorFormatterX) 4567 if par2_name=='rightascension' or par2_name=='ra': 4568 (ramin,ramax)=plt.ylim() 4569 locatorY=RALocator(ramin,ramax) 4570 axes.yaxis.set_major_locator(locatorY) 4571 majorFormatterY=RAFormatter() 4572 if par2_name=='declination' or par2_name=='dec': 4573 (decmin,decmax)=plt.ylim() 4574 locatorY=DecLocator(min=decmin,max=decmax) 4575 majorFormatterY=DecFormatter() 4576 axes.yaxis.set_major_locator(locatorY) 4577 4578 axes.yaxis.set_major_formatter(majorFormatterY) 4579 #locatorX.view_limits(bins[0],bins[-1]) 4580 axes.xaxis.set_major_locator(locatorX) 4581 4582 fix_axis_names(plt,par1_name,par2_name) 4583 4584 if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 4585 xmin,xmax=plt.xlim() 4586 if(xmin<0.0): xmin=0.0 4587 if(xmax>2.0*pi_constant): xmax=2.0*pi_constant 4588 plt.xlim(xmax,xmin) 4589 4590 return fig
4591
4592 4593 -def plot_two_param_kde(posterior,plot2DkdeParams):
4594 """ 4595 Plots a 2D kernel density estimate of the 2-parameter marginal posterior. 4596 4597 @param posterior: an instance of the Posterior class. 4598 4599 @param plot2DkdeParams: a dict {param1Name:Nparam1Bins,param2Name:Nparam2Bins} 4600 """ 4601 4602 from scipy import seterr as sp_seterr 4603 4604 par1_name,par2_name=plot2DkdeParams.keys() 4605 Nx=plot2DkdeParams[par1_name] 4606 Ny=plot2DkdeParams[par2_name] 4607 4608 xdat=posterior[par1_name].samples 4609 ydat=posterior[par2_name].samples 4610 4611 par_injvalue1=posterior[par1_name].injval 4612 par_injvalue2=posterior[par2_name].injval 4613 4614 par_trigvalues1=posterior[par1_name].trigvals 4615 par_trigvalues2=posterior[par2_name].trigvals 4616 4617 np.seterr(under='ignore') 4618 sp_seterr(under='ignore') 4619 4620 myfig=plt.figure(1,figsize=(6,4),dpi=200) 4621 myfig.add_axes(plt.Axes(myfig,[0.20,0.20,0.75,0.7])) 4622 plt.clf() 4623 4624 xax=np.linspace(min(xdat),max(xdat),Nx) 4625 yax=np.linspace(min(ydat),max(ydat),Ny) 4626 x,y=np.meshgrid(xax,yax) 4627 4628 samp=np.transpose(np.column_stack((xdat,ydat))) 4629 4630 kde=stats.kde.gaussian_kde(samp) 4631 4632 grid_coords = np.append(x.reshape(-1,1),y.reshape(-1,1),axis=1) 4633 4634 z = kde(grid_coords.T) 4635 z = z.reshape(Nx,Ny) 4636 4637 values=[] 4638 for level in levels: 4639 ilevel = int(Npts*level + 0.5) 4640 if ilevel >= Npts: 4641 ilevel = Npts-1 4642 zvalues.append(densort[ilevel]) 4643 4644 pp.contour(XS, YS, ZS, zvalues) 4645 4646 asp=xax.ptp()/yax.ptp() 4647 # if(asp<0.8 or asp > 1.6): asp=1.4 4648 plt.imshow(z,extent=(xax[0],xax[-1],yax[0],yax[-1]),aspect=asp,origin='lower') 4649 plt.colorbar() 4650 4651 if par_injvalue1 is not None and par_injvalue2 is not None: 4652 plt.plot([par_injvalue1],[par_injvalue2],'bo',scalex=False,scaley=False) 4653 4654 if par_trigvalues1 is not None and par_trigvalues2 is not None: 4655 par1IFOs = set([IFO for IFO in par_trigvalues1.keys()]) 4656 par2IFOs = set([IFO for IFO in par_trigvalues2.keys()]) 4657 IFOs = par1IFOs.intersection(par2IFOs) 4658 for IFO in IFOs: 4659 if IFO=='H1': color = 'r' 4660 elif IFO=='L1': color = 'g' 4661 elif IFO=='V1': color = 'm' 4662 else: color = 'c' 4663 plt.plot([par_trigvalues1[IFO]],[par_trigvalues2[IFO]],color=color,marker='o',scalex=False,scaley=False) 4664 4665 plt.xlabel(plot_label(par1_name)) 4666 plt.ylabel(plot_label(par2_name)) 4667 plt.grid() 4668 4669 # For RA and dec set custom labels and for RA reverse 4670 if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 4671 xmin,xmax=plt.xlim() 4672 plt.xlim(xmax,xmin) 4673 #if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 4674 # locs, ticks = plt.xticks() 4675 # (newlocs, newticks)=formatRATicks(locs, ticks) 4676 # plt.xticks(newlocs,newticks,rotation=45) 4677 #if(par1_name.lower()=='dec' or par1_name.lower()=='declination'): 4678 # locs, ticks = plt.xticks() 4679 # newlocs, newticks = formatDecTicks(locs) 4680 # plt.xticks(newlocs,newticks,rotation=45) 4681 4682 #if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 4683 # ymin,ymax=plt.ylim() 4684 # plt.ylim(ymax,ymin) 4685 #if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 4686 # locs, ticks = plt.yticks() 4687 # newlocs,newticks=formatRATicks(locs) 4688 # plt.yticks(newlocs,newticks) 4689 #if(par2_name.lower()=='dec' or par2_name.lower()=='declination'): 4690 # locs, ticks = plt.yticks() 4691 # newlocs,newticks=formatDecTicks(locs) 4692 # plt.yticks(newlocs,newticks) 4693 4694 return myfig
4695 #
4696 4697 -def get_inj_by_time(injections,time):
4698 """ 4699 Filter injections to find the injection with end time given by time +/- 0.1s 4700 """ 4701 import itertools 4702 injection = itertools.ifilter(lambda a: abs(float(a.get_end()) - time) < 0.1, injections).next() 4703 return injection
4704
4705 -def histogram2D(posterior,greedy2Params,confidence_levels):
4706 """ 4707 Returns a 2D histogram and edges for the two parameters passed in greedy2Params, plus the actual discrete confidence levels 4708 imposed by the finite number of samples. 4709 H,xedges,yedges,Hlasts = histogram2D(posterior,greedy2Params,confidence_levels) 4710 @param posterior: Posterior instance 4711 @param greedy2Params: a dict ;{param1Name:param1binSize,param2Name:param2binSize} 4712 @param confidence_levels: a list of the required confidence levels to plot on the contour map. 4713 """ 4714 4715 par1_name,par2_name=greedy2Params.keys() 4716 par1_bin=greedy2Params[par1_name] 4717 par2_bin=greedy2Params[par2_name] 4718 par1_injvalue=posterior[par1_name.lower()].injval 4719 par2_injvalue=posterior[par2_name.lower()].injval 4720 a=np.squeeze(posterior[par1_name].samples) 4721 b=np.squeeze(posterior[par2_name].samples) 4722 par1pos_min=a.min() 4723 par2pos_min=b.min() 4724 par1pos_max=a.max() 4725 par2pos_max=b.max() 4726 par1pos_Nbins= int(ceil((par1pos_max - par1pos_min)/par1_bin))+1 4727 par2pos_Nbins= int(ceil((par2pos_max - par2pos_min)/par2_bin))+1 4728 H, xedges, yedges = np.histogram2d(a,b, bins=(par1pos_Nbins, par2pos_Nbins),normed=True) 4729 temp=np.copy(H) 4730 temp=temp.ravel() 4731 confidence_levels.sort() 4732 Hsum=0 4733 Hlasts=[] 4734 idxes=np.argsort(temp) 4735 j=len(idxes)-1 4736 for cl in confidence_levels: 4737 while float(Hsum/np.sum(H))<cl: 4738 #ind = np.argsort(temp) 4739 max_i=idxes[j] 4740 j-=1 4741 val = temp[max_i] 4742 Hlast=val 4743 Hsum+=val 4744 temp[max_i]=0 4745 Hlasts.append(Hlast) 4746 return (H,xedges,yedges,Hlasts)
4747
4748 -def plot_two_param_greedy_bins_contourf(posteriors_by_name,greedy2Params,confidence_levels,colors_by_name,figsize=(7,6),dpi=120,figposition=[0.3,0.3,0.5,0.5],legend='right',hatches_by_name=None):
4749 """ 4750 @param posteriors_by_name A dictionary of posterior objects indexed by name 4751 @param greedy2Params: a dict ;{param1Name:param1binSize,param2Name:param2binSize} 4752 @param confidence_levels: a list of the required confidence levels to plot on the contour map. 4753 4754 """ 4755 fig=plt.figure(1,figsize=figsize,dpi=dpi) 4756 plt.clf() 4757 fig.add_axes(figposition) 4758 CSlst=[] 4759 name_list=[] 4760 par1_name,par2_name=greedy2Params.keys() 4761 for name,posterior in posteriors_by_name.items(): 4762 name_list.append(name) 4763 H,xedges,yedges,Hlasts=histogram2D(posterior,greedy2Params,confidence_levels+[0.99999999999999]) 4764 extent= [xedges[0], yedges[-1], xedges[-1], xedges[0]] 4765 CS2=plt.contourf(yedges[:-1],xedges[:-1],H,Hlasts,extend='max',colors=[colors_by_name[name]] ,alpha=0.3 ) 4766 CS=plt.contour(yedges[:-1],xedges[:-1],H,Hlasts,extend='max',colors=[colors_by_name[name]] ) 4767 CSlst.append(CS) 4768 4769 plt.title("%s-%s confidence contours (greedy binning)"%(par1_name,par2_name)) # add a title 4770 plt.xlabel(plot_label(par2_name)) 4771 plt.ylabel(plot_label(par1_name)) 4772 if len(name_list)!=len(CSlst): 4773 raise RuntimeError("Error number of contour objects does not equal number of names! Use only *one* contour from each set to associate a name.") 4774 full_name_list=[] 4775 dummy_lines=[] 4776 for plot_name in name_list: 4777 full_name_list.append(plot_name) 4778 if len(confidence_levels)>1: 4779 for cl in confidence_levels+[1]: 4780 dummy_lines.append(mpl_lines.Line2D(np.array([0.,1.]),np.array([0.,1.]),color='k')) 4781 full_name_list.append('%s%%'%str(int(cl*100))) 4782 fig_actor_lst = [cs.collections[0] for cs in CSlst] 4783 fig_actor_lst.extend(dummy_lines) 4784 if legend is not None: 4785 try: 4786 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right',framealpha=0.1) 4787 except: 4788 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right') 4789 for text in twodcontour_legend.get_texts(): 4790 text.set_fontsize('small') 4791 fix_axis_names(plt,par1_name,par2_name) 4792 return fig
4793
4794 -def fix_axis_names(plt,par1_name,par2_name):
4795 """ 4796 Fixes names of axes 4797 """ 4798 return 4799 4800 # For ra and dec set custom labels and for RA reverse 4801 if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 4802 ymin,ymax=plt.ylim() 4803 if(ymin<0.0): ylim=0.0 4804 if(ymax>2.0*pi_constant): ymax=2.0*pi_constant 4805 plt.ylim(ymax,ymin) 4806 if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 4807 locs, ticks = plt.yticks() 4808 newlocs, newticks = formatRATicks(locs) 4809 plt.yticks(newlocs,newticks) 4810 if(par1_name.lower()=='dec' or par1_name.lower()=='declination'): 4811 locs, ticks = plt.yticks() 4812 newlocs,newticks=formatDecTicks(locs) 4813 plt.yticks(newlocs,newticks) 4814 4815 if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 4816 xmin,xmax=plt.xlim() 4817 if(xmin<0.0): xmin=0.0 4818 if(xmax>2.0*pi_constant): xmax=2.0*pi_constant 4819 plt.xlim(xmax,xmin) 4820 if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 4821 locs, ticks = plt.xticks() 4822 newlocs, newticks = formatRATicks(locs) 4823 plt.xticks(newlocs,newticks,rotation=45) 4824 if(par2_name.lower()=='dec' or par2_name.lower()=='declination'): 4825 locs, ticks = plt.xticks() 4826 newlocs, newticks = formatDecTicks(locs) 4827 plt.xticks(newlocs,newticks,rotation=45) 4828 return plt
4829
4830 -def plot_two_param_greedy_bins_contour(posteriors_by_name,greedy2Params,confidence_levels,colors_by_name,line_styles=__default_line_styles,figsize=(4,3),dpi=250,figposition=[0.2,0.2,0.48,0.75],legend='right'):
4831 """ 4832 Plots the confidence level contours as determined by the 2-parameter 4833 greedy binning algorithm. 4834 4835 @param posteriors_by_name: A dict containing Posterior instances referenced by some id. 4836 4837 @param greedy2Params: a dict ;{param1Name:param1binSize,param2Name:param2binSize} 4838 4839 @param confidence_levels: a list of the required confidence levels to plot on the contour map. 4840 4841 @param colors_by_name: A dict of colors cross-referenced to the above Posterior ids. 4842 4843 @param legend: Argument for legend placement or None for no legend ('right', 'upper left', 'center' etc) 4844 4845 """ 4846 4847 fig=plt.figure(1,figsize=figsize,dpi=dpi) 4848 plt.clf() 4849 4850 axes=fig.add_axes(figposition) 4851 4852 #This fixes the precedence of line styles in the plot 4853 if len(line_styles)<len(confidence_levels): 4854 raise RuntimeError("Error: Need as many or more line styles to choose from as confidence levels to plot!") 4855 4856 CSlst=[] 4857 name_list=[] 4858 for name,posterior in posteriors_by_name.items(): 4859 4860 name_list.append(name) 4861 #Extract parameter names 4862 par1_name,par2_name=greedy2Params.keys() 4863 #Extract bin sizes 4864 par1_bin=greedy2Params[par1_name] 4865 par2_bin=greedy2Params[par2_name] 4866 4867 #Extract injection information 4868 par1_injvalue=posterior[par1_name.lower()].injval 4869 par2_injvalue=posterior[par2_name.lower()].injval 4870 4871 #Extract trigger information 4872 par1_trigvalues=posterior[par1_name.lower()].trigvals 4873 par2_trigvalues=posterior[par2_name.lower()].trigvals 4874 4875 a=np.squeeze(posterior[par1_name].samples) 4876 b=np.squeeze(posterior[par2_name].samples) 4877 4878 #Create 2D bin array 4879 par1pos_min=a.min() 4880 par2pos_min=b.min() 4881 4882 par1pos_max=a.max() 4883 par2pos_max=b.max() 4884 4885 par1pos_Nbins= int(ceil((par1pos_max - par1pos_min)/par1_bin))+1 4886 par2pos_Nbins= int(ceil((par2pos_max - par2pos_min)/par2_bin))+1 4887 4888 if par1_name.find('time')!=-1: 4889 offset=floor(min(a)) 4890 a=a-offset 4891 if par1_injvalue: 4892 par1_injvalue=par1_injvalue-offset 4893 ax1_name=par1_name+' + %i'%(int(offset)) 4894 else: ax1_name=par1_name 4895 4896 if par2_name.find('time')!=-1: 4897 offset=floor(min(b)) 4898 b=b-offset 4899 if par2_injvalue: 4900 par2_injvalue=par2_injvalue-offset 4901 ax2_name=par2_name+' + %i'%(int(offset)) 4902 else: ax2_name=par2_name 4903 4904 4905 majorFormatterX=ScalarFormatter(useMathText=True) 4906 majorFormatterX.format_data=lambda data:'%.4g'%(data) 4907 majorFormatterY=ScalarFormatter(useMathText=True) 4908 majorFormatterY.format_data=lambda data:'%.4g'%(data) 4909 majorFormatterX.set_scientific(True) 4910 majorFormatterY.set_scientific(True) 4911 axes.xaxis.set_major_formatter(majorFormatterX) 4912 axes.yaxis.set_major_formatter(majorFormatterY) 4913 4914 H, xedges, yedges = np.histogram2d(a,b, bins=(par1pos_Nbins, par2pos_Nbins),normed=True) 4915 4916 extent = [xedges[0], yedges[-1], xedges[-1], xedges[0]] 4917 4918 temp=np.copy(H) 4919 temp=temp.ravel() 4920 confidence_levels.sort() 4921 Hsum=0 4922 Hlasts=[] 4923 idxes=np.argsort(temp) 4924 j=len(idxes)-1 4925 for cl in confidence_levels: 4926 while float(Hsum/np.sum(H))<cl: 4927 #ind = np.argsort(temp) 4928 max_i=idxes[j] 4929 j-=1 4930 val = temp[max_i] 4931 Hlast=val 4932 Hsum+=val 4933 temp[max_i]=0 4934 Hlasts.append(Hlast) 4935 CS=plt.contour(yedges[:-1],xedges[:-1],H,Hlasts,colors=[colors_by_name[name]],linestyles=line_styles) 4936 plt.grid() 4937 if(par1_injvalue is not None and par2_injvalue is not None): 4938 plt.plot([par2_injvalue],[par1_injvalue],'b*',scalex=False,scaley=False,markersize=12) 4939 if(par1_trigvalues is not None and par2_trigvalues is not None): 4940 par1IFOs = set([IFO for IFO in par1_trigvalues.keys()]) 4941 par2IFOs = set([IFO for IFO in par2_trigvalues.keys()]) 4942 IFOs = par1IFOs.intersection(par2IFOs) 4943 if IFO=='H1': color = 'r' 4944 elif IFO=='L1': color = 'g' 4945 elif IFO=='V1': color = 'm' 4946 else: color = 'c' 4947 plt.plot([par2_trigvalues[IFO]],[par1_trigvalues[IFO]],color=color,marker='*',scalex=False,scaley=False) 4948 CSlst.append(CS) 4949 4950 Nchars=max(map(lambda d:len(majorFormatterX.format_data(d)),axes.get_xticks())) 4951 if Nchars>8: 4952 Nticks=3 4953 elif Nchars>5: 4954 Nticks=4 4955 elif Nchars>4: 4956 Nticks=5 4957 else: 4958 Nticks=6 4959 locatorX=matplotlib.ticker.MaxNLocator(nbins=Nticks-1) 4960 if par2_name=='rightascension' or par2_name=='ra': 4961 (ramin,ramax)=plt.xlim() 4962 locatorX=RALocator(min=ramin,max=ramax) 4963 majorFormatterX=RAFormatter() 4964 if par2_name=='declination' or par2_name=='dec': 4965 (decmin,decmax)=plt.xlim() 4966 locatorX=DecLocator(min=decmin,max=decmax) 4967 majorFormatterX=DecFormatter() 4968 axes.xaxis.set_major_formatter(majorFormatterX) 4969 if par1_name=='rightascension' or par1_name=='ra': 4970 (ramin,ramax)=plt.ylim() 4971 locatorY=RALocator(ramin,ramax) 4972 axes.yaxis.set_major_locator(locatorY) 4973 majorFormatterY=RAFormatter() 4974 if par1_name=='declination' or par1_name=='dec': 4975 (decmin,decmax)=plt.ylim() 4976 locatorY=DecLocator(min=decmin,max=decmax) 4977 majorFormatterY=DecFormatter() 4978 axes.yaxis.set_major_locator(locatorY) 4979 4980 axes.yaxis.set_major_formatter(majorFormatterY) 4981 #locatorX.view_limits(bins[0],bins[-1]) 4982 axes.xaxis.set_major_locator(locatorX) 4983 4984 #plt.title("%s-%s confidence contours (greedy binning)"%(par1_name,par2_name)) # add a title 4985 plt.xlabel(plot_label(ax2_name)) 4986 plt.ylabel(plot_label(ax1_name)) 4987 4988 if len(name_list)!=len(CSlst): 4989 raise RuntimeError("Error number of contour objects does not equal number of names! Use only *one* contour from each set to associate a name.") 4990 full_name_list=[] 4991 dummy_lines=[] 4992 4993 for plot_name in name_list: 4994 full_name_list.append(plot_name) 4995 if len(confidence_levels)>1: 4996 for ls_,cl in zip(line_styles[0:len(confidence_levels)],confidence_levels): 4997 dummy_lines.append(mpl_lines.Line2D(np.array([0.,1.]),np.array([0.,1.]),ls=ls_,color='k')) 4998 full_name_list.append('%s%%'%str(int(cl*100))) 4999 5000 fig_actor_lst = [cs.collections[0] for cs in CSlst] 5001 5002 fig_actor_lst.extend(dummy_lines) 5003 5004 if legend is not None: 5005 try: 5006 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right',framealpha=0.1) 5007 except: 5008 twodcontour_legend=plt.figlegend(tuple(fig_actor_lst), tuple(full_name_list), loc='right') 5009 for text in twodcontour_legend.get_texts(): 5010 text.set_fontsize('small') 5011 5012 5013 # For ra and dec set custom labels and for RA reverse 5014 #if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 5015 # ymin,ymax=plt.ylim() 5016 # if(ymin<0.0): ylim=0.0 5017 # if(ymax>2.0*pi_constant): ymax=2.0*pi_constant 5018 # plt.ylim(ymax,ymin) 5019 #if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 5020 # locs, ticks = plt.yticks() 5021 # newlocs, newticks = formatRATicks(locs) 5022 # plt.yticks(newlocs,newticks) 5023 #if(par1_name.lower()=='dec' or par1_name.lower()=='declination'): 5024 # locs, ticks = plt.yticks() 5025 # newlocs,newticks=formatDecTicks(locs) 5026 # plt.yticks(newlocs,newticks) 5027 5028 if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 5029 xmin,xmax=plt.xlim() 5030 if(xmin<0.0): xmin=0.0 5031 if(xmax>2.0*pi_constant): xmax=2.0*pi_constant 5032 plt.xlim(xmax,xmin) 5033 #if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 5034 # locs, ticks = plt.xticks() 5035 # newlocs, newticks = formatRATicks(locs) 5036 # plt.xticks(newlocs,newticks,rotation=45) 5037 #if(par2_name.lower()=='dec' or par2_name.lower()=='declination'): 5038 # locs, ticks = plt.xticks() 5039 # newlocs, newticks = formatDecTicks(locs) 5040 # plt.xticks(newlocs,newticks,rotation=45) 5041 5042 return fig
5043 #
5044 5045 -def plot_two_param_greedy_bins_hist(posterior,greedy2Params,confidence_levels):
5046 """ 5047 Histograms of the ranked pixels produced by the 2-parameter greedy 5048 binning algorithm colured by their confidence level. 5049 5050 @param toppoints: Nx2 array of 2-parameter posterior samples. 5051 5052 @param posterior: an instance of the Posterior class. 5053 5054 @param greedy2Params: a dict ;{param1Name:param1binSize,param2Name:param2binSize} 5055 """ 5056 5057 from scipy import seterr as sp_seterr 5058 5059 np.seterr(under='ignore') 5060 sp_seterr(under='ignore') 5061 5062 #Extract parameter names 5063 par1_name,par2_name=greedy2Params.keys() 5064 #Extract bin sizes 5065 par1_bin=greedy2Params[par1_name] 5066 par2_bin=greedy2Params[par2_name] 5067 5068 a=np.squeeze(posterior[par1_name].samples) 5069 b=np.squeeze(posterior[par2_name].samples) 5070 5071 #Extract injection information 5072 par1_injvalue=posterior[par1_name.lower()].injval 5073 par2_injvalue=posterior[par2_name.lower()].injval 5074 5075 #Create 2D bin array 5076 par1pos_min=a.min() 5077 par2pos_min=b.min() 5078 5079 par1pos_max=a.max() 5080 par2pos_max=b.max() 5081 5082 par1pos_Nbins= int(ceil((par1pos_max - par1pos_min)/par1_bin))+1 5083 par2pos_Nbins= int(ceil((par2pos_max - par2pos_min)/par2_bin))+1 5084 5085 # Adjust for time parameter 5086 if par1_name.find('time')!=-1: 5087 offset=floor(min(a)) 5088 a=a-offset 5089 if par1_injvalue: 5090 par1_injvalue=par1_injvalue-offset 5091 ax1_name=par1_name+' + %i'%(int(offset)) 5092 else: ax1_name=par1_name 5093 5094 if par2_name.find('time')!=-1: 5095 offset=floor(min(b)) 5096 b=b-offset 5097 if par2_injvalue: 5098 par2_injvalue=par2_injvalue-offset 5099 ax2_name=par2_name+' + %i'%(int(offset)) 5100 else: ax2_name=par2_name 5101 5102 5103 #Extract trigger information 5104 par1_trigvalues=posterior[par1_name.lower()].trigvals 5105 par2_trigvalues=posterior[par2_name.lower()].trigvals 5106 5107 myfig=plt.figure() 5108 axes=plt.Axes(myfig,[0.3,0.3,0.95-0.3,0.90-0.3]) 5109 myfig.add_axes(axes) 5110 5111 #plt.clf() 5112 plt.xlabel(plot_label(ax2_name)) 5113 plt.ylabel(plot_label(ax1_name)) 5114 5115 #bins=(par1pos_Nbins,par2pos_Nbins) 5116 bins=(50,50) # Matches plot_one_param_pdf 5117 5118 majorFormatterX=ScalarFormatter(useMathText=True) 5119 majorFormatterX.format_data=lambda data:'%.4g'%(data) 5120 majorFormatterY=ScalarFormatter(useMathText=True) 5121 majorFormatterY.format_data=lambda data:'%.4g'%(data) 5122 majorFormatterX.set_scientific(True) 5123 majorFormatterY.set_scientific(True) 5124 axes.xaxis.set_major_formatter(majorFormatterX) 5125 axes.yaxis.set_major_formatter(majorFormatterY) 5126 H, xedges, yedges = np.histogram2d(a,b, bins,normed=False) 5127 5128 5129 #Replace H with greedy bin confidence levels at each pixel... 5130 temp=np.copy(H) 5131 temp=temp.flatten() 5132 5133 Hsum=0 5134 Hsum_actual=np.sum(H) 5135 5136 idxes=np.argsort(temp) 5137 j=len(idxes)-1 5138 while Hsum<Hsum_actual: 5139 #ind = np.argsort(temp) 5140 max_i=idxes[j] 5141 j-=1 5142 val = temp[max_i] 5143 Hsum+=int(val) 5144 temp[max_i]=0 5145 5146 #print Hsum,Hsum_actual 5147 H.flat[max_i]=1-float(Hsum)/float(Hsum_actual) 5148 5149 extent = [yedges[0], yedges[-1], xedges[0], xedges[-1]] 5150 plt.imshow(np.flipud(H), axes=axes, aspect='auto', extent=extent, interpolation='nearest',cmap='gray_r') 5151 plt.gca().autoscale_view() 5152 plt.colorbar() 5153 5154 #plt.hexbin(a,b,cmap='gray_r',axes=axes ) 5155 5156 Nchars=max(map(lambda d:len(majorFormatterX.format_data(d)),axes.get_xticks())) 5157 if Nchars>8: 5158 Nticks=3 5159 elif Nchars>5: 5160 Nticks=4 5161 elif Nchars>4: 5162 Nticks=5 5163 else: 5164 Nticks=6 5165 locatorX=matplotlib.ticker.MaxNLocator(nbins=Nticks-1) 5166 (xmin,xmax)=plt.xlim() 5167 (ymin,ymax)=plt.ylim() 5168 if par2_name=='rightascension' or par2_name=='ra': 5169 locatorX=RALocator(min=xmin,max=xmax) 5170 majorFormatterX=RAFormatter() 5171 if par2_name=='declination' or par2_name=='dec': 5172 locatorX=DecLocator(min=xmin,max=xmax) 5173 majorFormatterX=DecFormatter() 5174 if par1_name=='rightascension' or par1_name=='ra': 5175 locatorY=RALocator(min=ymin,max=ymax) 5176 axes.yaxis.set_major_locator(locatorY) 5177 majorFormatterY=RAFormatter() 5178 if par1_name=='declination' or par1_name=='dec': 5179 locatorY=DecLocator(min=ymin,max=ymax) 5180 axes.yaxis.set_major_locator(locatorY) 5181 majorFormatterY=DecFormatter() 5182 5183 axes.xaxis.set_major_formatter(majorFormatterX) 5184 axes.yaxis.set_major_formatter(majorFormatterY) 5185 #locatorX.view_limits(bins[0],bins[-1]) 5186 axes.xaxis.set_major_locator(locatorX) 5187 5188 if par1_injvalue is not None and par2_injvalue is not None: 5189 plt.plot([par2_injvalue],[par1_injvalue],'bo',scalex=False,scaley=False) 5190 5191 if par1_trigvalues is not None and par2_trigvalues is not None: 5192 par1IFOs = set([IFO for IFO in par1_trigvalues.keys()]) 5193 par2IFOs = set([IFO for IFO in par2_trigvalues.keys()]) 5194 IFOs = par1IFOs.intersection(par2IFOs) 5195 if IFO=='H1': color = 'r' 5196 elif IFO=='L1': color = 'g' 5197 elif IFO=='V1': color = 'm' 5198 else: color = 'c' 5199 plt.plot([par2_trigvalues[IFO]],[par1_trigvalues[IFO]],color=color,marker='o',scalex=False,scaley=False) 5200 5201 # For RA and dec set custom labels and for RA reverse 5202 #if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 5203 # ymin,ymax=plt.ylim() 5204 # plt.ylim(ymax,ymin) 5205 #if(par1_name.lower()=='ra' or par1_name.lower()=='rightascension'): 5206 # locs, ticks = plt.yticks() 5207 # newlocs, newticks = formatRATicks(locs) 5208 # plt.yticks(newlocs,newticks) 5209 #if(par1_name.lower()=='dec' or par1_name.lower()=='declination'): 5210 # locs, ticks = plt.yticks() 5211 # newlocs, newticks = formatDecTicks(locs) 5212 # plt.yticks(newlocs,newticks) 5213 5214 if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 5215 xmin,xmax=plt.xlim() 5216 if(xmin)<0.0: xmin=0.0 5217 if(xmax>2.0*pi_constant): xmax=2.0*pi_constant 5218 plt.xlim(xmax,xmin) 5219 #if(par2_name.lower()=='ra' or par2_name.lower()=='rightascension'): 5220 # locs, ticks = plt.xticks() 5221 # newlocs, newticks = formatRATicks(locs) 5222 # plt.xticks(newlocs,newticks,rotation=45) 5223 #if(par2_name.lower()=='dec' or par2_name.lower()=='declination'): 5224 # locs, ticks = plt.xticks() 5225 # newlocs, newticks = formatDecTicks(locs) 5226 # plt.xticks(newlocs,newticks,rotation=45) 5227 5228 return myfig
5229
5230 -def greedy_bin_one_param(posterior,greedy1Param,confidence_levels):
5231 """ 5232 Determine the 1-parameter Bayesian Confidence Interval using a greedy 5233 binning algorithm. 5234 5235 @param posterior: an instance of the posterior class. 5236 5237 @param greedy1Param: a dict; {paramName:paramBinSize}. 5238 5239 @param confidence_levels: A list of floats of the required confidence intervals [(0-1)]. 5240 """ 5241 5242 paramName=greedy1Param.keys()[0] 5243 par_bin=greedy1Param.values()[0] 5244 par_samps=posterior[paramName.lower()].samples 5245 5246 parpos_min=min(par_samps)[0] 5247 parpos_max=max(par_samps)[0] 5248 5249 par_point=parpos_min 5250 5251 parpos_Nbins= int(ceil((parpos_max - parpos_min)/par_bin))+1 5252 5253 greedyPoints=np.zeros((parpos_Nbins,2)) 5254 # ...NB 2D so it can be put through same confidence level function 5255 greedyHist=np.zeros(parpos_Nbins,dtype='i8') 5256 5257 #Bin up 5258 for i in range(parpos_Nbins): 5259 greedyPoints[i,0]=par_point 5260 greedyPoints[i,1]=par_point 5261 par_point+=par_bin 5262 5263 for par_samp in par_samps: 5264 par_samp=par_samp[0] 5265 par_binNumber=int(floor((par_samp-parpos_min)/par_bin)) 5266 try: 5267 greedyHist[par_binNumber]+=1 5268 except IndexError: 5269 print "IndexError: bin number: %i total bins: %i parsamp: %f "\ 5270 %(par_binNumber,parpos_Nbins,par_samp) 5271 5272 #Find injection bin 5273 injbin=None 5274 par_injvalue=posterior[paramName].injval 5275 if par_injvalue: 5276 par_binNumber=floor((par_injvalue-parpos_min)/par_bin) 5277 injbin=par_binNumber 5278 5279 toppoints,injectionconfidence,reses,injection_area=_greedy_bin(greedyHist,greedyPoints,injbin,float(par_bin),int(len(par_samps)),confidence_levels) 5280 cl_intervals=[] 5281 confidence_levels.sort() 5282 for cl in confidence_levels: 5283 ind=np.nonzero(toppoints[:,-1]<cl) 5284 5285 if len(ind[0]) > 1: 5286 cl_intervals.append((np.min(toppoints[ind,0]),np.max(toppoints[ind,0]))) 5287 5288 else: 5289 5290 cl_intervals.append((toppoints[ind[0],0],toppoints[ind[0],0])) 5291 5292 return toppoints,injectionconfidence,reses,injection_area,cl_intervals
5293
5294 # 5295 -def contigious_interval_one_param(posterior,contInt1Params,confidence_levels):
5296 """ 5297 Calculates the smallest contigious 1-parameter confidence interval for a 5298 set of given confidence levels. 5299 5300 @param posterior: an instance of the Posterior class. 5301 5302 @param contInt1Params: a dict {paramName:paramBinSize}. 5303 5304 @param confidence_levels: Required confidence intervals. 5305 5306 """ 5307 oneDContCL={} 5308 oneDContInj={} 5309 5310 paramName=contInt1Params.keys()[0] 5311 par_bin=contInt1Params.values()[0] 5312 5313 par_injvalue=posterior[paramName].injval 5314 5315 par_samps=posterior[paramName].samples 5316 5317 parpos_min=min(par_samps) 5318 parpos_max=max(par_samps) 5319 5320 par_point=parpos_min 5321 parpos_Nbins= int(ceil((parpos_max - parpos_min)/par_bin))+1 5322 5323 greedyHist=np.zeros(parpos_Nbins,dtype='i8') 5324 5325 for par_samp in par_samps: 5326 par_binNumber=int(floor((par_samp-parpos_min)/par_bin)) 5327 try: 5328 greedyHist[par_binNumber]+=1 5329 except IndexError: 5330 print "IndexError: bin number: %i total bins: %i parsamp: %f bin: %f - %f"\ 5331 %( 5332 par_binNumber, 5333 parpos_Nbins, 5334 par_samp, 5335 greedyPoints[par_binNumber-1,0], 5336 greedyPoints[par_binNumber-1,0]+par_bin 5337 ) 5338 5339 injbin=None 5340 #Find injection bin 5341 if par_injvalue: 5342 par_binNumber=floor((par_injvalue-parpos_min)/par_bin) 5343 injbin=par_binNumber 5344 5345 j=0 5346 #print "Calculating contigious confidence intervals for %s..."%par_name 5347 len_par_samps=len(par_samps) 5348 5349 injinterval=None 5350 5351 #Determine smallest contigious interval for given confidence levels (brute force) 5352 while j < len(confidence_levels): 5353 confidence_level=confidence_levels[j] 5354 #Loop over size of interval 5355 max_left=0 5356 max_right=0 5357 5358 for i in range(len(greedyHist)): 5359 5360 max_frac=None 5361 left=0 5362 right=i 5363 5364 #Slide interval 5365 while right<len(greedyHist): 5366 Npoints=sum(greedyHist[left:right]) 5367 frac=float(Npoints)/float(len_par_samps) 5368 #print "left %i , right %i , frac %f"%(left,right,frac) 5369 5370 if max_frac is None: 5371 max_frac=frac 5372 max_left=left 5373 max_right=right 5374 else: 5375 if frac>max_frac: 5376 max_frac=frac 5377 max_left=left 5378 max_right=right 5379 5380 left+=1 5381 right+=1 5382 5383 if injbin is not None and injinterval is None: 5384 if injbin in range(max_left,max_right): 5385 injinterval=(max_right-max_left)*par_bin 5386 oneDContInj['interval']=injinterval 5387 oneDContInj['confidence']=1-frac 5388 if max_frac > confidence_level: 5389 break 5390 5391 max_frac=None 5392 5393 if max_frac is None: 5394 print "Cant determine intervals at %f confidence!"%confidence_level 5395 else: 5396 5397 oneDContCL['left']=max_left*par_bin 5398 oneDContCL['right']=max_right*par_bin 5399 oneDContCL['width']=(max_right-max_left)*par_bin 5400 k=j 5401 while k+1<len(confidence_levels) : 5402 if confidence_levels[k+1]<max_frac: 5403 j+=1 5404 k+=1 5405 j+=1 5406 5407 return oneDContCL,oneDContInj
5408 #
5409 -def burnin(data,spin_flag,deltaLogP,outputfile):
5410 5411 pos,bayesfactor=_burnin(data,spin_flag,deltaLogP,outputfile) 5412 5413 return pos,bayesfactor
5414
5415 5416 -class ACLError(StandardError):
5417 - def __init__(self, *args):
5418 super(ACLError, self).__init__(*args)
5419
5420 5421 -def autocorrelation(series):
5422 """Returns an estimate of the autocorrelation function of a given 5423 series. Returns only the positive-lag portion of the ACF, 5424 normalized so that the zero-th element is 1.""" 5425 x=series-np.mean(series) 5426 y=np.conj(x[::-1]) 5427 5428 acf=np.fft.ifftshift(signal.fftconvolve(y,x,mode='full')) 5429 5430 N=series.shape[0] 5431 5432 acf = acf[0:N] 5433 5434 return acf/acf[0]
5435
5436 5437 -def autocorrelation_length_estimate(series, acf=None, M=5, K=2):
5438 """Attempts to find a self-consistent estimate of the 5439 autocorrelation length of a given series. 5440 5441 If C(tau) is the autocorrelation function (normalized so C(0) = 1, 5442 for example from the autocorrelation procedure in this module), 5443 then the autocorrelation length is the smallest s such that 5444 5445 1 + 2*C(1) + 2*C(2) + ... + 2*C(M*s) < s 5446 5447 In words: the autocorrelation length is the shortest length so 5448 that the sum of the autocorrelation function is smaller than that 5449 length over a window of M times that length. 5450 5451 The maximum window length is restricted to be len(series)/K as a 5452 safety precaution against relying on data near the extreme of the 5453 lags in the ACF, where there is a lot of noise. Note that this 5454 implies that the series must be at least M*K*s samples long in 5455 order to get a reliable estimate of the ACL. 5456 5457 If no such s can be found, raises ACLError; in this case it is 5458 likely that the series is too short relative to its true 5459 autocorrelation length to obtain a consistent ACL estimate.""" 5460 5461 if acf is None: 5462 acf=autocorrelation(series) 5463 acf[1:] *= 2.0 5464 5465 imax=int(acf.shape[0]/K) 5466 5467 # Cumulative sum and ACL length associated with each window 5468 cacf=np.cumsum(acf) 5469 s=np.arange(1, cacf.shape[0]+1)/float(M) 5470 5471 # Find all places where cumulative sum over window is smaller than 5472 # associated ACL. 5473 estimates=np.flatnonzero(cacf[:imax] < s[:imax]) 5474 5475 if estimates.shape[0] > 0: 5476 # Return the first index where cumulative sum is smaller than 5477 # ACL associated with that index's window 5478 return s[estimates[0]] 5479 else: 5480 # Cannot find self-consistent ACL estimate. 5481 raise ACLError('autocorrelation length too short for consistent estimate')
5482
5483 5484 -def effectiveSampleSize(samples, Nskip=1):
5485 """ 5486 Compute the effective sample size, calculating the ACL using only 5487 the second half of the samples to avoid ACL overestimation due to 5488 chains equilibrating after adaptation. 5489 """ 5490 N = len(samples) 5491 acf = autocorrelation(samples[N/2:]) 5492 try: 5493 acl = autocorrelation_length_estimate(samples[N/2:], acf=acf) 5494 except ACLError: 5495 acl = N 5496 Neffective = floor(N/acl) 5497 acl *= Nskip 5498 return (Neffective, acl, acf)
5499
5500 5501 -def readCoincXML(xml_file, trignum):
5502 triggers=None 5503 5504 from glue.ligolw import ligolw 5505 coincXML = utils.load_filename(xml_file, contenthandler = lsctables.use_in(ligolw.LIGOLWContentHandler)) 5506 coinc = lsctables.CoincTable.get_table(coincXML) 5507 coincMap = lsctables.CoincMapTable.get_table(coincXML) 5508 snglInsps = lsctables.SnglInspiralTable.get_table(coincXML) 5509 5510 if (trignum>len(coinc)): 5511 raise RuntimeError("Error: You asked for trigger %d, but %s contains only %d triggers" %(trignum,coincfile,len(tiggers))) 5512 else: 5513 coincEventID = coinc.getColumnByName('coinc_event_id')[trignum] 5514 eventIDs = [row.event_id for row in coincMap if row.coinc_event_id == coincEventID] 5515 triggers = [row for row in snglInsps if row.event_id in eventIDs] 5516 return triggers
5517
5518 #=============================================================================== 5519 # Parameter estimation codes results parser 5520 #=============================================================================== 5521 5522 -def find_ndownsample(samples, nDownsample):
5523 """ 5524 Given a list of files, threshold value, and a desired 5525 number of outputs posterior samples, return the skip number to 5526 achieve the desired number of posterior samples. 5527 """ 5528 if nDownsample is None: 5529 print "Max ACL(s):" 5530 splineParams=["spcal_npts", "spcal_active","constantcal_active"] 5531 for i in np.arange(25): 5532 for k in lal.cached_detector_by_prefix: 5533 splineParams.append(k+'_spcal_freq_'+str(i)) 5534 splineParams.append(k+'_spcal_logfreq_'+str(i)) 5535 5536 nonParams = ["logpost", "post", "cycle", "timestamp", "snrh1", "snrl1", "snrv1", 5537 "margtime","margtimephi","margtime","time_max","time_min", 5538 "time_mean", "time_maxl","sky_frame","psdscaleflag","logdeltaf","flow","f_ref", 5539 "lal_amporder","lal_pnorder","lal_approximant","tideo","spino","signalmodelflag", 5540 "temperature","nifo","nlocaltemps","ntemps","randomseed","samplerate","segmentlength","segmentstart", 5541 "t0", "phase_maxl", "azimuth", "cosalpha", "lal_amporder"] + logParams + snrParams + splineParams 5542 fixedParams = [p for p in samples.colnames if all(x==samples[p][0] for x in samples[p])] 5543 print "Fixed parameters: "+str(fixedParams) 5544 nonParams.extend(fixedParams) 5545 params = [p for p in samples.colnames if p.lower() not in nonParams] 5546 stride=np.diff(samples['cycle'])[0] 5547 results = np.array([np.array(effectiveSampleSize(samples[param])[:2]) for param in params]) 5548 nEffs = results[:,0] 5549 nEffective = min(nEffs) 5550 ACLs = results[:,1] 5551 maxACLind = np.argmax(ACLs) 5552 maxACL = ACLs[maxACLind] 5553 # Get index in header, which includes "non-params" 5554 print "%i (%s)." %(stride*maxACL,params[maxACLind]) 5555 5556 nskip = 1 5557 if nDownsample is not None: 5558 if len(samples) > nDownsample: 5559 nskip *= floor(len(samples)/nDownsample) 5560 nskip = int(nskip) 5561 else: 5562 nEff = nEffective 5563 if nEff > 1: 5564 if len(samples) > nEff: 5565 nskip = int(ceil(len(samples)/nEff)) 5566 else: 5567 nskip = np.nan 5568 return nskip
5569
5570 -class PEOutputParser(object):
5571 """ 5572 A parser for the output of Bayesian parameter estimation codes. 5573 5574 TODO: Will be abstract class when LDG moves over to Python >2.6, 5575 inherited by each method . 5576 """
5577 - def __init__(self,inputtype):
5578 if inputtype is 'mcmc_burnin': 5579 self._parser=self._mcmc_burnin_to_pos 5580 elif inputtype is 'ns': 5581 self._parser=self._ns_to_pos 5582 elif inputtype is 'common': 5583 self._parser=self._common_to_pos 5584 elif inputtype is 'fm': 5585 self._parser=self._followupmcmc_to_pos 5586 elif inputtype is "inf_mcmc": 5587 self._parser=self._infmcmc_to_pos 5588 elif inputtype is "xml": 5589 self._parser=self._xml_to_pos 5590 elif inputtype == 'hdf5': 5591 self._parser = self._hdf5_to_pos 5592 elif inputtype == 'hdf5s': 5593 self._parser = self._hdf5s_to_pos 5594 else: 5595 raise ValueError('Invalid value for "inputtype": %r' % inputtype)
5596
5597 - def parse(self,files,**kwargs):
5598 """ 5599 Parse files. 5600 """ 5601 return self._parser(files,**kwargs)
5602
5603 - def _infmcmc_to_pos(self,files,outdir=None,deltaLogP=None,fixedBurnins=None,nDownsample=None,oldMassConvention=False,**kwargs):
5604 """ 5605 Parser for lalinference_mcmcmpi output. 5606 """ 5607 if not (fixedBurnins is None): 5608 if not (deltaLogP is None): 5609 print "Warning: using deltaLogP criteria in addition to fixed burnin" 5610 if len(fixedBurnins) == 1 and len(files) > 1: 5611 print "Only one fixedBurnin criteria given for more than one output. Applying this to all outputs." 5612 fixedBurnins = np.ones(len(files),'int')*fixedBurnins[0] 5613 elif len(fixedBurnins) != len(files): 5614 raise RuntimeError("Inconsistent number of fixed burnin criteria and output files specified.") 5615 print "Fixed burning criteria: ",fixedBurnins 5616 else: 5617 fixedBurnins = np.zeros(len(files)) 5618 logPThreshold=-np.inf 5619 if not (deltaLogP is None): 5620 logPThreshold= - deltaLogP 5621 print "Eliminating any samples before log(Post) = ", logPThreshold 5622 nskips=self._find_ndownsample(files, logPThreshold, fixedBurnins, nDownsample) 5623 if nDownsample is None: 5624 print "Downsampling to take only uncorrelated posterior samples from each file." 5625 if len(nskips) == 1 and np.isnan(nskips[0]): 5626 print "WARNING: All samples in chain are correlated. Downsampling to 10000 samples for inspection!!!" 5627 nskips=self._find_ndownsample(files, logPThreshold, fixedBurnins, 10000) 5628 else: 5629 for i in range(len(nskips)): 5630 if np.isnan(nskips[i]): 5631 print "%s eliminated since all samples are correlated." 5632 else: 5633 print "Downsampling by a factor of ", nskips[0], " to achieve approximately ", nDownsample, " posterior samples" 5634 if outdir is None: 5635 outdir='' 5636 runfileName=os.path.join(outdir,"lalinfmcmc_headers.dat") 5637 postName="posterior_samples.dat" 5638 runfile=open(runfileName, 'w') 5639 outfile=open(postName, 'w') 5640 try: 5641 self._infmcmc_output_posterior_samples(files, runfile, outfile, logPThreshold, fixedBurnins, nskips, oldMassConvention) 5642 finally: 5643 runfile.close() 5644 outfile.close() 5645 return self._common_to_pos(open(postName,'r'))
5646 5647
5648 - def _infmcmc_output_posterior_samples(self, files, runfile, outfile, logPThreshold, fixedBurnins, nskips=None, oldMassConvention=False):
5649 """ 5650 Concatenate all the samples from the given files into outfile. 5651 For each file, only those samples past the point where the 5652 log(post) > logPThreshold are concatenated after eliminating 5653 fixedBurnin. 5654 """ 5655 nRead=0 5656 outputHeader=False 5657 acceptedChains=0 5658 if nskips is None: 5659 nskips = np.ones(len(files),'int') 5660 for infilename,i,nskip,fixedBurnin in zip(files,range(1,len(files)+1),nskips,fixedBurnins): 5661 infile=open(infilename,'r') 5662 try: 5663 print "Writing header of %s to %s"%(infilename,runfile.name) 5664 runInfo,header=self._clear_infmcmc_header(infile) 5665 runfile.write('Chain '+str(i)+':\n') 5666 runfile.writelines(runInfo) 5667 print "Processing file %s to %s"%(infilename,outfile.name) 5668 write_fref = False 5669 if 'f_ref' not in header: 5670 write_fref = True 5671 f_ref=self._find_infmcmc_f_ref(runInfo) 5672 if oldMassConvention: 5673 # Swap #1 for #2 because our old mass convention 5674 # has m2 > m1, while the common convention has m1 5675 # > m2 5676 header=[self._swaplabel12(label) for label in header] 5677 if not outputHeader: 5678 for label in header: 5679 outfile.write(label) 5680 outfile.write(" ") 5681 if write_fref: 5682 outfile.write("f_ref") 5683 outfile.write(" ") 5684 outfile.write("chain") 5685 outfile.write("\n") 5686 outputHeader=header 5687 iterindex=header.index("cycle") 5688 logpindex=header.index("logpost") 5689 output=False 5690 for line in infile: 5691 line=line.lstrip() 5692 lineParams=line.split() 5693 iter=int(lineParams[iterindex]) 5694 logP=float(lineParams[logpindex]) 5695 if (iter > fixedBurnin) and (logP >= logPThreshold): 5696 output=True 5697 if output: 5698 if nRead % nskip == 0: 5699 for label in outputHeader: 5700 # Note that the element "a1" in the 5701 # *header* actually already 5702 # corresponds to the "a2" *column* of 5703 # the input because we switched the 5704 # names above 5705 outfile.write(lineParams[header.index(label)]) 5706 outfile.write("\t") 5707 if write_fref: 5708 outfile.write(f_ref) 5709 outfile.write("\t") 5710 outfile.write(str(i)) 5711 outfile.write("\n") 5712 nRead=nRead+1 5713 if output: acceptedChains += 1 5714 finally: 5715 infile.close() 5716 print "%i of %i chains accepted."%(acceptedChains,len(files))
5717
5718 - def _swaplabel12(self, label):
5719 if label[-1] == '1': 5720 return label[0:-1] + '2' 5721 elif label[-1] == '2': 5722 return label[0:-1] + '1' 5723 else: 5724 return label[:]
5725
5726 - def _find_max_logP(self, files):
5727 """ 5728 Given a list of files, reads them, finding the maximum log(post) 5729 """ 5730 maxLogP = -np.inf 5731 for inpname in files: 5732 infile=open(inpname, 'r') 5733 try: 5734 runInfo,header=self._clear_infmcmc_header(infile) 5735 logpindex=header.index("logpost") 5736 for line in infile: 5737 line=line.lstrip().split() 5738 logP=float(line[logpindex]) 5739 if logP > maxLogP: 5740 maxLogP=logP 5741 finally: 5742 infile.close() 5743 print "Found max log(post) = ", maxLogP 5744 return maxLogP
5745
5746 - def _find_ndownsample(self, files, logPthreshold, fixedBurnins, nDownsample):
5747 """ 5748 Given a list of files, threshold value, and a desired 5749 number of outputs posterior samples, return the skip number to 5750 achieve the desired number of posterior samples. 5751 """ 5752 nfiles = len(files) 5753 ntots=[] 5754 nEffectives = [] 5755 if nDownsample is None: print "Max ACL(s):" 5756 for inpname,fixedBurnin in zip(files,fixedBurnins): 5757 infile = open(inpname, 'r') 5758 try: 5759 runInfo,header = self._clear_infmcmc_header(infile) 5760 header = [name.lower() for name in header] 5761 logpindex = header.index("logpost") 5762 iterindex = header.index("cycle") 5763 deltaLburnedIn = False 5764 fixedBurnedIn = False 5765 adapting = True 5766 lines=[] 5767 ntot=0 5768 for line in infile: 5769 line = line.lstrip().split() 5770 iter = int(line[iterindex]) 5771 logP = float(line[logpindex]) 5772 if iter > fixedBurnin: 5773 fixedBurnedIn = True 5774 # If adaptation reset, throw out what was collected so far 5775 elif fixedBurnedIn: 5776 fixedBurnedIn = False 5777 ntot = 0 5778 lines = [] 5779 if logP > logPthreshold: 5780 deltaLburnedIn = True 5781 if iter > 0: 5782 adapting = False 5783 if fixedBurnedIn and deltaLburnedIn and not adapting: 5784 ntot += 1 5785 lines.append(line) 5786 ntots.append(ntot) 5787 if nDownsample is None: 5788 try: 5789 splineParams=["spcal_npts", "spcal_active","constantcal_active"] 5790 for i in np.arange(5): 5791 for k in ['h1','l1']: 5792 splineParams.append(k+'_spcal_freq'+str(i)) 5793 splineParams.append(k+'_spcal_logfreq'+str(i)) 5794 5795 nonParams = ["logpost", "cycle", "timestamp", "snrh1", "snrl1", "snrv1", 5796 "margtime","margtimephi","margtime","time_max","time_min", 5797 "time_mean", "time_maxl","sky_frame","psdscaleflag","logdeltaf","flow","f_ref", 5798 "lal_amporder","lal_pnorder","lal_approximant","tideo","spino","signalmodelflag", 5799 "temperature","nifo","nlocaltemps","ntemps","randomseed","samplerate","segmentlength","segmentstart", 5800 "t0", "phase_maxl", "azimuth", "cosalpha"] + logParams + snrParams + splineParams 5801 nonParamsIdxs = [header.index(name) for name in nonParams if name in header] 5802 samps = np.array(lines).astype(float) 5803 fixedIdxs = np.where(np.amin(samps,axis=0)-np.amax(samps,axis=0) == 0.0)[0] 5804 nonParamsIdxs.extend(fixedIdxs) 5805 paramIdxs = [i for i in range(len(header)) if i not in nonParamsIdxs] 5806 stride=samps[1,iterindex] - samps[0,iterindex] 5807 results = np.array([np.array(effectiveSampleSize(samps[:,i])[:2]) for i in paramIdxs]) 5808 nEffs = results[:,0] 5809 nEffectives.append(min(nEffs)) 5810 ACLs = results[:,1] 5811 maxACLind = np.argmax(ACLs) 5812 maxACL = ACLs[maxACLind] 5813 # Get index in header, which includes "non-params" 5814 maxACLind = paramIdxs[maxACLind] 5815 print "%i (%s) for chain %s." %(stride*maxACL,header[maxACLind],inpname) 5816 except: 5817 nEffectives.append(None) 5818 print "Error computing effective sample size of %s!"%inpname 5819 5820 finally: 5821 infile.close() 5822 nskips = np.ones(nfiles) 5823 ntot = sum(ntots) 5824 if nDownsample is not None: 5825 if ntot > nDownsample: 5826 nskips *= int(floor(ntot/nDownsample)) 5827 else: 5828 for i in range(nfiles): 5829 nEff = nEffectives[i] 5830 ntot = ntots[i] 5831 if nEff > 1: 5832 if ntot > nEff: 5833 nskips[i] = int(ceil(ntot/nEff)) 5834 else: 5835 nskips[i] = None 5836 return nskips
5837
5838 - def _find_infmcmc_f_ref(self, runInfo):
5839 """ 5840 Searches through header to determine reference frequency of waveforms. 5841 If no fRef given, calls _find_infmcmc_f_lower to get the lower frequency 5842 bound, which is the default reference frequency for LALInference. 5843 """ 5844 fRef = None 5845 runInfoIter = iter(runInfo) 5846 for line in runInfoIter: 5847 headers=line.lstrip().lower().split() 5848 try: 5849 fRefColNum = headers.index('f_ref') # strings get converted to all lower case 5850 info = runInfoIter.next().lstrip().lower().split() 5851 fRef = info[-1]#fRefColNum] # too many column names with spaces for this way to work. I just grab the last value. Hopefully we will update to xml output files and those messy headers will be gone. 5852 break 5853 except ValueError: 5854 continue 5855 5856 # ***TEMPORARY*** If not in table, check command line. 5857 # ...This is messy, but the only option for dealing with old headers 5858 if not fRef: 5859 runInfoIter = iter(runInfo) 5860 for line in runInfoIter: 5861 headers=line.lstrip().lower().split() 5862 try: 5863 if headers[0]=="command": 5864 try: 5865 fRefInd = headers.index('--fref')+1 5866 fRef = headers[fRefInd] 5867 except ValueError: 5868 pass 5869 break 5870 except IndexError: 5871 continue 5872 5873 # If no fRef is found, use lower frequency bound 5874 if not fRef: 5875 fRef = self._find_infmcmc_f_lower(runInfo) 5876 5877 return fRef
5878
5879 - def _find_infmcmc_f_lower(self, runInfo):
5880 """ 5881 Searches through header to determine starting frequency of waveforms. 5882 Assumes same for all IFOs. 5883 """ 5884 runInfo = iter(runInfo) 5885 for line in runInfo: 5886 headers=line.lstrip().lower().split() 5887 try: 5888 flowColNum = headers.index('f_low') 5889 IFOinfo = runInfo.next().lstrip().lower().split() 5890 f_lower = IFOinfo[flowColNum] 5891 break 5892 except ValueError: 5893 continue 5894 return f_lower
5895
5896 - def _clear_infmcmc_header(self, infile):
5897 """ 5898 Reads lalinference_mcmcmpi file given, returning the run info and 5899 common output header information. 5900 """ 5901 runInfo = [] 5902 for line in infile: 5903 runInfo.append(line) 5904 headers=line.lstrip().lower().split() 5905 try: 5906 headers.index('cycle') 5907 break 5908 except ValueError: 5909 continue 5910 else: 5911 raise RuntimeError("couldn't find line with 'cycle' in LALInferenceMCMC input") 5912 return runInfo[:-1],headers
5913 5914
5915 - def _mcmc_burnin_to_pos(self,files,spin=False,deltaLogP=None):
5916 """ 5917 Parser for SPINspiral output . 5918 """ 5919 raise NotImplementedError 5920 if deltaLogP is not None: 5921 pos,bayesfactor=burnin(data,spin,deltaLogP,"posterior_samples.dat") 5922 return self._common_to_pos(open("posterior_samples.dat",'r'))
5923
5924 - def _ns_to_pos(self,files,Nlive=None,Npost=None,posfilename='posterior_samples.dat'):
5925 """ 5926 Parser for nested sampling output. 5927 files : list of input NS files 5928 Nlive : Number of live points 5929 Npost : Desired number of posterior samples 5930 posfilename : Posterior output file name (default: 'posterior_samples.dat') 5931 """ 5932 try: 5933 from lalapps.nest2pos import draw_N_posterior_many,draw_posterior_many 5934 except ImportError: 5935 print "Need lalapps.nest2pos to convert nested sampling output!" 5936 raise 5937 5938 if Nlive is None: 5939 raise RuntimeError("Need to specify number of live points in positional arguments of parse!") 5940 5941 #posfile.write('mchirp \t eta \t time \t phi0 \t dist \t RA \t dec \t 5942 #psi \t iota \t likelihood \n') 5943 # get parameter list 5944 it = iter(files) 5945 5946 # check if there's a file containing the parameter names 5947 parsfilename = (it.next()).strip('.gz')+'_params.txt' 5948 5949 if os.path.isfile(parsfilename): 5950 print 'Looking for '+parsfilename 5951 5952 if os.access(parsfilename,os.R_OK): 5953 5954 with open(parsfilename,'r') as parsfile: 5955 outpars=parsfile.readline()+'\n' 5956 else: 5957 raise RuntimeError('Cannot open parameters file %s!'%(parsfilename)) 5958 5959 else: # Use hardcoded CBC parameter names 5960 outpars='mchirp \t eta \t time \t phi0 \t dist \t RA \t \ 5961 dec \t psi \t iota \t logl \n' 5962 5963 # Find the logL column 5964 parsvec=outpars.split() 5965 logLcol=-1 5966 for i in range(len(parsvec)): 5967 if parsvec[i].lower()=='logl': 5968 logLcol=i 5969 if logLcol==-1: 5970 print 'Error! Could not find logL column in parameter list: %s'%(outpars) 5971 raise RuntimeError 5972 5973 inarrays=map(np.loadtxt,files) 5974 if Npost is None: 5975 pos=draw_posterior_many(inarrays,[Nlive for f in files],logLcols=[logLcol for f in files]) 5976 else: 5977 pos=draw_N_posterior_many(inarrays,[Nlive for f in files],Npost,logLcols=[logLcol for f in files]) 5978 5979 with open(posfilename,'w') as posfile: 5980 5981 posfile.write(outpars) 5982 5983 for row in pos: 5984 for i in row: 5985 posfile.write('%10.12e\t' %(i)) 5986 posfile.write('\n') 5987 5988 with open(posfilename,'r') as posfile: 5989 return_val=self._common_to_pos(posfile) 5990 5991 return return_val
5992
5993 - def _followupmcmc_to_pos(self,files):
5994 """ 5995 Parser for followupMCMC output. 5996 """ 5997 return self._common_to_pos(open(files[0],'r'),delimiter=',')
5998 5999
6000 - def _multinest_to_pos(self,files):
6001 """ 6002 Parser for MultiNest output. 6003 """ 6004 return self._common_to_pos(open(files[0],'r'))
6005
6006 - def _xml_to_pos(self,infile):
6007 """ 6008 Parser for VOTable XML Using 6009 """ 6010 from xml.etree import ElementTree as ET 6011 xmlns='http://www.ivoa.net/xml/VOTable/v1.1' 6012 try: 6013 register_namespace=ET.register_namespace 6014 except AttributeError: 6015 def register_namespace(prefix,uri): 6016 ET._namespace_map[uri]=prefix
6017 register_namespace('vot',xmlns) 6018 tree = ET.ElementTree() 6019 6020 tree.parse(infile) 6021 # Find the posterior table 6022 tables = tree.findall('.//{%s}TABLE'%(xmlns)) 6023 for table in tables: 6024 if table.get('utype')=='lalinference:results:posteriorsamples': 6025 return(self._VOTTABLE2pos(table)) 6026 for table in tables: 6027 if table.get('utype')=='lalinference:results:nestedsamples': 6028 nsresource=[node for node in tree.findall('{%s}RESOURCE'%(xmlns)) if node.get('utype')=='lalinference:results'][0] 6029 return(self._VOTTABLE2pos(vo_nest2pos(nsresource))) 6030 raise RuntimeError('Cannot find "Posterior Samples" TABLE element in XML input file %s'%(infile))
6031
6032 - def _VOTTABLE2pos(self,table):
6033 """ 6034 Parser for a VOT TABLE element with FIELDs and TABLEDATA elements 6035 """ 6036 from xml.etree import ElementTree as ET 6037 xmlns='http://www.ivoa.net/xml/VOTable/v1.1' 6038 try: 6039 register_namespace=ET.register_namespace 6040 except AttributeError: 6041 def register_namespace(prefix,uri): 6042 ET._namespace_map[uri]=prefix 6043 register_namespace('vot',xmlns)
6044 6045 header=[] 6046 for field in table.findall('./{%s}FIELD'%(xmlns)): 6047 header.append(field.attrib['name']) 6048 if(len(header)==0): 6049 raise RuntimeError('Unable to find FIELD nodes for table headers in XML table') 6050 data=table.findall('./{%s}DATA'%(xmlns)) 6051 tabledata=data[0].find('./{%s}TABLEDATA'%(xmlns)) 6052 llines=[] 6053 for row in tabledata: 6054 llines.append(np.array(map(lambda a:float(a.text),row))) 6055 flines=np.array(llines) 6056 for i in range(0,len(header)): 6057 if header[i].lower().find('log')!=-1 and header[i].lower() not in logParams and re.sub('log', '', header[i].lower()) not in [h.lower() for h in header]: 6058 print 'exponentiating %s'%(header[i]) 6059 6060 flines[:,i]=np.exp(flines[:,i]) 6061 6062 header[i]=re.sub('log', '', header[i], flags=re.IGNORECASE) 6063 if header[i].lower().find('sin')!=-1 and re.sub('sin', '', header[i].lower()) not in [h.lower() for h in header]: 6064 print 'asining %s'%(header[i]) 6065 flines[:,i]=np.arcsin(flines[:,i]) 6066 header[i]=re.sub('sin', '', header[i], flags=re.IGNORECASE) 6067 if header[i].lower().find('cos')!=-1 and re.sub('cos', '', header[i].lower()) not in [h.lower() for h in header]: 6068 print 'acosing %s'%(header[i]) 6069 flines[:,i]=np.arccos(flines[:,i]) 6070 header[i]=re.sub('cos', '', header[i], flags=re.IGNORECASE) 6071 header[i]=header[i].replace('(','') 6072 header[i]=header[i].replace(')','') 6073 print 'Read columns %s'%(str(header)) 6074 return header,flines 6075
6076 - def _hdf5s_to_pos(self, infiles, fixedBurnins=None, deltaLogP=None, nDownsample=None, tablename=None, **kwargs):
6077 from astropy.table import vstack 6078 6079 if fixedBurnins is None: 6080 fixedBurnins = np.zeros(len(infiles)) 6081 6082 if len(infiles) > 1: 6083 multiple_chains = True 6084 6085 chains = [] 6086 for i, [infile, fixedBurnin] in enumerate(zip(infiles, fixedBurnins)): 6087 chain = self._hdf5_to_table(infile, fixedBurnin=fixedBurnin, deltaLogP=deltaLogP, nDownsample=nDownsample, multiple_chains=multiple_chains, tablename=tablename, **kwargs) 6088 chain.add_column(astropy.table.Column(i*np.ones(len(chain)), name='chain')) 6089 chains.append(chain) 6090 6091 # Apply deltaLogP criteria across chains 6092 if deltaLogP is not None: 6093 logPThreshold = -np.inf 6094 for chain in chains: 6095 if len(chain) > 0: 6096 logPThreshold = max([logPThreshold, max(chain['logpost'])- deltaLogP]) 6097 print("Eliminating any samples before log(L) = {}".format(logPThreshold)) 6098 6099 for i, chain in enumerate(chains): 6100 if deltaLogP is not None: 6101 above_threshold = np.arange(len(chain))[chain['logpost'] > logPThreshold] 6102 burnin_idx = above_threshold[0] if len(above_threshold) > 0 else len(chain) 6103 else: 6104 burnin_idx = 0 6105 chains[i] = chain[burnin_idx:] 6106 6107 samples = vstack(chains) 6108 6109 # Downsample one more time 6110 if nDownsample is not None: 6111 nskip = find_ndownsample(samples, nDownsample) 6112 samples = samples[::nskip] 6113 6114 return samples.colnames, as_array(samples).view(float).reshape(-1, len(samples.columns))
6115
6116 - def _hdf5_to_table(self, infile, deltaLogP=None, fixedBurnin=None, nDownsample=None, multiple_chains=False, tablename=None, **kwargs):
6117 """ 6118 Parse a HDF5 file and return an array of posterior samples ad list of 6119 parameter names. Equivalent to '_common_to_pos' and work in progress. 6120 """ 6121 if not tablename: 6122 samples = read_samples(infile, tablename=lalinference.LALInferenceHDF5PosteriorSamplesDatasetName) 6123 else: 6124 samples = read_samples(infile, tablename=tablename) 6125 params = samples.colnames 6126 6127 for param in params: 6128 param_low = param.lower() 6129 if param_low.find('log') != -1 and param_low not in logParams and re.sub('log', '', param_low) not in [p.lower() for p in params]: 6130 print('exponentiating %s' % param) 6131 new_param = re.sub('log', '', param, flags=re.IGNORECASE) 6132 samples[new_param] = np.exp(samples[param]) 6133 del samples[param] 6134 param = new_param 6135 if param_low.find('sin') != -1 and re.sub('sin', '', param_low) not in [p.lower() for p in params]: 6136 print('asining %s' % param) 6137 new_param = re.sub('sin', '', param, flags=re.IGNORECASE) 6138 samples[new_param] = np.arcsin(samples[param]) 6139 del samples[param] 6140 param = new_param 6141 if param_low.find('cos') != -1 and re.sub('cos', '', param_low) not in [p.lower() for p in params]: 6142 print('acosing %s' % param) 6143 new_param = re.sub('cos', '', param, flags=re.IGNORECASE) 6144 samples[new_param] = np.arccos(samples[param]) 6145 del samples[param] 6146 param = new_param 6147 6148 if param != param.replace('(', ''): 6149 samples.rename_column(param, param.replace('(', '')) 6150 if param != param.replace(')', ''): 6151 samples.rename_column(param, param.replace(')', '')) 6152 6153 #Make everything a float, since that's what's excected of a CommonResultsObj 6154 replace_column(samples, param, samples[param].astype(float)) 6155 6156 params = samples.colnames 6157 print('Read columns %s' % str(params)) 6158 6159 # MCMC burnin and downsampling 6160 if 'cycle' in params: 6161 if not (fixedBurnin is None): 6162 if not (deltaLogP is None): 6163 print "Warning: using deltaLogP criteria in addition to fixed burnin" 6164 print "Fixed burning criteria: ",fixedBurnin 6165 else: 6166 fixedBurnin = 0 6167 6168 burned_in_cycles = np.arange(len(samples))[samples['cycle'] > fixedBurnin] 6169 burnin_idx = burned_in_cycles[0] if len(burned_in_cycles) > 0 else len(samples) 6170 samples = samples[burnin_idx:] 6171 6172 logPThreshold=-np.inf 6173 if len(samples) > 0 and not (deltaLogP is None): 6174 logPThreshold = max(samples['logpost'])- deltaLogP 6175 print "Eliminating any samples before log(post) = ", logPThreshold 6176 burnin_idx = np.arange(len(samples))[samples['logpost'] > logPThreshold][0] 6177 samples = samples[burnin_idx:] 6178 6179 if len(samples) > 0: 6180 nskip = find_ndownsample(samples, nDownsample) 6181 if nDownsample is None: 6182 print "Downsampling to take only uncorrelated posterior samples from each file." 6183 if np.isnan(nskip) and not multiple_chains: 6184 print "WARNING: All samples in chain are correlated. Downsampling to 10000 samples for inspection!!!" 6185 nskip = find_ndownsample(samples, 10000) 6186 samples = samples[::nskip] 6187 else: 6188 if np.isnan(nskip): 6189 print "WARNING: All samples in {} are correlated.".format(infile) 6190 samples = samples[-1:] 6191 else: 6192 print "Downsampling by a factor of ", nskip, " to achieve approximately ", nDownsample, " posterior samples" 6193 samples = samples[::nskip] 6194 6195 return samples
6196
6197 - def _hdf5_to_pos(self, infile, fixedBurnins=None, deltaLogP=None, nDownsample=None, tablename=None, **kwargs):
6198 samples = self._hdf5_to_table(infile, fixedBurnins=fixedBurnins, deltaLogP=deltaLogP, nDownsample=nDownsample, tablename=tablename, **kwargs) 6199 6200 return samples.colnames, as_array(samples).view(float).reshape(-1, len(samples.columns))
6201
6202 - def _common_to_pos(self,infile,info=[None,None]):
6203 """ 6204 Parse a file in the 'common format' and return an array of posterior 6205 samples and list of parameter names. Will apply inverse functions to 6206 columns with names containing sin,cos,log. 6207 """ 6208 6209 [headerfile,delimiter]=info 6210 6211 if headerfile==None: 6212 formatstr=infile.readline().lstrip() 6213 else: 6214 hf=open(headerfile,'r') 6215 formatstr=hf.readline().lstrip() 6216 hf.close() 6217 6218 formatstr=formatstr.replace('#','') 6219 formatstr=formatstr.replace('"','') 6220 6221 header=formatstr.split(delimiter) 6222 header[-1]=header[-1].rstrip('\n') 6223 nparams=len(header) 6224 llines=[] 6225 dec=re.compile(r'^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$|^inf$') 6226 6227 for line_number,line in enumerate(infile): 6228 sline=line.split(delimiter) 6229 if sline[-1] == '\n': 6230 del(sline[-1]) 6231 proceed=True 6232 if len(sline)<1: 6233 print 'Ignoring empty line in input file: %s'%(sline) 6234 proceed=False 6235 elif len(sline)!=nparams: 6236 sys.stderr.write('WARNING: Malformed row %i, read %i elements but there is meant to be %i\n'%(line_number,len(sline),nparams)) 6237 proceed=False 6238 6239 for elemn,st in enumerate(sline): 6240 s=st.replace('\n','') 6241 if dec.search(s) is None: 6242 print 'Warning! Ignoring non-numeric data after the header: %s. Row = %i,Element=%i'%(s,line_number,elemn) 6243 proceed=False 6244 elif s is '\n': 6245 proceed=False 6246 6247 if proceed: 6248 llines.append(map(float,sline)) 6249 6250 flines=np.array(llines) 6251 6252 if not flines.any(): 6253 raise RuntimeError("ERROR: no lines read in!") 6254 6255 6256 for i in range(0,len(header)): 6257 if header[i].lower().find('log')!=-1 and header[i].lower() not in logParams and re.sub('log', '', header[i].lower()) not in [h.lower() for h in header]: 6258 print 'exponentiating %s'%(header[i]) 6259 6260 flines[:,i]=np.exp(flines[:,i]) 6261 6262 header[i]=re.sub('log', '', header[i], flags=re.IGNORECASE) 6263 if header[i].lower().find('sin')!=-1 and re.sub('sin', '', header[i].lower()) not in [h.lower() for h in header]: 6264 print 'asining %s'%(header[i]) 6265 flines[:,i]=np.arcsin(flines[:,i]) 6266 header[i]=re.sub('sin', '', header[i], flags=re.IGNORECASE) 6267 if header[i].lower().find('cos')!=-1 and re.sub('cos', '', header[i].lower()) not in [h.lower() for h in header]: 6268 print 'acosing %s'%(header[i]) 6269 flines[:,i]=np.arccos(flines[:,i]) 6270 header[i]=re.sub('cos', '', header[i], flags=re.IGNORECASE) 6271 header[i]=header[i].replace('(','') 6272 header[i]=header[i].replace(')','') 6273 print 'Read columns %s'%(str(header)) 6274 return header,flines
6275
6276 6277 -def parse_converge_output_section(fo):
6278 result={} 6279 lines=fo.split('\n') 6280 chain_line=False 6281 for line in lines: 6282 6283 if '[1]' in line: 6284 key=line.replace('[1]','').strip(' ').strip('"') 6285 result[key]={} 6286 out=result[key] 6287 continue 6288 if result is not {}: 6289 if 'chain' in line: 6290 chain_line=True 6291 continue 6292 if chain_line: 6293 chain_line=False 6294 key=line.strip('"').split()[1] 6295 out[key]=[] 6296 out2=out[key] 6297 else: 6298 try: 6299 newline=line.strip('"').split() 6300 if newline is not []: 6301 out2.append(line.strip('"').split()) 6302 except: 6303 pass 6304 6305 return result
6306 #
6307 6308 -def vo_nest2pos(nsresource,Nlive=None):
6309 """ 6310 Parse a VO Table RESOURCE containing nested sampling output and 6311 return a VOTable TABLE element with posterior samples in it. 6312 This can be added to an existing tree by the user. 6313 Nlive will be read from the nsresource, unless specified 6314 """ 6315 from xml.etree import ElementTree as ET 6316 import copy 6317 from math import log, exp 6318 xmlns='http://www.ivoa.net/xml/VOTable/v1.1' 6319 try: 6320 register_namespace=ET.register_namespace 6321 except AttributeError: 6322 def register_namespace(prefix,uri): 6323 ET._namespace_map[uri]=prefix
6324 register_namespace('vot',xmlns) 6325 6326 postable=ET.Element("{%s}TABLE"%(xmlns),attrib={'name':'Posterior Samples','utype':'lalinference:results:posteriorsamples'}) 6327 i=0 6328 nstables=[resource for resource in nsresource.findall("./{%s}TABLE"%(xmlns)) if resource.get("utype")=="lalinference:results:nestedsamples"] 6329 6330 nstable=nstables[0] 6331 if Nlive is None: 6332 runstateResource = [resource for resource in nsresource.findall("./{%s}RESOURCE"%(xmlns)) if resource.get("utype")=="lalinference:state"][0] 6333 algTable = [table for table in runstateResource.findall("./{%s}TABLE"%(xmlns)) if table.get("utype")=="lalinference:state:algorithmparams"][0] 6334 Nlive = int ([param for param in algTable.findall("./{%s}PARAM"%(xmlns)) if param.get("name")=='Nlive'][0].get('value')) 6335 print 'Found Nlive %i'%(Nlive) 6336 if Nlive is None: 6337 raise RuntimeError("Cannot find number of live points in XML table, please specify") 6338 logLcol = None 6339 for fieldnode in nstable.findall('./{%s}FIELD'%xmlns): 6340 if fieldnode.get('name') == 'logL': 6341 logLcol=i 6342 i=i+1 6343 postable.append(copy.deepcopy(fieldnode)) 6344 for paramnode in nstable.findall('./{%s}PARAM'%(xmlns)): 6345 postable.append(copy.deepcopy(paramnode)) 6346 if logLcol is None: 6347 RuntimeError("Unable to find logL column") 6348 posdataNode=ET.Element("{%s}DATA"%(xmlns)) 6349 postabledataNode=ET.Element("{%s}TABLEDATA"%(xmlns)) 6350 postable.append(posdataNode) 6351 posdataNode.append(postabledataNode) 6352 nstabledata=nstable.find('./{%s}DATA/{%s}TABLEDATA'%(xmlns,xmlns)) 6353 logw=log(1.0 - exp(-1.0/float(Nlive))) 6354 weights=[] 6355 for row in nstabledata: 6356 logL=float(row[logLcol].text) 6357 weights.append(logL-logw) 6358 logw=logw-1.0/float(Nlive) 6359 mw=max(weights) 6360 weights = [w - mw for w in weights] 6361 for (row,weight) in zip(nstabledata,weights): 6362 if weight > log(random.random()): 6363 postabledataNode.append(copy.deepcopy(row)) 6364 return postable 6365 6366 xmlns='http://www.ivoa.net/xml/VOTable/v1.1'
6367 6368 -class VOT2HTML:
6369 - def __init__(self):
6370 self.html=htmlSection("VOTable information") 6371 self.skiptable=0
6372
6373 - def start(self,tag,attrib):
6374 if tag=='{%s}TABLE'%(xmlns): 6375 if attrib['utype']=='lalinference:results:nestedsamples'\ 6376 or attrib['utype']=='lalinference:results:posteriorsamples': 6377 self.skiptable=1 6378 else: 6379 self.skiptable=0 6380 self.tableouter=htmlChunk('div') 6381 self.tableouter.h2(attrib['name']) 6382 try: 6383 self.tableouter.p(attrib['utype']) 6384 except KeyError: 6385 pass 6386 self.fixedparams=htmlChunk('table',attrib={'class':'statstable'},parent=self.tableouter) 6387 self.table=htmlChunk('table',attrib={'class':'statstable'},parent=self.tableouter) 6388 self.tabheader=htmlChunk('tr',parent=self.table) 6389 if tag=='{%s}FIELD'%(xmlns): 6390 self.field=htmlChunk('th',{'name':attrib['name']},parent=self.tabheader) 6391 if tag=='{%s}TR'%(xmlns): 6392 self.tabrow=htmlChunk('tr',parent=self.table) 6393 if tag=='{%s}TD'%(xmlns): 6394 self.td=htmlChunk('td',parent=self.tabrow) 6395 if tag=='{%s}PARAM'%(xmlns): 6396 pnode=htmlChunk('tr',parent=self.fixedparams) 6397 namenode=htmlChunk('td',parent=pnode) 6398 namenode.p(attrib['name']) 6399 valnode=htmlChunk('td',parent=pnode) 6400 valnode.p(attrib['value'])
6401
6402 - def end(self,tag):
6403 if tag=='{%s}TABLE'%(xmlns): 6404 if not self.skiptable: 6405 self.html.append(self.tableouter._html) 6406 if tag=='{%s}FIELD'%(xmlns): 6407 self.field.p(self.data) 6408 if tag=='{%s}TD'%(xmlns): 6409 self.td.p(self.data)
6410
6411 - def data(self,data):
6412 self.data=data
6413
6414 - def close(self):
6415 return self.html.toprettyxml()
6416
6417 -def _cl_width(cl_bound):
6418 """Returns (high - low), the width of the given confidence 6419 bounds.""" 6420 6421 return cl_bound[1] - cl_bound[0]
6422
6423 -def _cl_count(cl_bound, samples):
6424 """Returns the number of samples within the given confidence 6425 bounds.""" 6426 6427 return np.sum((samples >= cl_bound[0]) & (samples <= cl_bound[1]))
6428
6429 -def confidence_interval_uncertainty(cl, cl_bounds, posteriors):
6430 """Returns a tuple (relative_change, fractional_uncertainty, 6431 percentile_uncertainty) giving the uncertainty in confidence 6432 intervals from multiple posteriors. 6433 6434 The uncertainty in the confidence intervals is the difference in 6435 length between the widest interval, formed from the smallest to 6436 largest values among all the cl_bounds, and the narrowest 6437 interval, formed from the largest-small and smallest-large values 6438 among all the cl_bounds. Note that neither the smallest nor the 6439 largest confidence intervals necessarily correspond to one of the 6440 cl_bounds. 6441 6442 The relative change relates the confidence interval uncertainty to 6443 the expected value of the parameter, the fractional uncertainty 6444 relates this length to the length of the confidence level from the 6445 combined posteriors, and the percentile uncertainty gives the 6446 change in percentile over the combined posterior between the 6447 smallest and largest confidence intervals. 6448 6449 @param cl The confidence level (between 0 and 1). 6450 6451 @param cl_bounds A list of (low, high) pairs giving the confidence 6452 interval associated with each posterior. 6453 6454 @param posteriors A list of PosteriorOneDPDF objects giving the 6455 posteriors.""" 6456 6457 Ns=[p.samples.shape[0] for p in posteriors] 6458 Nsamplers=len(Ns) 6459 6460 # Weight each sample within a run equally, and each run equally 6461 # with respect to the others 6462 all_samples = np.squeeze(np.concatenate([p.samples for p in posteriors], axis=0)) 6463 weights = np.squeeze(np.concatenate([p.samples*0.0+1.0/(Nsamplers*N) for (N,p) in zip(Ns,posteriors)], axis=0)) 6464 6465 isort=np.argsort(all_samples) 6466 6467 all_samples = all_samples[isort] 6468 weights = weights[isort] 6469 6470 param_mean = np.average(all_samples, weights=weights) 6471 6472 N=all_samples.shape[0] 6473 6474 alpha = (1.0 - cl)/2.0 6475 6476 wttotal = np.cumsum(weights) 6477 ilow = np.nonzero(wttotal >= alpha)[0][0] 6478 ihigh = np.nonzero(wttotal >= 1.0-alpha)[0][0] 6479 6480 all_cl_bound = (all_samples[ilow], all_samples[ihigh]) 6481 6482 low_bounds = np.array([l for (l,h) in cl_bounds]) 6483 high_bounds = np.array([h for (l,h) in cl_bounds]) 6484 6485 largest_cl_bound = (np.min(low_bounds), np.max(high_bounds)) 6486 smallest_cl_bound = (np.max(low_bounds), np.min(high_bounds)) 6487 6488 if smallest_cl_bound[1] < smallest_cl_bound[0]: 6489 # Then the smallest CL is NULL 6490 smallest_cl_bound = (0.0, 0.0) 6491 6492 ci_uncertainty = _cl_width(largest_cl_bound) - _cl_width(smallest_cl_bound) 6493 6494 relative_change = ci_uncertainty/param_mean 6495 6496 frac_uncertainty = ci_uncertainty/_cl_width(all_cl_bound) 6497 6498 quant_uncertainty = float(_cl_count(largest_cl_bound, all_samples) - _cl_count(smallest_cl_bound, all_samples))/float(N) 6499 6500 return (relative_change, frac_uncertainty, quant_uncertainty)
6501
6502 6503 -def plot_waveform(pos=None,siminspiral=None,event=0,path=None,ifos=['H1','L1','V1']):
6504 6505 from lalsimulation.lalsimulation import SimInspiralChooseTDWaveform,SimInspiralChooseFDWaveform 6506 from lalsimulation.lalsimulation import SimInspiralImplementedTDApproximants,SimInspiralImplementedFDApproximants 6507 from lal.lal import StrainUnit 6508 from lal.lal import CreateREAL8TimeSeries,CreateForwardREAL8FFTPlan,CreateTukeyREAL8Window,CreateCOMPLEX16FrequencySeries,DimensionlessUnit,REAL8TimeFreqFFT,CutREAL8TimeSeries 6509 from lal.lal import LIGOTimeGPS 6510 from lal.lal import MSUN_SI as LAL_MSUN_SI 6511 from lal.lal import PC_SI as LAL_PC_SI 6512 import lalsimulation as lalsim 6513 from pylal import antenna as ant 6514 from math import cos,sin,sqrt 6515 from glue.ligolw import lsctables 6516 from glue.ligolw import utils 6517 import os 6518 import numpy as np 6519 from numpy import arange 6520 from pylal import bayespputils as bppu 6521 from matplotlib import pyplot as plt,cm as mpl_cm,lines as mpl_lines 6522 import copy 6523 if path is None: 6524 path=os.getcwd() 6525 if event is None: 6526 event=0 6527 colors_inj={'H1':'r','L1':'g','V1':'m','I1':'b','J1':'y'} 6528 colors_rec={'H1':'k','L1':'k','V1':'k','I1':'k','J1':'k'} 6529 # time and freq data handling variables 6530 srate=4096.0 6531 seglen=60. 6532 length=srate*seglen # lenght of 60 secs, hardcoded. May call a LALSimRoutine to get an idea 6533 deltaT=1/srate 6534 deltaF = 1.0 / (length* deltaT); 6535 6536 # build window for FFT 6537 pad=0.4 6538 timeToFreqFFTPlan = CreateForwardREAL8FFTPlan(int(length), 1 ); 6539 window=CreateTukeyREAL8Window(int(length),2.0*pad*srate/length); 6540 WinNorm = sqrt(window.sumofsquares/window.data.length); 6541 # time and freq domain strain: 6542 segStart=100000000 6543 strainT=CreateREAL8TimeSeries("strainT",segStart,0.0,1.0/srate,DimensionlessUnit,int(length)); 6544 strainF= CreateCOMPLEX16FrequencySeries("strainF",segStart, 0.0, deltaF, DimensionlessUnit,int(length/2. +1)); 6545 6546 f_min=25 # hardcoded default (may be changed below) 6547 f_ref=100 # hardcoded default (may be changed below) 6548 f_max=srate/2.0 6549 plot_fmax=f_max 6550 6551 inj_strains=dict((i,{"T":{'x':None,'strain':None},"F":{'x':None,'strain':None}}) for i in ifos) 6552 rec_strains=dict((i,{"T":{'x':None,'strain':None},"F":{'x':None,'strain':None}}) for i in ifos) 6553 6554 inj_domain=None 6555 rec_domain=None 6556 font_size=26 6557 if siminspiral is not None: 6558 skip=0 6559 try: 6560 xmldoc = utils.load_filename(siminspiral,contenthandler=ExtractSimInspiralTableLIGOLWContentHandler) 6561 tbl = lsctables.table.get_table(xmldoc, "sim_inspiral") 6562 if event>0: 6563 tbl=tbl[event] 6564 else: 6565 tbl=tbl[0] 6566 except: 6567 print "Cannot read event %s from table %s. Won't plot injected waveform \n"%(event,siminspiral) 6568 skip=1 6569 if not skip: 6570 REAL8time=tbl.geocent_end_time+1e-9*tbl.geocent_end_time_ns 6571 GPStime=LIGOTimeGPS(REAL8time) 6572 M1=tbl.mass1 6573 M2=tbl.mass2 6574 D=tbl.distance 6575 m1=M1*LAL_MSUN_SI 6576 m2=M2*LAL_MSUN_SI 6577 phiRef=tbl.coa_phase 6578 6579 f_min = tbl.f_lower 6580 s1x = tbl.spin1x 6581 s1y = tbl.spin1y 6582 s1z = tbl.spin1z 6583 s2x = tbl.spin2x 6584 s2y = tbl.spin2y 6585 s2z = tbl.spin2z 6586 6587 r=D*LAL_PC_SI*1.0e6 6588 iota=tbl.inclination 6589 print "WARNING: Defaulting to inj_fref =100Hz to plot the injected WF. This is hardcoded since xml table does not carry this information\n" 6590 6591 lambda1=0 6592 lambda2=0 6593 waveFlags=None 6594 nonGRparams=None 6595 wf=str(tbl.waveform) 6596 6597 injapproximant=lalsim.GetApproximantFromString(wf) 6598 amplitudeO=int(tbl.amp_order ) 6599 phaseO=lalsim.GetOrderFromString(wf) 6600 6601 ra=tbl.longitude 6602 dec=tbl.latitude 6603 psi=tbl.polarization 6604 6605 if SimInspiralImplementedFDApproximants(injapproximant): 6606 inj_domain='F' 6607 [plus,cross]=SimInspiralChooseFDWaveform(phiRef, deltaF, m1, m2, s1x, s1y, s1z,s2x,s2y,s2z,f_min,f_max, f_ref, r, iota, lambda1, lambda2,waveFlags, nonGRparams, amplitudeO, phaseO, injapproximant) 6608 elif SimInspiralImplementedTDApproximants(injapproximant): 6609 inj_domain='T' 6610 [plus,cross]=SimInspiralChooseTDWaveform(phiRef, deltaT, m1, m2, s1x, s1y, s1z,s2x,s2y,s2z,f_min, f_ref, r, iota, lambda1, lambda2,waveFlags, nonGRparams, amplitudeO, phaseO, injapproximant) 6611 else: 6612 print "\nThe approximant %s doesn't seem to be recognized by lalsimulation!\n Skipping WF plots\n"%injapproximant 6613 return None 6614 6615 for ifo in ifos: 6616 (fp,fc,fa,qv)=ant.response(REAL8time,ra,dec,iota,psi,'radians',ifo) 6617 if inj_domain=='T': 6618 # strain is a temporary container for this IFO strain. 6619 # Take antenna pattern into accout and window the data 6620 for k in np.arange(strainT.data.length): 6621 if k<plus.data.length: 6622 strainT.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 6623 else: 6624 strainT.data.data[k]=0.0 6625 strainT.data.data[k]*=window.data.data[k] 6626 # now copy in the dictionary only the part of strain which is not null (that is achieved using plus.data.length as length) 6627 inj_strains[ifo]["T"]['strain']=np.array([strainT.data.data[k] for k in arange(plus.data.length)]) 6628 inj_strains[ifo]["T"]['x']=np.array([REAL8time - deltaT*(plus.data.length-1-k) for k in np.arange(plus.data.length)]) 6629 6630 # Take the FFT 6631 for j in arange(strainF.data.length): 6632 strainF.data.data[j]=0.0 6633 REAL8TimeFreqFFT(strainF,strainT,timeToFreqFFTPlan); 6634 for j in arange(strainF.data.length): 6635 strainF.data.data[j]/=WinNorm 6636 # copy in the dictionary 6637 inj_strains[ifo]["F"]['strain']=np.array([strainF.data.data[k] for k in arange(int(strainF.data.length))]) 6638 inj_strains[ifo]["F"]['x']=np.array([strainF.f0+ k*strainF.deltaF for k in arange(int(strainF.data.length))]) 6639 elif inj_domain=='F': 6640 for k in np.arange(strainF.data.length): 6641 if k<plus.data.length: 6642 strainF.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 6643 else: 6644 strainF.data.data[k]=0.0 6645 # copy in the dictionary 6646 inj_strains[ifo]["F"]['strain']=np.array([strainF.data.data[k] for k in arange(int(strainF.data.length))]) 6647 inj_strains[ifo]["F"]['x']=np.array([strainF.f0+ k*strainF.deltaF for k in arange(int(strainF.data.length))]) 6648 if pos is not None: 6649 6650 # Select the maxP sample 6651 _,which=pos._posMap() 6652 6653 if 'time' in pos.names: 6654 REAL8time=pos['time'].samples[which][0] 6655 elif 'time_maxl' in pos.names: 6656 REAL8time=pos['time_maxl'].samples[which][0] 6657 elif 'time_min' in pos.names and 'time_max' in pos.names: 6658 REAL8time=pos['time_min'].samples[which][0]+0.5*(pos['time_max'].samples[which][0]-pos['time_min'].samples[which][0]) 6659 else: 6660 print "ERROR: could not find any time parameter in the posterior file. Not plotting the WF...\n" 6661 return None 6662 6663 # first check we have approx in posterior samples, otherwise skip 6664 skip=0 6665 try: 6666 approximant=int(pos['LAL_APPROXIMANT'].samples[which][0]) 6667 amplitudeO=int(pos['LAL_AMPORDER'].samples[which][0]) 6668 phaseO=int(pos['LAL_PNORDER'].samples[which][0]) 6669 except: 6670 skip=1 6671 if skip==0: 6672 GPStime=LIGOTimeGPS(REAL8time) 6673 6674 q=pos['q'].samples[which][0] 6675 mc=pos['mc'].samples[which][0] 6676 M1,M2=bppu.q2ms(mc,q) 6677 if 'dist' in pos.names: 6678 D=pos['dist'].samples[which][0] 6679 elif 'distance' in pos.names: 6680 D=pos['distance'].samples[which][0] 6681 elif 'logdistance' in pos.names: 6682 D=exp(pos['distance'].samples[which][0]) 6683 6684 6685 m1=M1*LAL_MSUN_SI 6686 m2=M2*LAL_MSUN_SI 6687 if 'phi_orb' in pos.names: 6688 phiRef=pos['phi_orb'].samples[which][0] 6689 elif 'phase_maxl' in pos.names: 6690 phiRef=pos['phase_maxl'].samples[which][0] 6691 print 'INFO: phi_orb not estimated, using maximum likelihood value' 6692 else: 6693 print 'WARNING: phi_orb not found in posterior files. Defaulting to 0.0 which is probably *not* what you want\n' 6694 phiRef=0.0 6695 6696 try: 6697 for name in ['flow','f_lower']: 6698 if name in pos.names: 6699 f_min=pos[name].samples[which][0] 6700 except: 6701 pass 6702 6703 try: 6704 for name in ['fref','f_ref','f_Ref','fRef']: 6705 if name in pos.names: 6706 fname=name 6707 6708 Fref = np.unique(pos[fname].samples) 6709 if len(Fref) > 1: 6710 print "ERROR: Expected f_ref to be constant for all samples. Can't tell which value was injected! Defaulting to 100 Hz\n" 6711 print Fref 6712 else: 6713 f_ref = Fref[0] 6714 except ValueError: 6715 print "WARNING: Could not read fref from posterior file! Defaulting to 100 Hz\n" 6716 6717 try: 6718 a = pos['a1'].samples[which][0] 6719 the = pos['theta_spin1'].samples[which][0] 6720 phi = pos['phi_spin1'].samples[which][0] 6721 s1x = (a * sin(the) * cos(phi)); 6722 s1y = (a * sin(the) * sin(phi)); 6723 s1z = (a * cos(the)); 6724 a = pos['a2'].samples[which][0] 6725 the = pos['theta_spin2'].samples[which][0] 6726 phi = pos['phi_spin2'].samples[which][0] 6727 s2x = (a * sin(the) * cos(phi)); 6728 s2y = (a * sin(the) * sin(phi)); 6729 s2z = (a * cos(the)); 6730 iota=pos['inclination'].samples[which][0] 6731 except: 6732 try: 6733 iota, s1x, s1y, s1z, s2x, s2y, s2z=lalsim.SimInspiralTransformPrecessingNewInitialConditions(pos['theta_jn'].samples[which][0], pos['phi_JL'].samples[which][0], pos['tilt1'].samples[which][0], pos['tilt2'].samples[which][0], pos['phi12'].samples[which][0], pos['a1'].samples[which][0], pos['a2'].samples[which][0], m1, m2, f_ref) 6734 except: 6735 if 'a1z' in pos.names: 6736 s1z=pos['a1z'].samples[which][0] 6737 elif 'a1' in pos.names: 6738 s1z=pos['a1'].samples[which][0] 6739 else: 6740 s1z=0 6741 if 'a2z' in pos.names: 6742 s2z=pos['a2z'].samples[which][0] 6743 elif 'a2' in pos.names: 6744 s2z=pos['a2'].samples[which][0] 6745 else: 6746 s2z=0 6747 s1x=s1y=s2x=s2y=0.0 6748 if 'inclination' in pos.names: 6749 iota=pos['inclination'].samples[which][0] 6750 else: 6751 iota=pos['theta_jn'].samples[which][0] 6752 6753 r=D*LAL_PC_SI*1.0e6 6754 6755 lambda1=0 6756 lambda2=0 6757 waveFlags=None 6758 nonGRparams=None 6759 approximant=int(pos['LAL_APPROXIMANT'].samples[which][0]) 6760 amplitudeO=int(pos['LAL_AMPORDER'].samples[which][0]) 6761 phaseO=int(pos['LAL_PNORDER'].samples[which][0]) 6762 6763 if SimInspiralImplementedFDApproximants(approximant): 6764 rec_domain='F' 6765 [plus,cross]=SimInspiralChooseFDWaveform(phiRef, deltaF, m1, m2, s1x, s1y, s1z,s2x,s2y,s2z,f_min, f_max, f_ref,r, iota, lambda1, lambda2,waveFlags, nonGRparams, amplitudeO, phaseO, approximant) 6766 elif SimInspiralImplementedTDApproximants(approximant): 6767 rec_domain='T' 6768 [plus,cross]=SimInspiralChooseTDWaveform(phiRef, deltaT, m1, m2, s1x, s1y, s1z,s2x,s2y,s2z,f_min, f_ref, r, iota, lambda1, lambda2,waveFlags, nonGRparams, amplitudeO, phaseO, approximant) 6769 else: 6770 print "The approximant %s doesn't seem to be recognized by lalsimulation!\n Skipping WF plots\n"%approximant 6771 return None 6772 6773 ra=pos['ra'].samples[which][0] 6774 dec=pos['dec'].samples[which][0] 6775 psi=pos['psi'].samples[which][0] 6776 fs={} 6777 for ifo in ifos: 6778 (fp,fc,fa,qv)=ant.response(REAL8time,ra,dec,iota,psi,'radians',ifo) 6779 if rec_domain=='T': 6780 # strain is a temporary container for this IFO strain. 6781 # Take antenna pattern into accout and window the data 6782 for k in np.arange(strainT.data.length): 6783 if k<plus.data.length: 6784 strainT.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 6785 else: 6786 strainT.data.data[k]=0.0 6787 strainT.data.data[k]*=window.data.data[k] 6788 # now copy in the dictionary only the part of strain which is not null (that is achieved using plus.data.length as length) 6789 rec_strains[ifo]["T"]['strain']=np.array([strainT.data.data[k] for k in arange(plus.data.length)]) 6790 rec_strains[ifo]["T"]['x']=np.array([REAL8time - deltaT*(plus.data.length-1-k) for k in np.arange(plus.data.length)]) 6791 6792 # Take the FFT 6793 for j in arange(strainF.data.length): 6794 strainF.data.data[j]=0.0 6795 REAL8TimeFreqFFT(strainF,strainT,timeToFreqFFTPlan); 6796 for j in arange(strainF.data.length): 6797 strainF.data.data[j]/=WinNorm 6798 # copy in the dictionary 6799 rec_strains[ifo]["F"]['strain']=np.array([strainF.data.data[k] for k in arange(int(strainF.data.length))]) 6800 rec_strains[ifo]["F"]['x']=np.array([strainF.f0+ k*strainF.deltaF for k in arange(int(strainF.data.length))]) 6801 elif rec_domain=='F': 6802 for k in np.arange(strainF.data.length): 6803 if k<plus.data.length: 6804 strainF.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 6805 else: 6806 strainF.data.data[k]=0.0 6807 # copy in the dictionary 6808 rec_strains[ifo]["F"]['strain']=np.array([strainF.data.data[k] for k in arange(int(strainF.data.length))]) 6809 rec_strains[ifo]["F"]['x']=np.array([strainF.f0+ k*strainF.deltaF for k in arange(int(strainF.data.length))]) 6810 6811 myfig=plt.figure(1,figsize=(23,15)) 6812 6813 rows=len(ifos) 6814 cols=2 6815 6816 #this variables decide which domain will be plotted on the left column of the plot. 6817 # only plot Time domain if both injections and recovery are TD 6818 global_domain="F" 6819 if rec_domain is not None and inj_domain is not None: 6820 if rec_domain=="T" and inj_domain=="T": 6821 global_domain="T" 6822 elif rec_domain is not None: 6823 if rec_domain=="T": 6824 global_domain="T" 6825 elif inj_domain is not None: 6826 if inj_domain=="T": 6827 global_domain="T" 6828 6829 A,axes=plt.subplots(nrows=rows,ncols=cols,sharex=False,sharey=False) 6830 plt.setp(A,figwidth=23,figheight=15) 6831 for (r,i) in zip(np.arange(rows),ifos): 6832 for c in np.arange(cols): 6833 ax=axes[r] 6834 if type(ax)==np.ndarray: 6835 ax=ax[c] 6836 else: 6837 ax=axes[c] 6838 if rec_strains[i]["T"]['strain'] is not None or rec_strains[i]["F"]['strain'] is not None: 6839 if c==0: 6840 if global_domain=="T": 6841 ax.plot(rec_strains[i]["T"]['x'],rec_strains[i]["T"]['strain'],colors_rec[i],alpha=0.5,label='%s maP'%i) 6842 else: 6843 data=rec_strains[i]["F"]['strain'] 6844 f=rec_strains[i]["F"]['x'] 6845 mask=np.logical_and(f>=f_min,f<=plot_fmax) 6846 ys=data 6847 ax.semilogx(f[mask],ys[mask].real,'.-',color=colors_rec[i],alpha=0.5,label='%s maP'%i) 6848 else: 6849 data=rec_strains[i]["F"]['strain'] 6850 f=rec_strains[i]["F"]['x'] 6851 mask=np.logical_and(f>=f_min,f<=plot_fmax) 6852 ys=data 6853 ax.loglog(f[mask],abs(ys[mask]),'--',color=colors_rec[i],alpha=0.5,linewidth=4) 6854 ax.set_xlim([min(f[mask]),max(f[mask])]) 6855 ax.grid(True,which='both') 6856 if inj_strains[i]["T"]['strain'] is not None or inj_strains[i]["F"]['strain'] is not None: 6857 if c==0: 6858 if global_domain=="T": 6859 ax.plot(inj_strains[i]["T"]['x'],inj_strains[i]["T"]['strain'],colors_inj[i],alpha=0.5,label='%s inj'%i) 6860 else: 6861 data=inj_strains[i]["F"]['strain'] 6862 f=inj_strains[i]["F"]['x'] 6863 mask=np.logical_and(f>=f_min,f<=plot_fmax) 6864 ys=data 6865 ax.plot(f[mask],ys[mask].real,'.-',color=colors_inj[i],alpha=0.5,label='%s inj'%i) 6866 else: 6867 data=inj_strains[i]["F"]['strain'] 6868 f=inj_strains[i]["F"]['x'] 6869 mask=np.logical_and(f>=f_min,f<=plot_fmax) 6870 ys=data 6871 ax.loglog(f[mask],abs(ys[mask]),'--',color=colors_inj[i],alpha=0.5,linewidth=4) 6872 ax.set_xlim([min(f[mask]),max(f[mask])]) 6873 ax.grid(True,which='both') 6874 6875 if r==0: 6876 if c==0: 6877 if global_domain=="T": 6878 ax.set_title(r"$h(t)$",fontsize=font_size) 6879 else: 6880 ax.set_title(r"$\Re[h(f)]$",fontsize=font_size) 6881 else: 6882 ax.set_title(r"$|h(f)|$",fontsize=font_size) 6883 elif r==rows-1: 6884 if c==0: 6885 if global_domain=="T": 6886 ax.set_xlabel("time [s]",fontsize=font_size) 6887 else: 6888 ax.set_xlabel("frequency [Hz]",fontsize=font_size) 6889 else: 6890 ax.set_xlabel("frequency [Hz]",fontsize=font_size) 6891 6892 ax.legend(loc='best') 6893 ax.grid(True) 6894 6895 #ax.tight_layout() 6896 A.savefig(os.path.join(path,'WF_DetFrame.png'),bbox_inches='tight') 6897 return inj_strains,rec_strains
6898
6899 6900 -def plot_psd(psd_files,outpath=None,f_min=30.):
6901 myfig2=plt.figure(figsize=(15,15),dpi=500) 6902 ax=plt.subplot(1,1,1) 6903 colors={'H1':'r','L1':'g','V1':'m','I1':'k','J1':'y'} 6904 6905 if outpath is None: 6906 outpath=os.getcwd() 6907 tmp=[] 6908 for f in psd_files: 6909 if not os.path.isfile(f): 6910 print "PSD file %s has not been found and won't be plotted\n"%f 6911 else: 6912 tmp.append(f) 6913 if tmp==[]: 6914 return None 6915 else: 6916 psd_files=tmp 6917 6918 freqs = {} 6919 for f in psd_files: 6920 data=np.loadtxt(f) 6921 freq=data[:,0] 6922 data=data[:,1] 6923 idx=f.find('-PSD.dat') 6924 ifo=f[idx-2:idx] 6925 freqs[ifo.lower()] = freq 6926 fr=[] 6927 da=[] 6928 for (f,d) in zip(freq,data): 6929 if f>f_min and d!=0.0 and np.isfinite(d): 6930 fr.append(f) 6931 da.append(d) 6932 plt.loglog(fr,da,colors[ifo],label=ifo,alpha=0.5,linewidth=3) 6933 plt.xlim([min(fr),max(fr)]) 6934 plt.xlabel("Frequency [Hz]",fontsize=26) 6935 plt.ylabel("PSD",fontsize=26) 6936 plt.legend(loc='best') 6937 plt.grid(which='both') 6938 try: 6939 plt.tight_layout() 6940 myfig2.savefig(os.path.join(outpath,'PSD.png'),bbox_inches='tight') 6941 except: 6942 myfig2.savefig(os.path.join(outpath,'PSD.png')) 6943 myfig2.clf() 6944 6945 return freqs
6946 6947 cred_level = lambda cl, x: np.sort(x, axis=0)[int(cl*len(x))]
6948 6949 -def cred_interval(x, cl=.9, lower=True):
6950 """Return location of lower or upper confidence levels 6951 Args: 6952 x: List of samples. 6953 cl: Confidence level to return the bound of. 6954 lower: If ``True``, return the lower bound, otherwise return the upper bound. 6955 """ 6956 if lower: 6957 return cred_level((1.-cl)/2, x) 6958 else: 6959 return cred_level((1.+cl)/2, x)
6960
6961 -def spline_angle_xform(delta_psi):
6962 """Returns the angle in degrees corresponding to the spline 6963 calibration parameters delta_psi. 6964 6965 """ 6966 rot = (2.0 + 1.0j*delta_psi)/(2.0 - 1.0j*delta_psi) 6967 6968 return 180.0/np.pi*np.arctan2(np.imag(rot), np.real(rot))
6969
6970 -def plot_spline_pos(logf, ys, nf=100, level=0.9, color='k', label=None, xform=None):
6971 """Plot calibration posterior estimates for a spline model in log space. 6972 Args: 6973 logf: The (log) location of spline control points. 6974 ys: List of posterior draws of function at control points ``logf`` 6975 nx: Number of points to evaluate spline at for plotting. 6976 level: Credible level to fill in. 6977 color: Color to plot with. 6978 label: Label for plot. 6979 xform: Function to transform the spline into plotted values. 6980 """ 6981 f = np.exp(logf) 6982 fs = np.linspace(f.min(), f.max(), nf) 6983 6984 data = np.zeros((ys.shape[0], nf)) 6985 6986 if xform is None: 6987 zs = ys 6988 else: 6989 zs = xform(ys) 6990 6991 mu = np.mean(zs, axis=0) 6992 lower_cl = mu - cred_interval(zs, level, lower=True) 6993 upper_cl = cred_interval(zs, level, lower=False) - mu 6994 plt.errorbar(np.exp(logf), mu, yerr=[lower_cl, upper_cl], fmt='.', color=color, lw=4, alpha=0.5, capsize=0) 6995 6996 for i, samp in enumerate(ys): 6997 temp = interpolate.spline(logf, samp, np.log(fs)) 6998 if xform is None: 6999 data[i] = temp 7000 else: 7001 data[i] = xform(temp) 7002 7003 line, = plt.plot(fs, np.mean(data, axis=0), color=color, label=label) 7004 color = line.get_color() 7005 plt.fill_between(fs, cred_interval(data, level), cred_interval(data, level, lower=False), color=color, alpha=.1, linewidth=0.1) 7006 plt.xlim(f.min()-.5, f.max()+50)
7007
7008 -def plot_calibration_pos(pos, level=.9, outpath=None):
7009 fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(15, 15), dpi=500) 7010 7011 font_size = 32 7012 if outpath is None: 7013 outpath=os.getcwd() 7014 7015 params = pos.names 7016 ifos = np.unique([param.split('_')[0] for param in params if 'spcal_freq' in param]) 7017 for ifo in ifos: 7018 if ifo=='h1': color = 'r' 7019 elif ifo=='l1': color = 'g' 7020 elif ifo=='v1': color = 'm' 7021 else: color = 'c' 7022 7023 # Assume spline control frequencies are constant 7024 freq_params = np.sort([param for param in params if 7025 '{0}_spcal_freq'.format(ifo) in param]) 7026 7027 logfreqs = np.log([pos[param].median for param in freq_params]) 7028 7029 # Amplitude calibration model 7030 plt.sca(ax1) 7031 amp_params = np.sort([param for param in params if 7032 '{0}_spcal_amp'.format(ifo) in param]) 7033 if len(amp_params) > 0: 7034 amp = 100*np.column_stack([pos[param].samples for param in amp_params]) 7035 plot_spline_pos(logfreqs, amp, color=color, level=level, label="{0} (mean, {1}%)".format(ifo.upper(), int(level*100))) 7036 7037 # Phase calibration model 7038 plt.sca(ax2) 7039 phase_params = np.sort([param for param in params if 7040 '{0}_spcal_phase'.format(ifo) in param]) 7041 if len(phase_params) > 0: 7042 phase = np.column_stack([pos[param].samples for param in phase_params]) 7043 7044 plot_spline_pos(logfreqs, phase, color=color, level=level, label="{0} (mean, {1}%)".format(ifo.upper(), int(level*100)), xform=spline_angle_xform) 7045 7046 ax1.tick_params(labelsize=.75*font_size) 7047 ax2.tick_params(labelsize=.75*font_size) 7048 try: 7049 plt.legend(loc='upper right', prop={'size':.75*font_size}, framealpha=0.1) 7050 except: 7051 plt.legend(loc='upper right', prop={'size':.75*font_size}) 7052 ax1.set_xscale('log') 7053 ax2.set_xscale('log') 7054 7055 ax2.set_xlabel('Frequency (Hz)', fontsize=font_size) 7056 ax1.set_ylabel('Amplitude (%)', fontsize=font_size) 7057 ax2.set_ylabel('Phase (deg)', fontsize=font_size) 7058 7059 outp = os.path.join(outpath, 'calibration.png') 7060 try: 7061 fig.tight_layout() 7062 fig.savefig(outp, bbox_inches='tight') 7063 except: 7064 fig.savefig(outp) 7065 plt.close(fig)
7066
7067 7068 -def plot_burst_waveform(pos=None,simburst=None,event=0,path=None,ifos=['H1','L1','V1']):
7069 7070 from lalinference.lalinference import SimBurstChooseFDWaveform,SimBurstChooseTDWaveform 7071 from lalinference.lalinference import SimBurstImplementedFDApproximants,SimBurstImplementedTDApproximants 7072 from lal.lal import StrainUnit 7073 from lal.lal import CreateREAL8TimeSeries,CreateForwardREAL8FFTPlan,CreateTukeyREAL8Window,CreateCOMPLEX16FrequencySeries,DimensionlessUnit,REAL8TimeFreqFFT,CutREAL8TimeSeries,ResampleREAL8TimeSeries,CreateReverseREAL8FFTPlan,REAL8FreqTimeFFT 7074 from lal.lal import LIGOTimeGPS 7075 from lal.lal import MSUN_SI as LAL_MSUN_SI 7076 from lal.lal import PC_SI as LAL_PC_SI 7077 import lalsimulation as lalsim 7078 import lalinference as lalinf 7079 from pylal import antenna as ant 7080 from math import cos,sin,sqrt 7081 from glue.ligolw import lsctables 7082 from glue.ligolw import utils 7083 import os 7084 import numpy as np 7085 from numpy import arange,real,imag,absolute,fabs,pi 7086 from pylal import bayespputils as bppu 7087 from matplotlib import pyplot as plt,cm as mpl_cm,lines as mpl_lines 7088 import copy 7089 if path is None: 7090 path=os.getcwd() 7091 if event is None: 7092 event=0 7093 colors_inj={'H1':'r','L1':'g','V1':'m','I1':'b','J1':'y'} 7094 colors_rec={'H1':'k','L1':'k','V1':'k','I1':'k','J1':'k'} 7095 #import sim inspiral table content handler 7096 from glue.ligolw import ligolw 7097 from glue.ligolw import lsctables 7098 from glue.ligolw import table 7099 from glue.ligolw import utils 7100 class LIGOLWContentHandlerExtractSimBurstTable(ligolw.LIGOLWContentHandler): 7101 def __init__(self,document): 7102 ligolw.LIGOLWContentHandler.__init__(self,document) 7103 self.tabname=lsctables.SimBurstTable.tableName 7104 self.intable=False 7105 self.tableElementName=''
7106 def startElement(self,name,attrs): 7107 if attrs.has_key('Name') and table.Table.TableName(attrs['Name'])==self.tabname: 7108 self.tableElementName=name 7109 # Got the right table, let's see if it's the right event 7110 ligolw.LIGOLWContentHandler.startElement(self,name,attrs) 7111 self.intable=True 7112 elif self.intable: # We are in the correct table 7113 ligolw.LIGOLWContentHandler.startElement(self,name,attrs) 7114 def endElement(self,name): 7115 if self.intable: ligolw.LIGOLWContentHandler.endElement(self,name) 7116 if self.intable and name==self.tableElementName: self.intable=False 7117 7118 lsctables.use_in(LIGOLWContentHandlerExtractSimBurstTable) 7119 7120 # time and freq data handling variables 7121 srate=4096.0 7122 seglen=10. 7123 length=srate*seglen # lenght of 10 secs, hardcoded. 7124 deltaT=1/srate 7125 deltaF = 1.0 / (length* deltaT); 7126 7127 # build window for FFT 7128 pad=0.4 7129 timeToFreqFFTPlan = CreateForwardREAL8FFTPlan(int(length), 1 ); 7130 freqToTimeFFTPlan = CreateReverseREAL8FFTPlan(int(length), 1 ); 7131 window=CreateTukeyREAL8Window(int(length),2.0*pad*srate/length); 7132 # A random GPS time to initialize arrays. Epoch will be overwritten with sensible times further down 7133 segStart=939936910.000 7134 strainFinj= CreateCOMPLEX16FrequencySeries("strainF",segStart,0.0,deltaF,DimensionlessUnit,int(length/2. +1)); 7135 strainTinj=CreateREAL8TimeSeries("strainT",segStart,0.0,1.0/srate,DimensionlessUnit,int(length)); 7136 strainFrec= CreateCOMPLEX16FrequencySeries("strainF",segStart,0.0,deltaF,DimensionlessUnit,int(length/2. +1)); 7137 strainTrec=CreateREAL8TimeSeries("strainT",segStart,0.0,1.0/srate,DimensionlessUnit,int(length)); 7138 GlobREAL8time=None 7139 f_min=25 # hardcoded default (may be changed below) 7140 f_ref=100 # hardcoded default (may be changed below) 7141 f_max=srate/2.0 7142 7143 plot_fmax=2048 7144 plot_fmin=0.01 7145 plot_tmin=1e11 7146 plot_tmax=-1e11 7147 7148 inj_strains=dict((i,{"T":{'x':None,'strain':None},"F":{'x':None,'strain':None}}) for i in ifos) 7149 rec_strains=dict((i,{"T":{'x':None,'strain':None},"F":{'x':None,'strain':None}}) for i in ifos) 7150 7151 inj_domain=None 7152 rec_domain=None 7153 font_size=26 7154 if simburst is not None: 7155 skip=0 7156 try: 7157 xmldoc = utils.load_filename(simburst,contenthandler=LIGOLWContentHandlerExtractSimBurstTable) 7158 tbl = lsctables.SimBurstTable.get_table(xmldoc) 7159 if event>0: 7160 tbl=tbl[event] 7161 else: 7162 tbl=tbl[0] 7163 except: 7164 print "Cannot read event %s from table %s. Won't plot injected waveform \n"%(event,simburst) 7165 skip=1 7166 if not skip: 7167 REAL8time=tbl.time_geocent_gps+1e-9*tbl.time_geocent_gps_ns 7168 GPStime=LIGOTimeGPS(REAL8time) 7169 GlobREAL8time=(REAL8time) 7170 strainTinj.epoch=LIGOTimeGPS(round(GlobREAL8time,0)-seglen/2.) 7171 strainFinj.epoch=LIGOTimeGPS(round(GlobREAL8time,0)-seglen/2.) 7172 f0=tbl.frequency 7173 q=tbl.q 7174 dur=tbl.duration 7175 hrss=tbl.hrss 7176 polar_e_angle=tbl.pol_ellipse_angle 7177 polar_e_ecc=tbl.pol_ellipse_e 7178 7179 BurstExtraParams=None 7180 wf=str(tbl.waveform) 7181 7182 injapproximant=lalinf.GetBurstApproximantFromString(wf) 7183 ra=tbl.ra 7184 dec=tbl.dec 7185 psi=tbl.psi 7186 7187 if SimBurstImplementedFDApproximants(injapproximant): 7188 inj_domain='F' 7189 [plus,cross]=SimBurstChooseFDWaveform(deltaF, deltaT, f0, q,dur, f_min, f_max,hrss,polar_e_angle ,polar_e_ecc,BurstExtraParams, injapproximant) 7190 elif SimBurstImplementedTDApproximants(injapproximant): 7191 inj_domain='T' 7192 [plus,cross]=SimBurstChooseTDWaveform(deltaT, f0, q,dur, f_min, f_max,hrss,polar_e_angle ,polar_e_ecc,BurstExtraParams, injapproximant) 7193 else: 7194 print "\nThe approximant %s doesn't seem to be recognized by lalinference!\n Skipping WF plots\n"%injapproximant 7195 return None 7196 7197 for ifo in ifos: 7198 (fp,fc,fa,qv)=ant.response(REAL8time,ra,dec,0.0,psi,'radians',ifo) 7199 if inj_domain=='T': 7200 # bin of ref time as seen in strainT 7201 tCinstrain=np.floor(REAL8time-float(strainTinj.epoch))/deltaT 7202 # bin of strainT where we need to start copying the WF over 7203 #tSinstrain=floor(tCinstrain-float(plus.data.length)/2.)+1 7204 tSinstrain=int( (REAL8time-fabs(float(plus.epoch)) - fabs(float(strainTinj.epoch)))/deltaT) 7205 rem=(REAL8time-fabs(float(plus.epoch)) - fabs(float(strainTinj.epoch)))/deltaT-tSinstrain 7206 # strain is a temporary container for this IFO strain. 7207 # Zero until tSinstrain 7208 for k in np.arange(tSinstrain): 7209 strainTinj.data.data[k]=0.0 7210 # then copy plus/cross over 7211 for k in np.arange(plus.data.length): 7212 strainTinj.data.data[k+tSinstrain]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 7213 # Then zeros till the end (superfluous) 7214 for k in np.arange(strainTinj.data.length- (tSinstrain +plus.data.length)): 7215 strainTinj.data.data[k+tSinstrain+plus.data.length]=0.0 7216 for k in np.arange(strainTinj.data.length): 7217 strainTinj.data.data[k]*=window.data.data[k] 7218 np.savetxt('file.out',zip(np.array([strainTinj.epoch + j*deltaT for j in arange(strainTinj.data.length)]),np.array([strainTinj.data.data[j] for j in arange(strainTinj.data.length)]))) 7219 # now copy in the dictionary 7220 inj_strains[ifo]["T"]['strain']=np.array([strainTinj.data.data[j] for j in arange(strainTinj.data.length)]) 7221 inj_strains[ifo]["T"]['x']=np.array([strainTinj.epoch + j*deltaT for j in arange(strainTinj.data.length)]) 7222 # Take the FFT 7223 for j in arange(strainFinj.data.length): 7224 strainFinj.data.data[j]=0.0 7225 REAL8TimeFreqFFT(strainFinj,strainTinj,timeToFreqFFTPlan); 7226 twopit=2.*np.pi*(rem*deltaT) 7227 for k in arange(strainFinj.data.length): 7228 re = cos(twopit*deltaF*k) 7229 im = -sin(twopit*deltaF*k) 7230 strainFinj.data.data[k]*= (re + 1j*im); 7231 # copy in the dictionary 7232 inj_strains[ifo]["F"]['strain']=np.array([strainFinj.data.data[k] for k in arange(int(strainFinj.data.length))]) 7233 inj_strains[ifo]["F"]['x']=np.array([strainFinj.f0+ k*strainFinj.deltaF for k in arange(int(strainFinj.data.length))]) 7234 elif inj_domain=='F': 7235 for k in np.arange(strainFinj.data.length): 7236 if k<plus.data.length: 7237 strainFinj.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 7238 else: 7239 strainFinj.data.data[k]=0.0 7240 twopit=2.*np.pi*(REAL8time-float(strainFinj.epoch)) 7241 for k in arange(strainFinj.data.length): 7242 re = cos(twopit*deltaF*k) 7243 im = -sin(twopit*deltaF*k) 7244 strainFinj.data.data[k]*= (re + 1j*im); 7245 # copy in the dictionary 7246 inj_strains[ifo]["F"]['strain']=np.array([strainFinj.data.data[k] for k in arange(int(strainFinj.data.length))]) 7247 inj_strains[ifo]["F"]['x']=np.array([strainFinj.f0+ k*strainFinj.deltaF for k in arange(int(strainFinj.data.length))]) 7248 #update xlimits for plot, go 6 sigmas left and right of f0 7249 # This should work for SineGaussians 7250 if f0 is not None and f0 is not np.nan: 7251 if q is not None and q is not np.nan: 7252 sigmaF=f0/q 7253 if f0-6.*sigmaF>plot_fmin: 7254 plot_fmin=f0-6.*sigmaF 7255 if f0+6.*sigmaF<plot_fmax: 7256 plot_fmax=f0+6.*sigmaF 7257 sigmaT=q/(2.*pi*f0) 7258 if REAL8time-6.*sigmaT<plot_tmin: 7259 plot_tmin=REAL8time-6.*sigmaT 7260 if REAL8time+6.*sigmaT>plot_tmax: 7261 plot_tmax=REAL8time+6.*sigmaT 7262 # Now handle gaussians. For gaussians f0 is nan (FD centered at f=0) 7263 if dur is not None and dur is not np.nan: 7264 sigmaF=1./sqrt(2.)/pi/dur 7265 if 0+6.*sigmaF<plot_fmax: 7266 plot_fmax=0+6.*sigmaF 7267 plot_fmin=0.0 7268 sigmaT=dur/sqrt(2.) 7269 if REAL8time-6.*sigmaT<plot_tmin: 7270 plot_tmin=REAL8time-6.*sigmaT 7271 if REAL8time+6.*sigmaT>plot_tmax: 7272 plot_tmax=REAL8time+6.*sigmaT 7273 7274 7275 if pos is not None: 7276 7277 # Select the maxP sample 7278 _,which=pos._posMap() 7279 7280 if 'time' in pos.names: 7281 REAL8time=pos['time'].samples[which][0] 7282 elif 'time_maxl' in pos.names: 7283 REAL8time=pos['time_maxl'].samples[which][0] 7284 elif 'time_mean' in pos.names: 7285 REAL8time=pos['time_mean'].samples[which][0] 7286 elif 'time_min' in pos.names and 'time_max' in pos.names: 7287 REAL8time=pos['time_min'].samples[which][0]+0.5*(pos['time_max'].samples[which][0]-pos['time_min'].samples[which][0]) 7288 else: 7289 print "ERROR: could not find any time parameter in the posterior file. Not plotting the WF...\n" 7290 return None 7291 7292 # first check we have approx in posterior samples, otherwise skip 7293 skip=0 7294 7295 try: 7296 approximant=int(pos['LAL_APPROXIMANT'].samples[which][0]) 7297 except: 7298 skip=1 7299 if skip==0: 7300 GPStime=LIGOTimeGPS(REAL8time) 7301 if GlobREAL8time is None: 7302 GlobREAL8time=REAL8time 7303 strainTrec.epoch=LIGOTimeGPS(round(GlobREAL8time,0)-seglen/2.) 7304 strainFrec.epoch=LIGOTimeGPS(round(GlobREAL8time,0)-seglen/2.) 7305 if "duration" in pos.names: 7306 dur=pos["duration"].samples[which][0] 7307 else: 7308 dur=np.nan 7309 if "quality" in pos.names: 7310 q=pos['quality'].samples[which][0] 7311 else: 7312 q=np.nan 7313 if 'frequency' in pos.names: 7314 f0=pos['frequency'].samples[which][0] 7315 else: 7316 f0=np.nan 7317 try: 7318 hrss=pos['hrss'].samples[which][0] 7319 except: 7320 hrss=exp(pos['loghrss'].samples[which][0]) 7321 if np.isnan(q) and not np.isnan(dur): 7322 q=sqrt(2)*pi*dur 7323 alpha=None 7324 if 'alpha' in pos.names: 7325 alpha=pos['alpha'].samples[which][0] 7326 polar_e_angle=alpha 7327 polar_e_ecc=pos['polar_eccentricity'].samples[which][0] 7328 elif 'polar_ellipse_angle' in pos.names: 7329 polar_e_angle=pos['polar_ellipse_angle'].samples[which][0] 7330 polar_e_ecc=pos['polar_eccentricity'].samples[which][0] 7331 7332 BurstExtraParams=None 7333 #if alpha: 7334 # BurstExtraParams=lalsim.SimBurstCreateExtraParam("alpha",alpha) 7335 7336 if SimBurstImplementedFDApproximants(approximant): 7337 rec_domain='F' 7338 [plus,cross]=SimBurstChooseFDWaveform(deltaF, deltaT, f0, q,dur, f_min, f_max,hrss,polar_e_angle ,polar_e_ecc,BurstExtraParams, approximant) 7339 elif SimBurstImplementedTDApproximants(approximant): 7340 rec_domain='T' 7341 [plus,cross]=SimBurstChooseTDWaveform(deltaT, f0, q,dur, f_min, f_max,hrss,polar_e_angle ,polar_e_ecc,BurstExtraParams, approximant) 7342 else: 7343 print "The approximant %s doesn't seem to be recognized by lalinference!\n Skipping WF plots\n"%approximant 7344 return None 7345 ra=pos['ra'].samples[which][0] 7346 dec=pos['dec'].samples[which][0] 7347 psi=pos['psi'].samples[which][0] 7348 fs={} 7349 for ifo in ifos: 7350 (fp,fc,fa,qv)=ant.response(REAL8time,ra,dec,0.0,psi,'radians',ifo) 7351 if rec_domain=='T': 7352 # bin of ref time as seen in strainT 7353 tCinstrain=np.floor(REAL8time-float(strainTrec.epoch))/deltaT 7354 # bin of strainT where we need to start copying the WF over 7355 tSinstrain=int( (REAL8time-fabs(float(plus.epoch)) - fabs(float(strainTrec.epoch)))/deltaT) 7356 #tSinstrain=floor(tCinstrain-float(plus.data.length)/2.)+1 7357 #reminder for fractions of bin, will be added back in the FD WF 7358 rem=(REAL8time-fabs(float(plus.epoch)) - fabs(float(strainTrec.epoch)))/deltaT-tSinstrain 7359 7360 # strain is a temporary container for this IFO strain. 7361 # Zero until tSinstrain 7362 for k in np.arange(tSinstrain): 7363 strainTrec.data.data[k]=0.0 7364 # then copy plus/cross over 7365 for k in np.arange(plus.data.length): 7366 strainTrec.data.data[k+tSinstrain]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 7367 # Then zeros till the end (superfluous) 7368 for k in np.arange(strainTrec.data.length- (tSinstrain +plus.data.length)): 7369 strainTrec.data.data[k+tSinstrain+plus.data.length]=0.0 7370 for k in np.arange(strainTrec.data.length): 7371 strainTrec.data.data[k]*=window.data.data[k] 7372 # now copy in the dictionary 7373 rec_strains[ifo]["T"]['strain']=np.array([strainTrec.data.data[j] for j in arange(strainTrec.data.length)]) 7374 rec_strains[ifo]["T"]['x']=np.array([strainTrec.epoch + j*deltaT for j in arange(strainTrec.data.length)]) 7375 # Take the FFT 7376 for j in arange(strainFrec.data.length): 7377 strainFrec.data.data[j]=0.0 7378 REAL8TimeFreqFFT(strainFrec,strainTrec,timeToFreqFFTPlan); 7379 twopit=2.*np.pi*(rem*deltaT) 7380 for k in arange(strainFrec.data.length): 7381 re = cos(twopit*deltaF*k) 7382 im = -sin(twopit*deltaF*k) 7383 strainFrec.data.data[k]*= (re + 1j*im); 7384 # copy in the dictionary 7385 rec_strains[ifo]["F"]['strain']=np.array([strainFrec.data.data[k] for k in arange(int(strainFrec.data.length))]) 7386 rec_strains[ifo]["F"]['x']=np.array([strainFrec.f0+ k*strainFrec.deltaF for k in arange(int(strainFrec.data.length))]) 7387 elif rec_domain=='F': 7388 for k in np.arange(strainFrec.data.length): 7389 if k<plus.data.length: 7390 strainFrec.data.data[k]=((fp*plus.data.data[k]+fc*cross.data.data[k])) 7391 else: 7392 strainFrec.data.data[k]=0.0 7393 twopit=2.*np.pi*(REAL8time-float(strainFrec.epoch)) 7394 for k in arange(strainFrec.data.length): 7395 re = cos(twopit*deltaF*k) 7396 im = -sin(twopit*deltaF*k) 7397 strainFrec.data.data[k]*= (re + 1j*im); 7398 # copy in the dictionary 7399 rec_strains[ifo]["F"]['strain']=np.array([strainFrec.data.data[k] for k in arange(int(strainFrec.data.length))]) 7400 rec_strains[ifo]["F"]['x']=np.array([strainFrec.f0+ k*strainFrec.deltaF for k in arange(int(strainFrec.data.length))]) 7401 #update xlimits for plot, go 6 sigmas left and right of f0 7402 # This should work for SineGaussians 7403 if f0 is not None and f0 is not np.nan: 7404 if q is not None and q is not np.nan: 7405 sigmaF=f0/q 7406 if f0-6.*sigmaF>plot_fmin: 7407 plot_fmin=f0-6.*sigmaF 7408 if f0+6.*sigmaF<plot_fmax: 7409 plot_fmax=f0+6.*sigmaF 7410 sigmaT=q/(2.*pi*f0) 7411 if REAL8time-6.*sigmaT<plot_tmin: 7412 plot_tmin=REAL8time-6.*sigmaT 7413 if REAL8time+6.*sigmaT>plot_tmax: 7414 plot_tmax=REAL8time+6.*sigmaT 7415 # Now handle gaussians. For gaussians f0 is nan (FD centered at f=0) 7416 if dur is not None and dur is not np.nan: 7417 sigmaF=1./sqrt(2.)/pi/dur 7418 if 0+6.*sigmaF<plot_fmax: 7419 plot_fmax=0+6.*sigmaF 7420 plot_fmin=0.0 7421 sigmaT=dur/sqrt(2.) 7422 if REAL8time-6.*sigmaT<plot_tmin: 7423 plot_tmin=REAL8time-6.*sigmaT 7424 if REAL8time+6.*sigmaT>plot_tmax: 7425 plot_tmax=REAL8time+6.*sigmaT 7426 7427 myfig=plt.figure(1,figsize=(10,7)) 7428 7429 rows=len(ifos) 7430 cols=2 7431 7432 #this variables decide which domain will be plotted on the left column of the plot. 7433 # only plot Time domain if both injections and recovery are TD 7434 global_domain="F" 7435 if rec_domain is not None and inj_domain is not None: 7436 if rec_domain=="T" and inj_domain=="T": 7437 global_domain="T" 7438 elif rec_domain is not None: 7439 if rec_domain=="T": 7440 global_domain="T" 7441 elif inj_domain is not None: 7442 if inj_domain=="T": 7443 global_domain="T" 7444 7445 A,axes=plt.subplots(nrows=rows,ncols=cols,sharex=False,sharey=False) 7446 plt.setp(A,figwidth=10,figheight=7) 7447 for (r,i) in zip(np.arange(rows),ifos): 7448 for c in np.arange(cols): 7449 ax=axes[r] 7450 if type(ax)==np.ndarray: 7451 ax=ax[c] 7452 else: 7453 ax=axes[c] 7454 if rec_strains[i]["T"]['strain'] is not None or rec_strains[i]["F"]['strain'] is not None: 7455 if c==0: 7456 if global_domain=="T": 7457 ax.plot(rec_strains[i]["T"]['x'],rec_strains[i]["T"]['strain'],colors_rec[i],label='%s maP'%i,linewidth=5) 7458 ax.set_xlim([plot_tmin,plot_tmax]) 7459 #ax.vlines(GlobREAL8time,0.9*min(rec_strains[i]["T"]['strain']),1.1*max(rec_strains[i]["T"]['strain']),'k') 7460 else: 7461 data=rec_strains[i]["F"]['strain'] 7462 f=rec_strains[i]["F"]['x'] 7463 mask=np.logical_and(f>=plot_fmin,f<=plot_fmax) 7464 ys=data 7465 ax.plot(f[mask],real(ys[mask]),'-',color=colors_rec[i],label='%s maP'%i,linewidth=5) 7466 ax.set_xlim([plot_fmin,plot_fmax]) 7467 else: 7468 data=rec_strains[i]["F"]['strain'] 7469 f=rec_strains[i]["F"]['x'] 7470 mask=np.logical_and(f>=plot_fmin,f<=plot_fmax) 7471 ys=data 7472 ax.loglog(f[mask],absolute(ys[mask]),'--',color=colors_rec[i],linewidth=5) 7473 ax.grid(True,which='both') 7474 ax.set_xlim([plot_fmin,plot_fmax]) 7475 if inj_strains[i]["T"]['strain'] is not None or inj_strains[i]["F"]['strain'] is not None: 7476 if c==0: 7477 if global_domain=="T": 7478 ax.plot(inj_strains[i]["T"]['x'],inj_strains[i]["T"]['strain'],colors_inj[i],label='%s inj'%i,linewidth=2) 7479 ax.set_xlim([plot_tmin,plot_tmax]) 7480 else: 7481 data=inj_strains[i]["F"]['strain'] 7482 f=inj_strains[i]["F"]['x'] 7483 mask=np.logical_and(f>=plot_fmin,f<=plot_fmax) 7484 ys=data 7485 ax.plot(f[mask],real(ys[mask]),'-',color=colors_inj[i],label='%s inj'%i,linewidth=2) 7486 ax.set_xlim([plot_fmin,plot_fmax]) 7487 else: 7488 data=inj_strains[i]["F"]['strain'] 7489 f=inj_strains[i]["F"]['x'] 7490 mask=np.logical_and(f>=plot_fmin,f<=plot_fmax) 7491 ys=data 7492 ax.loglog(f[mask],absolute(ys[mask]),'--',color=colors_inj[i],linewidth=2) 7493 ax.grid(True,which='both') 7494 ax.set_xlim([plot_fmin,plot_fmax]) 7495 if r==0: 7496 if c==0: 7497 if global_domain=="T": 7498 ax.set_title(r"$h(t)$",fontsize=font_size) 7499 else: 7500 ax.set_title(r"$\Re[h(f)]$",fontsize=font_size) 7501 else: 7502 ax.set_title(r"$|h(f)|$",fontsize=font_size) 7503 elif r==rows-1: 7504 if c==0: 7505 if global_domain=="T": 7506 ax.set_xlabel("time [s]",fontsize=font_size) 7507 else: 7508 ax.set_xlabel("frequency [Hz]",fontsize=font_size) 7509 else: 7510 ax.set_xlabel("frequency [Hz]",fontsize=font_size) 7511 7512 ax.legend(loc='best') 7513 ax.grid(True) 7514 7515 #ax.tight_layout() 7516 A.savefig(os.path.join(path,'WF_DetFrame.png'),bbox_inches='tight') 7517 return inj_strains, rec_strains 7518
7519 -def make_1d_table(html,legend,label,pos,pars,noacf,GreedyRes,onepdfdir,sampsdir,savepdfs,greedy,analyticLikelihood,nDownsample):
7520 7521 from numpy import unique, sort 7522 global confidenceLevels 7523 confidence_levels=confidenceLevels 7524 7525 out={} 7526 if pars==[]: 7527 return out 7528 if set(pos.names)-set(pars)==set(pos.names): 7529 return out 7530 7531 #2D plots list 7532 tabid='onedmargtable_'+label.lower() 7533 html_ompdf=html.add_collapse_section('1D marginal posterior PDFs (%s)'%label,legend=legend,innertable_id=tabid) 7534 #Table matter 7535 if not noacf: 7536 html_ompdf_write= '<table id="%s"><tr><th>Histogram and Kernel Density Estimate</th><th>Samples used</th><th>Autocorrelation</th></tr>'%tabid 7537 else: 7538 html_ompdf_write= '<table id="%s"><tr><th>Histogram and Kernel Density Estimate</th><th>Samples used</th></tr>'%tabid 7539 7540 Nskip=0 7541 if 'chain' in pos.names: 7542 data,header=pos.samples() 7543 par_index=pos.names.index('cycle') 7544 chain_index=pos.names.index("chain") 7545 chains=unique(pos["chain"].samples) 7546 chainCycles = [sort(data[ data[:,chain_index] == chain, par_index ]) for chain in chains] 7547 chainNcycles = [] 7548 chainNskips = [] 7549 for cycles in chainCycles: 7550 if len(cycles) > 1: 7551 chainNcycles.append(cycles[-1] - cycles[0]) 7552 chainNskips.append(cycles[1] - cycles[0]) 7553 else: 7554 chainNcycles.append(1) 7555 chainNskips.append(1) 7556 elif 'cycle' in pos.names: 7557 cycles = sort(pos['cycle'].samples) 7558 if len(cycles) > 1: 7559 Ncycles = cycles[-1]-cycles[0] 7560 Nskip = cycles[1]-cycles[0] 7561 else: 7562 Ncycles = 1 7563 Nskip = 1 7564 7565 printed=0 7566 for par_name in pars: 7567 par_name=par_name.lower() 7568 try: 7569 pos[par_name.lower()] 7570 except KeyError: 7571 #print "No input chain for %s, skipping binning."%par_name 7572 continue 7573 try: 7574 par_bin=GreedyRes[par_name] 7575 except KeyError: 7576 print "Bin size is not set for %s, skipping binning."%par_name 7577 continue 7578 7579 #print "Binning %s to determine confidence levels ..."%par_name 7580 binParams={par_name:par_bin} 7581 injection_area=None 7582 injection_area=None 7583 if greedy: 7584 if printed==0: 7585 print "Using greedy 1-d binning credible regions\n" 7586 printed=1 7587 toppoints,injectionconfidence,reses,injection_area,cl_intervals=greedy_bin_one_param(pos,binParams,confidence_levels) 7588 else: 7589 if printed==0: 7590 print "Using 2-step KDE 1-d credible regions\n" 7591 printed=1 7592 if pos[par_name].injval is None: 7593 injCoords=None 7594 else: 7595 injCoords=[pos[par_name].injval] 7596 _,reses,injstats=kdtree_bin2Step(pos,[par_name],confidence_levels,injCoords=injCoords) 7597 if injstats is not None: 7598 injectionconfidence=injstats[3] 7599 injection_area=injstats[4] 7600 #Generate 1D histogram/kde plots 7601 print "Generating 1D plot for %s."%par_name 7602 out[par_name]=reses 7603 #Get analytic description if given 7604 pdf=cdf=None 7605 if analyticLikelihood: 7606 pdf = analyticLikelihood.pdf(par_name) 7607 cdf = analyticLikelihood.cdf(par_name) 7608 7609 oneDPDFParams={par_name:50} 7610 try: 7611 rbins,plotFig=plot_one_param_pdf(pos,oneDPDFParams,pdf,cdf,plotkde=False) 7612 except: 7613 print "Failed to produce plot for %s."%par_name 7614 continue 7615 7616 figname=par_name+'.png' 7617 oneDplotPath=os.path.join(onepdfdir,figname) 7618 plotFig.savefig(oneDplotPath) 7619 if(savepdfs): plotFig.savefig(os.path.join(onepdfdir,par_name+'.pdf')) 7620 plt.close(plotFig) 7621 7622 if rbins: 7623 print "r of injected value of %s (bins) = %f"%(par_name, rbins) 7624 7625 ##Produce plot of raw samples 7626 myfig=plt.figure(figsize=(4,3.5),dpi=200) 7627 pos_samps=pos[par_name].samples 7628 if not ("chain" in pos.names): 7629 # If there is not a parameter named "chain" in the 7630 # posterior, then just produce a plot of the samples. 7631 plt.plot(pos_samps,'k.', markersize=5, alpha=0.5, linewidth=0.0, figure=myfig) 7632 maxLen=len(pos_samps) 7633 else: 7634 # If there is a parameter named "chain", then produce a 7635 # plot of the various chains in different colors, with 7636 # smaller dots. 7637 data,header=pos.samples() 7638 par_index=pos.names.index(par_name) 7639 chain_index=pos.names.index("chain") 7640 chains=unique(pos["chain"].samples) 7641 chainData=[data[ data[:,chain_index] == chain, par_index ] for chain in chains] 7642 chainDataRanges=[range(len(cd)) for cd in chainData] 7643 maxLen=max([len(cd) for cd in chainData]) 7644 for rng, data in zip(chainDataRanges, chainData): 7645 plt.plot(rng, data, marker='.', markersize=1, alpha=0.5, linewidth=0.0,figure=myfig) 7646 plt.title("Gelman-Rubin R = %g"%(pos.gelman_rubin(par_name))) 7647 7648 #dataPairs=[ [rng, data] for (rng,data) in zip(chainDataRanges, chainData)] 7649 #flattenedData=[ item for pair in dataPairs for item in pair ] 7650 #maxLen=max([len(data) for data in flattenedData]) 7651 #plt.plot(array(flattenedData),marker=',',linewidth=0.0,figure=myfig) 7652 7653 7654 injpar=pos[par_name].injval 7655 7656 if injpar is not None: 7657 # Allow injection to be 5% outside the posterior plot 7658 minrange=min(pos_samps)-0.05*(max(pos_samps)-min(pos_samps)) 7659 maxrange=max(pos_samps)+0.05*(max(pos_samps)-min(pos_samps)) 7660 if minrange<injpar and maxrange>injpar: 7661 plt.axhline(injpar, color='r', linestyle='-.',linewidth=4) 7662 myfig.savefig(os.path.join(sampsdir,figname.replace('.png','_samps.png'))) 7663 if(savepdfs): myfig.savefig(os.path.join(sampsdir,figname.replace('.png','_samps.pdf'))) 7664 plt.close(myfig) 7665 acfail=0 7666 if not (noacf): 7667 acffig=plt.figure(figsize=(4,3.5),dpi=200) 7668 if not ("chain" in pos.names): 7669 data=pos_samps[:,0] 7670 try: 7671 (Neff, acl, acf) = effectiveSampleSize(data, Nskip) 7672 lines=plt.plot(acf, 'k.', marker='.', markersize=1, alpha=0.5, linewidth=0.0, figure=acffig) 7673 # Give ACL info if not already downsampled according to it 7674 if nDownsample is None: 7675 plt.title('Autocorrelation Function') 7676 elif 'cycle' in pos.names: 7677 last_color = lines[-1].get_color() 7678 plt.axvline(acl/Nskip, linestyle='-.', color=last_color) 7679 plt.title('ACL = %i N = %i'%(acl,Neff)) 7680 except FloatingPointError: 7681 # Ignore 7682 acfail=1 7683 pass 7684 else: 7685 try: 7686 acls = [] 7687 Nsamps = 0.0; 7688 for rng, data, Nskip, Ncycles in zip(chainDataRanges, chainData, chainNskips, chainNcycles): 7689 (Neff, acl, acf) = effectiveSampleSize(data, Nskip) 7690 acls.append(acl) 7691 Nsamps += Neff 7692 lines=plt.plot(acf,'k.', marker='.', markersize=1, alpha=0.5, linewidth=0.0, figure=acffig) 7693 # Give ACL info if not already downsampled according to it 7694 if nDownsample is not None: 7695 last_color = lines[-1].get_color() 7696 plt.axvline(acl/Nskip, linestyle='-.', color=last_color) 7697 if nDownsample is None: 7698 plt.title('Autocorrelation Function') 7699 else: 7700 plt.title('ACL = %i N = %i'%(max(acls),Nsamps)) 7701 except FloatingPointError: 7702 # Ignore 7703 acfail=1 7704 pass 7705 7706 acffig.savefig(os.path.join(sampsdir,figname.replace('.png','_acf.png'))) 7707 if(savepdfs): acffig.savefig(os.path.join(sampsdir,figname.replace('.png','_acf.pdf'))) 7708 plt.close(acffig) 7709 7710 if not noacf: 7711 if not acfail: 7712 acfhtml='<td width="30%"><img width="100%" src="1Dsamps/'+figname.replace('.png', '_acf.png')+'"/></td>' 7713 else: 7714 acfhtml='<td>ACF generation failed!</td>' 7715 html_ompdf_write+='<tr><td width="30%"><img width="100%" src="1Dpdf/'+figname+'"/></td><td width="30%"><img width="100%" src="1Dsamps/'+figname.replace('.png','_samps.png')+'"/></td>'+acfhtml+'</tr>' 7716 else: 7717 html_ompdf_write+='<tr><td width="30%"><img width="100%" src="1Dpdf/'+figname+'"/></td><td width="30%"><img width="100%" src="1Dsamps/'+figname.replace('.png','_samps.png')+'"/></td></tr>' 7718 7719 html_ompdf_write+='</table>' 7720 html_ompdf.write(html_ompdf_write) 7721 7722 return out
7723