lalsuite 7.26.2.dev20251210__cp312-cp312-macosx_12_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lal/.dylibs/Python +0 -0
- lal/.dylibs/libaec.0.dylib +0 -0
- lal/.dylibs/libbrotlicommon.1.2.0.dylib +0 -0
- lal/.dylibs/libbrotlidec.1.2.0.dylib +0 -0
- lal/.dylibs/libcfitsio.10.4.4.1.dylib +0 -0
- lal/.dylibs/libcrypto.3.dylib +0 -0
- lal/.dylibs/libcurl.4.dylib +0 -0
- lal/.dylibs/libfftw3.3.dylib +0 -0
- lal/.dylibs/libfftw3f.3.dylib +0 -0
- lal/.dylibs/libframel.8.48.4.dylib +0 -0
- lal/.dylibs/libgsl.28.dylib +0 -0
- lal/.dylibs/libgslcblas.0.dylib +0 -0
- lal/.dylibs/libhdf5.310.dylib +0 -0
- lal/.dylibs/libhdf5_hl.310.dylib +0 -0
- lal/.dylibs/libiconv.2.dylib +0 -0
- lal/.dylibs/libidn2.0.dylib +0 -0
- lal/.dylibs/libintl.8.dylib +0 -0
- lal/.dylibs/liblal.20.dylib +0 -0
- lal/.dylibs/liblalburst.8.dylib +0 -0
- lal/.dylibs/liblalframe.14.dylib +0 -0
- lal/.dylibs/liblalinference.23.dylib +0 -0
- lal/.dylibs/liblalinspiral.18.dylib +0 -0
- lal/.dylibs/liblalmetaio.11.dylib +0 -0
- lal/.dylibs/liblalpulsar.30.dylib +0 -0
- lal/.dylibs/liblalsimulation.37.dylib +0 -0
- lal/.dylibs/liblalsupport.14.dylib +0 -0
- lal/.dylibs/libmetaio.1.dylib +0 -0
- lal/.dylibs/libnghttp2.14.dylib +0 -0
- lal/.dylibs/libpsl.5.dylib +0 -0
- lal/.dylibs/libssl.3.dylib +0 -0
- lal/.dylibs/libsz.2.dylib +0 -0
- lal/.dylibs/libunistring.5.dylib +0 -0
- lal/.dylibs/libz.1.3.1.dylib +0 -0
- lal/.dylibs/libzstd.1.5.7.dylib +0 -0
- lal/__init__.py +145 -0
- lal/_lal.cpython-312-darwin.so +0 -0
- lal/_lal_swig.py +12 -0
- lal/antenna.py +1200 -0
- lal/git_version.py +64 -0
- lal/gpstime.py +233 -0
- lal/iterutils.py +408 -0
- lal/pipeline.py +3139 -0
- lal/rate.py +2455 -0
- lal/series.py +244 -0
- lal/utils/__init__.py +29 -0
- lal/utils/cache.py +379 -0
- lal/utils/series.py +277 -0
- lalapps/__init__.py +26 -0
- lalapps/bin/lal_cache +0 -0
- lalapps/bin/lal_fftw_wisdom +0 -0
- lalapps/bin/lal_fftwf_wisdom +0 -0
- lalapps/bin/lal_simd_detect +0 -0
- lalapps/bin/lal_tconvert +0 -0
- lalapps/bin/lal_version +0 -0
- lalapps/bin/lalapps_ComputeAntennaPattern +16 -0
- lalapps/bin/lalapps_ComputeFstatBenchmark +16 -0
- lalapps/bin/lalapps_ComputeFstatLatticeCount +16 -0
- lalapps/bin/lalapps_ComputeFstatMCUpperLimit +16 -0
- lalapps/bin/lalapps_ComputeFstatistic_v2 +16 -0
- lalapps/bin/lalapps_ComputePSD +16 -0
- lalapps/bin/lalapps_CopySFTs +16 -0
- lalapps/bin/lalapps_DistanceVsMass +0 -0
- lalapps/bin/lalapps_DriveHoughMulti +16 -0
- lalapps/bin/lalapps_FstatMetric_v2 +16 -0
- lalapps/bin/lalapps_HierarchSearchGCT +16 -0
- lalapps/bin/lalapps_HierarchicalSearch +16 -0
- lalapps/bin/lalapps_MakeSFTDAG +16 -0
- lalapps/bin/lalapps_MakeSFTs +16 -0
- lalapps/bin/lalapps_Makefakedata_v4 +16 -0
- lalapps/bin/lalapps_Makefakedata_v5 +16 -0
- lalapps/bin/lalapps_PredictFstat +16 -0
- lalapps/bin/lalapps_PrintDetectorState +16 -0
- lalapps/bin/lalapps_SFTclean +16 -0
- lalapps/bin/lalapps_SFTvalidate +16 -0
- lalapps/bin/lalapps_StringAddFrame +0 -0
- lalapps/bin/lalapps_StringSearch +0 -0
- lalapps/bin/lalapps_Weave +16 -0
- lalapps/bin/lalapps_WeaveCompare +16 -0
- lalapps/bin/lalapps_WeaveConcat +16 -0
- lalapps/bin/lalapps_WeaveSetup +16 -0
- lalapps/bin/lalapps_WriteSFTsfromSFDBs +16 -0
- lalapps/bin/lalapps_animate +0 -0
- lalapps/bin/lalapps_binj +0 -0
- lalapps/bin/lalapps_blindinj +0 -0
- lalapps/bin/lalapps_cache +16 -0
- lalapps/bin/lalapps_calfacs +0 -0
- lalapps/bin/lalapps_cbc_stochasticbank +0 -0
- lalapps/bin/lalapps_chirplen +0 -0
- lalapps/bin/lalapps_coh_PTF_inspiral +0 -0
- lalapps/bin/lalapps_coinj +0 -0
- lalapps/bin/lalapps_combine_crosscorr_toplists +16 -0
- lalapps/bin/lalapps_compareFstats +16 -0
- lalapps/bin/lalapps_compareSFTs +16 -0
- lalapps/bin/lalapps_create_time_correction_ephemeris +16 -0
- lalapps/bin/lalapps_dumpSFT +16 -0
- lalapps/bin/lalapps_effdist +0 -0
- lalapps/bin/lalapps_exc_resp +0 -0
- lalapps/bin/lalapps_fftw_wisdom +16 -0
- lalapps/bin/lalapps_fftwf_wisdom +16 -0
- lalapps/bin/lalapps_fits_header_getval +16 -0
- lalapps/bin/lalapps_fits_header_list +16 -0
- lalapps/bin/lalapps_fits_overview +16 -0
- lalapps/bin/lalapps_fits_table_list +16 -0
- lalapps/bin/lalapps_fr_ninja +0 -0
- lalapps/bin/lalapps_frextr +0 -0
- lalapps/bin/lalapps_frinfo +0 -0
- lalapps/bin/lalapps_frjoin +0 -0
- lalapps/bin/lalapps_frread +0 -0
- lalapps/bin/lalapps_frview +0 -0
- lalapps/bin/lalapps_gwf2xml +0 -0
- lalapps/bin/lalapps_heterodyne_pulsar +16 -0
- lalapps/bin/lalapps_inspawgfile +0 -0
- lalapps/bin/lalapps_inspfrinj +0 -0
- lalapps/bin/lalapps_inspinj +0 -0
- lalapps/bin/lalapps_inspiralDistance +0 -0
- lalapps/bin/lalapps_knope +16 -0
- lalapps/bin/lalapps_knope_automation_script +16 -0
- lalapps/bin/lalapps_knope_collate_results +16 -0
- lalapps/bin/lalapps_knope_result_page +16 -0
- lalapps/bin/lalapps_makeblindinj +85 -0
- lalapps/bin/lalapps_makeblindinj_himass +67 -0
- lalapps/bin/lalapps_ninja +0 -0
- lalapps/bin/lalapps_path2cache +16 -0
- lalapps/bin/lalapps_power +0 -0
- lalapps/bin/lalapps_pulsar_crosscorr_v2 +16 -0
- lalapps/bin/lalapps_pulsar_frequency_evolution +16 -0
- lalapps/bin/lalapps_pulsar_parameter_estimation_nested +16 -0
- lalapps/bin/lalapps_random_bank +0 -0
- lalapps/bin/lalapps_randombank +0 -0
- lalapps/bin/lalapps_run_pulsar_crosscorr_v2 +16 -0
- lalapps/bin/lalapps_searchsum2cache +16 -0
- lalapps/bin/lalapps_spec_avg +16 -0
- lalapps/bin/lalapps_spec_avg_long +16 -0
- lalapps/bin/lalapps_spec_coherence +16 -0
- lalapps/bin/lalapps_spininj +0 -0
- lalapps/bin/lalapps_splitSFTs +16 -0
- lalapps/bin/lalapps_splitbank +0 -0
- lalapps/bin/lalapps_ssbtodetector +16 -0
- lalapps/bin/lalapps_synthesizeBstatMC +16 -0
- lalapps/bin/lalapps_synthesizeLVStats +16 -0
- lalapps/bin/lalapps_synthesizeTransientStats +16 -0
- lalapps/bin/lalapps_tconvert +16 -0
- lalapps/bin/lalapps_tmpltbank +0 -0
- lalapps/bin/lalapps_version +0 -0
- lalapps/bin/lalapps_xtefitstoframe +0 -0
- lalapps/bin/lalburst_version +0 -0
- lalapps/bin/lalfr-cat +0 -0
- lalapps/bin/lalfr-cksum +0 -0
- lalapps/bin/lalfr-cut +0 -0
- lalapps/bin/lalfr-dump +0 -0
- lalapps/bin/lalfr-fmt +0 -0
- lalapps/bin/lalfr-paste +0 -0
- lalapps/bin/lalfr-print +0 -0
- lalapps/bin/lalfr-split +0 -0
- lalapps/bin/lalfr-stat +0 -0
- lalapps/bin/lalfr-stream +0 -0
- lalapps/bin/lalfr-vis +0 -0
- lalapps/bin/lalframe_version +0 -0
- lalapps/bin/lalinference_bench +0 -0
- lalapps/bin/lalinference_burst +0 -0
- lalapps/bin/lalinference_datadump +0 -0
- lalapps/bin/lalinference_injectedlike +0 -0
- lalapps/bin/lalinference_mpi_wrapper +59 -0
- lalapps/bin/lalinference_nest +0 -0
- lalapps/bin/lalinference_version +0 -0
- lalapps/bin/lalinspiral_version +0 -0
- lalapps/bin/lalmetaio_version +0 -0
- lalapps/bin/lalpulsar_ComputeAntennaPattern +0 -0
- lalapps/bin/lalpulsar_ComputeFstatBenchmark +0 -0
- lalapps/bin/lalpulsar_ComputeFstatLatticeCount +0 -0
- lalapps/bin/lalpulsar_ComputeFstatMCUpperLimit +0 -0
- lalapps/bin/lalpulsar_ComputeFstatistic_v2 +0 -0
- lalapps/bin/lalpulsar_ComputePSD +0 -0
- lalapps/bin/lalpulsar_DriveHoughMulti +0 -0
- lalapps/bin/lalpulsar_FstatMetric_v2 +0 -0
- lalapps/bin/lalpulsar_HierarchSearchGCT +0 -0
- lalapps/bin/lalpulsar_HierarchicalSearch +0 -0
- lalapps/bin/lalpulsar_MakeSFTs +0 -0
- lalapps/bin/lalpulsar_Makefakedata_v4 +0 -0
- lalapps/bin/lalpulsar_Makefakedata_v5 +0 -0
- lalapps/bin/lalpulsar_PredictFstat +0 -0
- lalapps/bin/lalpulsar_PrintDetectorState +0 -0
- lalapps/bin/lalpulsar_SFTclean +0 -0
- lalapps/bin/lalpulsar_SFTvalidate +0 -0
- lalapps/bin/lalpulsar_Weave +0 -0
- lalapps/bin/lalpulsar_WeaveCompare +0 -0
- lalapps/bin/lalpulsar_WeaveConcat +0 -0
- lalapps/bin/lalpulsar_WeaveSetup +0 -0
- lalapps/bin/lalpulsar_WriteSFTsfromSFDBs +0 -0
- lalapps/bin/lalpulsar_compareFstats +0 -0
- lalapps/bin/lalpulsar_compareSFTs +0 -0
- lalapps/bin/lalpulsar_create_time_correction_ephemeris +0 -0
- lalapps/bin/lalpulsar_crosscorr_v2 +0 -0
- lalapps/bin/lalpulsar_dumpSFT +0 -0
- lalapps/bin/lalpulsar_fits_header_getval +0 -0
- lalapps/bin/lalpulsar_fits_header_list +0 -0
- lalapps/bin/lalpulsar_fits_overview +0 -0
- lalapps/bin/lalpulsar_fits_table_list +0 -0
- lalapps/bin/lalpulsar_frequency_evolution +0 -0
- lalapps/bin/lalpulsar_heterodyne +0 -0
- lalapps/bin/lalpulsar_parameter_estimation_nested +0 -0
- lalapps/bin/lalpulsar_spec_avg +0 -0
- lalapps/bin/lalpulsar_spec_avg_long +0 -0
- lalapps/bin/lalpulsar_spec_coherence +0 -0
- lalapps/bin/lalpulsar_splitSFTs +0 -0
- lalapps/bin/lalpulsar_ssbtodetector +0 -0
- lalapps/bin/lalpulsar_synthesizeBstatMC +0 -0
- lalapps/bin/lalpulsar_synthesizeLVStats +0 -0
- lalapps/bin/lalpulsar_synthesizeTransientStats +0 -0
- lalapps/bin/lalpulsar_version +0 -0
- lalapps/bin/lalsim-bh-qnmode +0 -0
- lalapps/bin/lalsim-bh-ringdown +0 -0
- lalapps/bin/lalsim-bh-sphwf +0 -0
- lalapps/bin/lalsim-burst +0 -0
- lalapps/bin/lalsim-detector-noise +0 -0
- lalapps/bin/lalsim-detector-strain +0 -0
- lalapps/bin/lalsim-inject +0 -0
- lalapps/bin/lalsim-inspiral +0 -0
- lalapps/bin/lalsim-ns-eos-table +0 -0
- lalapps/bin/lalsim-ns-mass-radius +0 -0
- lalapps/bin/lalsim-ns-params +0 -0
- lalapps/bin/lalsim-sgwb +0 -0
- lalapps/bin/lalsim-unicorn +0 -0
- lalapps/bin/lalsimulation_version +0 -0
- lalapps/cosmicstring.py +691 -0
- lalapps/data/BNSMasses.dat +65022 -0
- lalapps/data/CorrelationMatrix.csv +15 -0
- lalapps/data/LALSimNeutronStarEOS_ABHT_QMC_RMF1_META.dat +1882 -0
- lalapps/data/LALSimNeutronStarEOS_ABHT_QMC_RMF2_META.dat +1939 -0
- lalapps/data/LALSimNeutronStarEOS_ABHT_QMC_RMF3_META.dat +1784 -0
- lalapps/data/LALSimNeutronStarEOS_ABHT_QMC_RMF4_META.dat +2074 -0
- lalapps/data/LALSimNeutronStarEOS_ALF1.dat +435 -0
- lalapps/data/LALSimNeutronStarEOS_ALF2.dat +453 -0
- lalapps/data/LALSimNeutronStarEOS_ALF3.dat +441 -0
- lalapps/data/LALSimNeutronStarEOS_ALF4.dat +441 -0
- lalapps/data/LALSimNeutronStarEOS_AP1.dat +212 -0
- lalapps/data/LALSimNeutronStarEOS_AP2.dat +212 -0
- lalapps/data/LALSimNeutronStarEOS_AP3.dat +212 -0
- lalapps/data/LALSimNeutronStarEOS_AP4.dat +210 -0
- lalapps/data/LALSimNeutronStarEOS_APR.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_APR4_EPP.dat +1447 -0
- lalapps/data/LALSimNeutronStarEOS_BBB2.dat +84 -0
- lalapps/data/LALSimNeutronStarEOS_BGN1H1.dat +123 -0
- lalapps/data/LALSimNeutronStarEOS_BHF_BBB2.dat +499 -0
- lalapps/data/LALSimNeutronStarEOS_BL_CHIRAL_META.dat +1534 -0
- lalapps/data/LALSimNeutronStarEOS_BPAL12.dat +61 -0
- lalapps/data/LALSimNeutronStarEOS_BSK19.dat +310 -0
- lalapps/data/LALSimNeutronStarEOS_BSK20.dat +310 -0
- lalapps/data/LALSimNeutronStarEOS_BSK21.dat +310 -0
- lalapps/data/LALSimNeutronStarEOS_ENG.dat +108 -0
- lalapps/data/LALSimNeutronStarEOS_FPS.dat +129 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_BSK14_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_DHSL59_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_DHSL69_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_F0_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_H1_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_H2_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_H3_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_H4_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_H5_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_LN55_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GMSR_SLY5_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GNH3.dat +71 -0
- lalapps/data/LALSimNeutronStarEOS_GPPVA_DD2_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GPPVA_DDME2_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GPPVA_FSU2H_BSK24.dat +1009 -0
- lalapps/data/LALSimNeutronStarEOS_GPPVA_FSU2_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GPPVA_NL3WRL55_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_GS1.dat +136 -0
- lalapps/data/LALSimNeutronStarEOS_GS2.dat +100 -0
- lalapps/data/LALSimNeutronStarEOS_H1.dat +114 -0
- lalapps/data/LALSimNeutronStarEOS_H2.dat +114 -0
- lalapps/data/LALSimNeutronStarEOS_H3.dat +98 -0
- lalapps/data/LALSimNeutronStarEOS_H4.dat +664 -0
- lalapps/data/LALSimNeutronStarEOS_H5.dat +703 -0
- lalapps/data/LALSimNeutronStarEOS_H6.dat +509 -0
- lalapps/data/LALSimNeutronStarEOS_H7.dat +703 -0
- lalapps/data/LALSimNeutronStarEOS_HQC18.dat +388 -0
- lalapps/data/LALSimNeutronStarEOS_KDE0V.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_KDE0V1.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_KDE0V1_BSK24.dat +1388 -0
- lalapps/data/LALSimNeutronStarEOS_KDE0V_BSK24.dat +1398 -0
- lalapps/data/LALSimNeutronStarEOS_MPA1.dat +102 -0
- lalapps/data/LALSimNeutronStarEOS_MS1.dat +122 -0
- lalapps/data/LALSimNeutronStarEOS_MS1B.dat +126 -0
- lalapps/data/LALSimNeutronStarEOS_MS1B_PP.dat +1447 -0
- lalapps/data/LALSimNeutronStarEOS_MS1_PP.dat +1447 -0
- lalapps/data/LALSimNeutronStarEOS_MS2.dat +48 -0
- lalapps/data/LALSimNeutronStarEOS_PAL6.dat +148 -0
- lalapps/data/LALSimNeutronStarEOS_PCL2.dat +134 -0
- lalapps/data/LALSimNeutronStarEOS_PCP_BSK24_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_PS.dat +165 -0
- lalapps/data/LALSimNeutronStarEOS_QMC700.dat +117 -0
- lalapps/data/LALSimNeutronStarEOS_RG_SLY4_BSK24.dat +1010 -0
- lalapps/data/LALSimNeutronStarEOS_RS.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_RS_BSK24.dat +1356 -0
- lalapps/data/LALSimNeutronStarEOS_SK255.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SK255_BSK24.dat +1066 -0
- lalapps/data/LALSimNeutronStarEOS_SK272.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKA.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKA_BSK24.dat +1433 -0
- lalapps/data/LALSimNeutronStarEOS_SKB.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKB_BSK24.dat +1373 -0
- lalapps/data/LALSimNeutronStarEOS_SKI2.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKI2_BSK24.dat +1348 -0
- lalapps/data/LALSimNeutronStarEOS_SKI3.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKI3_BSK24.dat +1355 -0
- lalapps/data/LALSimNeutronStarEOS_SKI4.dat +497 -0
- lalapps/data/LALSimNeutronStarEOS_SKI4_BSK24.dat +1348 -0
- lalapps/data/LALSimNeutronStarEOS_SKI5.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKI6.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKI6_BSK24.dat +1358 -0
- lalapps/data/LALSimNeutronStarEOS_SKMP.dat +498 -0
- lalapps/data/LALSimNeutronStarEOS_SKOP.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SKOP_BSK24.dat +1373 -0
- lalapps/data/LALSimNeutronStarEOS_SLY.dat +99 -0
- lalapps/data/LALSimNeutronStarEOS_SLY2.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SLY230A.dat +500 -0
- lalapps/data/LALSimNeutronStarEOS_SLY230A_BSK24.dat +1116 -0
- lalapps/data/LALSimNeutronStarEOS_SLY2_BSK24.dat +1106 -0
- lalapps/data/LALSimNeutronStarEOS_SLY4.dat +100 -0
- lalapps/data/LALSimNeutronStarEOS_SLY9.dat +498 -0
- lalapps/data/LALSimNeutronStarEOS_SLY9_BSK24.dat +1083 -0
- lalapps/data/LALSimNeutronStarEOS_SQM1.dat +176 -0
- lalapps/data/LALSimNeutronStarEOS_SQM2.dat +180 -0
- lalapps/data/LALSimNeutronStarEOS_SQM3.dat +176 -0
- lalapps/data/LALSimNeutronStarEOS_WFF1.dat +109 -0
- lalapps/data/LALSimNeutronStarEOS_WFF2.dat +109 -0
- lalapps/data/LALSimNeutronStarEOS_WFF3.dat +107 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_DDLZ1_BSK24.dat +1227 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_DDME2_BSK24.dat +1272 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_DDMEX_BSK24.dat +1280 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_GM1_BSK24.dat +1288 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_MTVTC_BSK24.dat +1288 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_NL3_BSK24.dat +1230 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_PKDD_BSK24.dat +1288 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_TM1_BSK24.dat +1288 -0
- lalapps/data/LALSimNeutronStarEOS_XMLSLZ_TW99_BSK24.dat +1288 -0
- lalapps/data/LIGO-P1200087-v18-AdV_BNS_OPTIMIZED.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_DESIGN.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_EARLY_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_EARLY_LOW.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_LATE_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_LATE_LOW.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_MID_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-AdV_MID_LOW.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_BNS_OPTIMIZED.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_DESIGN.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_EARLY_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_EARLY_LOW.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_LATE_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_LATE_LOW.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_MID_HIGH.txt +3000 -0
- lalapps/data/LIGO-P1200087-v18-aLIGO_MID_LOW.txt +3000 -0
- lalapps/data/LIGO-P1600143-v18-CE.txt +3000 -0
- lalapps/data/LIGO-P1600143-v18-CE_Pessimistic.txt +3000 -0
- lalapps/data/LIGO-P1600143-v18-CE_Wideband.txt +3000 -0
- lalapps/data/LIGO-P1600143-v18-ET_D.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-BHBH_20deg.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-High_Freq.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-NO_SRM.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-NSNS_Opt.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-ZERO_DET_high_P.txt +3000 -0
- lalapps/data/LIGO-T0900288-v3-ZERO_DET_low_P.txt +3000 -0
- lalapps/data/LIGO-T1600593-v1-KAGRA_Design.txt +4000 -0
- lalapps/data/LIGO-T1600593-v1-KAGRA_Early.txt +4000 -0
- lalapps/data/LIGO-T1600593-v1-KAGRA_Late.txt +4000 -0
- lalapps/data/LIGO-T1600593-v1-KAGRA_Mid.txt +4000 -0
- lalapps/data/LIGO-T1600593-v1-KAGRA_Opening.txt +4000 -0
- lalapps/data/LIGO-T1800042-v5-aLIGO_APLUS.txt +3000 -0
- lalapps/data/LIGO-T1800044-v5-aLIGO_DESIGN.txt +3000 -0
- lalapps/data/LIGO-T1800545-v1-AdV_O3low.txt +3000 -0
- lalapps/data/LIGO-T1800545-v1-AdV_O4.txt +3000 -0
- lalapps/data/LIGO-T1800545-v1-AdV_O4intermediate.txt +3000 -0
- lalapps/data/LIGO-T1800545-v1-KAGRA_128Mpc.txt +1000 -0
- lalapps/data/LIGO-T1800545-v1-KAGRA_25Mpc.txt +1000 -0
- lalapps/data/LIGO-T1800545-v1-KAGRA_80Mpc.txt +1000 -0
- lalapps/data/LIGO-T1800545-v1-aLIGO_140Mpc.txt +1000 -0
- lalapps/data/LIGO-T1800545-v1-aLIGO_175Mpc.txt +2792 -0
- lalapps/data/LIGO-T1800545-v1-aLIGO_O3low.txt +2792 -0
- lalapps/data/bimodalMeans.csv +3 -0
- lalapps/data/config_tiger_example.ini +150 -0
- lalapps/data/fiducialBBH.xml +67 -0
- lalapps/data/fiducialBNS.xml +67 -0
- lalapps/data/inspsrcs100Mpc.errors +38735 -0
- lalapps/data/lalinference_pipe_example.ini +573 -0
- lalapps/data/lib_pipe_example.ini +303 -0
- lalapps/data/power_pipe.ini +129 -0
- lalapps/data/unimodalMeans.csv +2 -0
- lalapps/git_version.py +64 -0
- lalburst/SimBurstUtils.py +324 -0
- lalburst/SnglBurstUtils.py +367 -0
- lalburst/__init__.py +7 -0
- lalburst/_lalburst.cpython-312-darwin.so +0 -0
- lalburst/_lalburst_swig.py +16 -0
- lalburst/binjfind.py +824 -0
- lalburst/bucluster.py +409 -0
- lalburst/burca.py +315 -0
- lalburst/burca_tailor.py +349 -0
- lalburst/cafe.py +579 -0
- lalburst/calc_likelihood.py +145 -0
- lalburst/cs_gamma.cpython-312-darwin.so +0 -0
- lalburst/date.py +118 -0
- lalburst/git_version.py +64 -0
- lalburst/offsetvector.py +278 -0
- lalburst/packing.py +170 -0
- lalburst/power.py +1457 -0
- lalburst/snglcluster.py +136 -0
- lalburst/snglcoinc.py +2637 -0
- lalburst/stringutils.py +607 -0
- lalburst/timeslides.py +236 -0
- lalframe/__init__.py +7 -0
- lalframe/_lalframe.cpython-312-darwin.so +0 -0
- lalframe/_lalframe_swig.py +14 -0
- lalframe/frread.py +324 -0
- lalframe/git_version.py +64 -0
- lalframe/utils/__init__.py +25 -0
- lalframe/utils/frtools.py +61 -0
- lalinference/__init__.py +7 -0
- lalinference/_bayespputils.cpython-312-darwin.so +0 -0
- lalinference/_lalinference.cpython-312-darwin.so +0 -0
- lalinference/_lalinference_swig.py +19 -0
- lalinference/bayespputils.py +7479 -0
- lalinference/bayestar/__init__.py +2 -0
- lalinference/bayestar/deprecation.py +72 -0
- lalinference/git_version.py +64 -0
- lalinference/imrtgr/__init__.py +0 -0
- lalinference/imrtgr/imrtgrutils.py +168 -0
- lalinference/imrtgr/nrutils.py +1366 -0
- lalinference/imrtgr/pneqns.py +250 -0
- lalinference/io/__init__.py +31 -0
- lalinference/io/hdf5.py +365 -0
- lalinference/lalinference_pipe_utils.py +3617 -0
- lalinference/nest2pos.py +151 -0
- lalinference/plot/__init__.py +34 -0
- lalinference/plot/spindisk.py +104 -0
- lalinference/tiger/__init__.py +0 -0
- lalinference/tiger/make_injtimes.py +634 -0
- lalinference/tiger/omegascans_dag.py +691 -0
- lalinference/tiger/postproc.py +1338 -0
- lalinference/wrapper.py +231 -0
- lalinspiral/__init__.py +7 -0
- lalinspiral/_lalinspiral.cpython-312-darwin.so +0 -0
- lalinspiral/_lalinspiral_swig.py +18 -0
- lalinspiral/_thinca.cpython-312-darwin.so +0 -0
- lalinspiral/git_version.py +64 -0
- lalinspiral/inspinjfind.py +485 -0
- lalinspiral/thinca.py +509 -0
- lalmetaio/__init__.py +7 -0
- lalmetaio/_lalmetaio.cpython-312-darwin.so +0 -0
- lalmetaio/_lalmetaio_swig.py +14 -0
- lalmetaio/git_version.py +64 -0
- lalpulsar/NstarTools.py +259 -0
- lalpulsar/PulsarParametersWrapper.py +938 -0
- lalpulsar/__init__.py +7 -0
- lalpulsar/_lalpulsar.cpython-312-darwin.so +0 -0
- lalpulsar/_lalpulsar_swig.py +17 -0
- lalpulsar/git_version.py +64 -0
- lalpulsar/knope_utils.py +6497 -0
- lalpulsar/lineFileParser.py +264 -0
- lalpulsar/metric_utils.py +78 -0
- lalpulsar/piecewise_model/__init__.py +7 -0
- lalpulsar/piecewise_model/basis_functions.py +156 -0
- lalpulsar/piecewise_model/class_definitions.py +323 -0
- lalpulsar/piecewise_model/errors.py +37 -0
- lalpulsar/piecewise_model/estimating_knots.py +833 -0
- lalpulsar/piecewise_model/gte_and_other_methods.py +189 -0
- lalpulsar/piecewise_model/mols_for_gte.py +269 -0
- lalpulsar/piecewise_model/pw_fstat.py +813 -0
- lalpulsar/piecewise_model/pw_model_simulations.py +156 -0
- lalpulsar/piecewise_model/sampling_methods.py +186 -0
- lalpulsar/piecewise_model/semicoherent_metric_methods.py +375 -0
- lalpulsar/piecewise_model/tbank_estimates.py +293 -0
- lalpulsar/public_sft_directory.py +82 -0
- lalpulsar/pulsarhtmlutils.py +1395 -0
- lalpulsar/pulsarpputils.py +3638 -0
- lalpulsar/simulateCW.py +602 -0
- lalpulsar/simulateHeterodynedCW.py +591 -0
- lalsimulation/__init__.py +7 -0
- lalsimulation/_lalsimulation.cpython-312-darwin.so +0 -0
- lalsimulation/_lalsimulation_swig.py +14 -0
- lalsimulation/git_version.py +64 -0
- lalsimulation/gwsignal/__init__.py +9 -0
- lalsimulation/gwsignal/core/__init__.py +2 -0
- lalsimulation/gwsignal/core/conditioning_subroutines.py +196 -0
- lalsimulation/gwsignal/core/errors.py +136 -0
- lalsimulation/gwsignal/core/gw.py +206 -0
- lalsimulation/gwsignal/core/parameter_conventions.py +122 -0
- lalsimulation/gwsignal/core/utils.py +329 -0
- lalsimulation/gwsignal/core/waveform.py +725 -0
- lalsimulation/gwsignal/core/waveform_conditioning.py +455 -0
- lalsimulation/gwsignal/models/__init__.py +29 -0
- lalsimulation/gwsignal/models/pyseobnr_model.py +452 -0
- lalsimulation/nrfits/NRSur3dq8Remnant.py +92 -0
- lalsimulation/nrfits/NRSur7dq4Remnant.py +469 -0
- lalsimulation/nrfits/__init__.py +1 -0
- lalsimulation/nrfits/eval_fits.py +364 -0
- lalsimulation/nrfits/nrfits.py +78 -0
- lalsimulation/nrfits/pn_spin_evolution_wrapper.py +92 -0
- lalsimulation/nrfits/quaternion_utils.py +74 -0
- lalsimulation/tilts_at_infinity/__init__.py +2 -0
- lalsimulation/tilts_at_infinity/calc_tilts_prec_avg_regularized.py +1424 -0
- lalsimulation/tilts_at_infinity/hybrid_spin_evolution.py +461 -0
- lalsimulation/tilts_at_infinity/tilts_at_infinity_utils.py +167 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesBurstPPAnalysis +305 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesBurstPostProc +1364 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesCombinePTMCMCh5s +100 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesCombinePosteriors +235 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesCompPos +1121 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesDIEvidence +68 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesGraceDBinfo +182 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesMCMC2pos +314 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesPPAnalysis +322 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesPlotSpinDisk +42 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesPosToSimBurst +227 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesPosToSimInspiral +307 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesPostProc +1345 -0
- lalsuite-7.26.2.dev20251210.data/scripts/cbcBayesThermoInt +107 -0
- lalsuite-7.26.2.dev20251210.data/scripts/imrtgr_imr_consistency_test +796 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_cache +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_fftw_wisdom +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_fftwf_wisdom +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_path2cache +148 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_searchsum2cache +172 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_simd_detect +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_tconvert +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lal_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputeAntennaPattern +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputeFstatBenchmark +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputeFstatLatticeCount +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputeFstatMCUpperLimit +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputeFstatistic_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ComputePSD +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_CopySFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_DistanceVsMass +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_DriveHoughMulti +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_FstatMetric_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_HierarchSearchGCT +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_HierarchicalSearch +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_MakeSFTDAG +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_MakeSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_Makefakedata_v4 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_Makefakedata_v5 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_PredictFstat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_PrintDetectorState +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_SFTclean +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_SFTvalidate +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_StringAddFrame +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_StringSearch +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_Weave +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_WeaveCompare +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_WeaveConcat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_WeaveSetup +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_WriteSFTsfromSFDBs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_animate +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_binj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_blindinj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_cache +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_cafe +99 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_calfacs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_cbc_stochasticbank +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_chirplen +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_coh_PTF_inspiral +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_coinj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_combine_crosscorr_toplists +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_compareFstats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_compareSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_cosmicstring_pipe +525 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_create_time_correction_ephemeris +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_dumpSFT +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_effdist +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_exc_resp +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fftw_wisdom +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fftwf_wisdom +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fits_header_getval +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fits_header_list +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fits_overview +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fits_table_list +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_fr_ninja +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_frextr +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_frinfo +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_frjoin +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_frread +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_frview +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_gwf2xml +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_heterodyne_pulsar +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_inspawgfile +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_inspfrinj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_inspinj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_inspiralDistance +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_knope +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_knope_automation_script +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_knope_collate_results +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_knope_result_page +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_makeblindinj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_makeblindinj_himass +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ninja +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_path2cache +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_power +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_power_likelihood_pipe +219 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_power_pipe +417 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_pulsar_crosscorr_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_pulsar_frequency_evolution +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_pulsar_parameter_estimation_nested +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_random_bank +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_randombank +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_run_pulsar_crosscorr_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_searchsum2cache +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_spec_avg +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_spec_avg_long +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_spec_coherence +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_spininj +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_splitSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_splitbank +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_ssbtodetector +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_apply_vetoes +171 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_calc_likelihood +172 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_contour_plotter +141 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_contour_plotter_largeloops +133 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_cs_gamma +110 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_cs_gamma_largeloops +119 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_final +1064 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_meas_likelihood +264 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_plot_binj +543 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_string_plot_likelihood +380 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_synthesizeBstatMC +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_synthesizeLVStats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_synthesizeTransientStats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_tconvert +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_tmpltbank +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalapps_xtefitstoframe +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_cluster +156 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_coinc +224 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_cut +425 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_gen_timeslides +254 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_inj_pic +254 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_injfind +170 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_plot_tisi +165 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_power_calc_likelihood +182 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_power_final +1369 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_power_meas_likelihood +206 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_power_plot_binj +934 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_power_plot_binjtf +302 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalburst_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-cat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-cksum +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-cut +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-dump +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-fmt +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-paste +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-print +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-split +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-stat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-stream +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalfr-vis +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalframe_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_bench +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_burst +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_burst_pp_pipe +220 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_coherence_test +139 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_compute_roq_weights +404 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_cpnest +58 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_datadump +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_evolve_spins_and_append_samples +202 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_injectedlike +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_merge_posteriors +57 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_mpi_wrapper +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_multi_pipe +144 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_nest +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_nest2pos +286 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_pipe +512 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_pp_pipe +229 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_review_test +362 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinference_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinspiral_injfind +206 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinspiral_thinca +240 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalinspiral_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalmetaio_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputeAntennaPattern +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputeFstatBenchmark +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputeFstatLatticeCount +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputeFstatMCUpperLimit +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputeFstatistic_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ComputePSD +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_CopyPublicSFTs +216 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_DriveHoughMulti +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_FstatMetric_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_HierarchSearchGCT +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_HierarchicalSearch +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_MakeSFTDAG +1142 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_MakeSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_Makefakedata_v4 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_Makefakedata_v5 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_MoveSFTs +208 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_PiecewiseSearch +963 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_PredictFstat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_PrintDetectorState +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_SFTclean +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_SFTvalidate +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_Weave +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_WeaveCompare +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_WeaveConcat +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_WeaveSetup +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_WriteSFTsfromSFDBs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_compareFstats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_compareSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_create_time_correction_ephemeris +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_crosscorr_v2 +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_dumpSFT +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_fits_header_getval +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_fits_header_list +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_fits_overview +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_fits_table_list +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_frequency_evolution +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_heterodyne +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_knope +145 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_knope_automation_script +731 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_knope_collate_results +675 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_knope_result_page +2977 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_parameter_estimation_nested +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_spec_avg +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_spec_avg_long +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_spec_coherence +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_splitSFTs +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_ssbtodetector +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_synthesizeBstatMC +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_synthesizeLVStats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_synthesizeTransientStats +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalpulsar_version +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-bh-qnmode +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-bh-ringdown +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-bh-sphwf +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-burst +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-detector-noise +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-detector-strain +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-inject +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-inspiral +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-ns-eos-table +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-ns-mass-radius +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-ns-params +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-sgwb +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsim-unicorn +6 -0
- lalsuite-7.26.2.dev20251210.data/scripts/lalsimulation_version +6 -0
- lalsuite-7.26.2.dev20251210.dist-info/METADATA +90 -0
- lalsuite-7.26.2.dev20251210.dist-info/RECORD +749 -0
- lalsuite-7.26.2.dev20251210.dist-info/WHEEL +6 -0
- lalsuite-7.26.2.dev20251210.dist-info/licenses/COPYING +339 -0
- lalsuite-7.26.2.dev20251210.dist-info/top_level.txt +9 -0
lal/rate.py
ADDED
|
@@ -0,0 +1,2455 @@
|
|
|
1
|
+
# Copyright (C) 2006--2021 Kipp Cannon
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or modify it
|
|
4
|
+
# under the terms of the GNU General Public License as published by the
|
|
5
|
+
# Free Software Foundation; either version 2 of the License, or (at your
|
|
6
|
+
# option) any later version.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful, but
|
|
9
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
11
|
+
# Public License for more details.
|
|
12
|
+
#
|
|
13
|
+
# You should have received a copy of the GNU General Public License along
|
|
14
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
15
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# =============================================================================
|
|
20
|
+
#
|
|
21
|
+
# Preamble
|
|
22
|
+
#
|
|
23
|
+
# =============================================================================
|
|
24
|
+
#
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
This module provides facilities for studying impulsive events. A number of
|
|
29
|
+
multi-dimensional binning functions are provided, as well as code to
|
|
30
|
+
convolve binned data with integral- and phase-preserving window functions
|
|
31
|
+
to produce smoothed representations of data sets. This is particularly
|
|
32
|
+
well suited for use in computing moving-average rate data from collections
|
|
33
|
+
of impulsive events, elliminating binning artifacts from histograms, and
|
|
34
|
+
smoothing contour plots.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
from functools import reduce
|
|
39
|
+
try:
|
|
40
|
+
from fpconst import PosInf, NegInf
|
|
41
|
+
except ImportError:
|
|
42
|
+
# fpconst is not part of the standard library and might not
|
|
43
|
+
# be available
|
|
44
|
+
PosInf = float("+inf")
|
|
45
|
+
NegInf = float("-inf")
|
|
46
|
+
import itertools
|
|
47
|
+
import math
|
|
48
|
+
import numpy
|
|
49
|
+
import random
|
|
50
|
+
import scipy
|
|
51
|
+
__numpy__version__ = tuple(map(int, numpy.__version__.strip().split(".")[:2]))
|
|
52
|
+
__scipy__version__ = tuple(map(int, scipy.__version__.strip().split(".")[:2]))
|
|
53
|
+
# FIXME Uncomment these lines when the interpolator problem is fixed or when we
|
|
54
|
+
# figure out the correct version numbers to check for
|
|
55
|
+
'''
|
|
56
|
+
if __scipy__version__ >= (0, 9) and __numpy__version__ >= (1, 7):
|
|
57
|
+
from scipy.interpolate import interp1d, interp2d, LinearNDInterpolator
|
|
58
|
+
else:
|
|
59
|
+
# pre scipy/numpy 0.9/1.7 had busted/missing interpolation code.
|
|
60
|
+
# replacements are provided below
|
|
61
|
+
pass
|
|
62
|
+
'''
|
|
63
|
+
from scipy.signal import signaltools
|
|
64
|
+
|
|
65
|
+
import igwn_segments as segments
|
|
66
|
+
|
|
67
|
+
from igwn_ligolw import ligolw
|
|
68
|
+
from igwn_ligolw import types as ligolw_types
|
|
69
|
+
import lal
|
|
70
|
+
from . import iterutils
|
|
71
|
+
from . import git_version
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
|
|
75
|
+
__version__ = "git id %s" % git_version.id
|
|
76
|
+
__date__ = git_version.date
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# =============================================================================
|
|
81
|
+
#
|
|
82
|
+
# Bins
|
|
83
|
+
#
|
|
84
|
+
# =============================================================================
|
|
85
|
+
#
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Bins(object):
|
|
89
|
+
"""
|
|
90
|
+
Parent class for 1-dimensional binnings. This class is not
|
|
91
|
+
intended to be used directly, but to be subclassed for use in real
|
|
92
|
+
bins classes.
|
|
93
|
+
"""
|
|
94
|
+
def __init__(self):
|
|
95
|
+
"""
|
|
96
|
+
Initialize a Bins instance. Subclasses must override this
|
|
97
|
+
method.
|
|
98
|
+
"""
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
def __len__(self):
|
|
102
|
+
"""
|
|
103
|
+
The number of bins in the binning. Subclasses must
|
|
104
|
+
override this method.
|
|
105
|
+
"""
|
|
106
|
+
raise NotImplementedError
|
|
107
|
+
|
|
108
|
+
def __eq__(self, other):
|
|
109
|
+
"""
|
|
110
|
+
Two binnings are the same if they are instances of the same
|
|
111
|
+
class, and describe the same binnings. Subclasses should
|
|
112
|
+
override this method but need not.
|
|
113
|
+
"""
|
|
114
|
+
raise NotImplementedError
|
|
115
|
+
|
|
116
|
+
def __getitem__(self, x):
|
|
117
|
+
"""
|
|
118
|
+
Convert a co-ordinate to a bin index. The co-ordinate can
|
|
119
|
+
be a single value, or a Python slice instance describing a
|
|
120
|
+
range of values. If a single value is given, it is mapped
|
|
121
|
+
to the bin index corresponding to that value. If a slice
|
|
122
|
+
is given, it is converted to a slice whose lower bound is
|
|
123
|
+
the index of the bin in which the slice's lower bound
|
|
124
|
+
falls, and whose upper bound is 1 greater than the index of
|
|
125
|
+
the bin in which the slice's upper bound falls. Steps are
|
|
126
|
+
not supported in slices.
|
|
127
|
+
|
|
128
|
+
Subclasses must override this method, but may chain to this
|
|
129
|
+
to handle slices:
|
|
130
|
+
|
|
131
|
+
def __getitem__(self, x):
|
|
132
|
+
if type(x) is slice:
|
|
133
|
+
return super(type(self), self).__getitem__(x)
|
|
134
|
+
# now handle non-slices ...
|
|
135
|
+
"""
|
|
136
|
+
# assumes x is a slice. works with anything that defines
|
|
137
|
+
# .start, .stop and .step (but .step must be None).
|
|
138
|
+
if x.step is not None and x.step != 1:
|
|
139
|
+
raise NotImplementedError("step not supported: %s" % repr(x))
|
|
140
|
+
return slice(self[x.start] if x.start is not None else 0, self[x.stop] + 1 if x.stop is not None else len(self))
|
|
141
|
+
|
|
142
|
+
def __iter__(self):
|
|
143
|
+
"""
|
|
144
|
+
If __iter__ does not exist, Python uses __getitem__ with
|
|
145
|
+
range(0) as input to define iteration. This is nonsensical
|
|
146
|
+
for bin objects, so explicitly unsupport iteration.
|
|
147
|
+
Subclasses do not need to override this method.
|
|
148
|
+
"""
|
|
149
|
+
raise NotImplementedError
|
|
150
|
+
|
|
151
|
+
def lower(self):
|
|
152
|
+
"""
|
|
153
|
+
Return an array containing the locations of the lower
|
|
154
|
+
boundaries of the bins. Subclasses should override this
|
|
155
|
+
method.
|
|
156
|
+
"""
|
|
157
|
+
raise NotImplementedError
|
|
158
|
+
|
|
159
|
+
def centres(self):
|
|
160
|
+
"""
|
|
161
|
+
Return an array containing the locations of the bin
|
|
162
|
+
centres. Subclasses should override this method.
|
|
163
|
+
"""
|
|
164
|
+
raise NotImplementedError
|
|
165
|
+
|
|
166
|
+
def upper(self):
|
|
167
|
+
"""
|
|
168
|
+
Return an array containing the locations of the upper
|
|
169
|
+
boundaries of the bins. Subclasses should override this
|
|
170
|
+
method.
|
|
171
|
+
"""
|
|
172
|
+
raise NotImplementedError
|
|
173
|
+
|
|
174
|
+
#
|
|
175
|
+
# Sample from the binning
|
|
176
|
+
#
|
|
177
|
+
|
|
178
|
+
def randcoord(self, n = 1., domain = slice(None, None)):
|
|
179
|
+
"""
|
|
180
|
+
Generator yielding a sequence of x, ln(P(x)) tuples where x
|
|
181
|
+
is a randomly-chosen co-ordinate and P(x) is the PDF from
|
|
182
|
+
which x has been drawn evaluated at x. Each co-ordinate is
|
|
183
|
+
drawn uniformly from within a bin, which has been drawn
|
|
184
|
+
from a distribution whose CDF goes as [bin index]^{n}. For
|
|
185
|
+
more information on how bins are drawn, see
|
|
186
|
+
lal.iterutils.randindex.
|
|
187
|
+
|
|
188
|
+
If a domain is given, the values returned fall within
|
|
189
|
+
[start, stop]. If start or stop is None, the corresponding
|
|
190
|
+
end of the binning is used. If start or stop does not
|
|
191
|
+
correspond exactly to a bin boundary, the probability of
|
|
192
|
+
drawing a value from that bin is unchanged, but the values
|
|
193
|
+
drawn from that bin will be restricted to the allowed part
|
|
194
|
+
of the bin (the PDF is adjusted to reflect this). No
|
|
195
|
+
values are returned from bins with infinite size (after
|
|
196
|
+
clipping them to the requested domain), and the PDF is
|
|
197
|
+
adjusted to reflect this.
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
|
|
201
|
+
>>> import math
|
|
202
|
+
>>> # natural log of 1/10
|
|
203
|
+
>>> print("%.15g" % math.log(1./10))
|
|
204
|
+
-2.30258509299405
|
|
205
|
+
>>> # linear bins spanning [0, 10]
|
|
206
|
+
>>> bins = LinearBins(0, 10, 5)
|
|
207
|
+
>>> # draw a random value, ln P(value) = ln 1/10
|
|
208
|
+
>>> next(bins.randcoord()) # doctest: +ELLIPSIS
|
|
209
|
+
(..., -2.3025850929940455)
|
|
210
|
+
>>> # binning with infinite boundaries
|
|
211
|
+
>>> bins = ATanBins(-1, +1, 4)
|
|
212
|
+
>>> # will ask for values in [0.5, +inf], i.e. the last two
|
|
213
|
+
>>> # bins, but values from final bin will be disallowed, so
|
|
214
|
+
>>> # return values will be uniform in part of the second
|
|
215
|
+
>>> # last bin, [0.5, 0.6366]
|
|
216
|
+
>>> print("%.15g" % math.log(1. / (bins.upper()[-2] - 0.5)))
|
|
217
|
+
1.99055359585182
|
|
218
|
+
>>> next(bins.randcoord(domain = slice(0.5, None))) # doctest: +ELLIPSIS
|
|
219
|
+
(..., 1.9905535958518226)
|
|
220
|
+
>>> # things that aren't supported:
|
|
221
|
+
>>> # domain slice with a step
|
|
222
|
+
>>> next(LinearBins(0, 10, 1).randcoord(domain = slice(None, None, 2)))
|
|
223
|
+
Traceback (most recent call last):
|
|
224
|
+
...
|
|
225
|
+
NotImplementedError: step not supported: slice(None, None, 2)
|
|
226
|
+
|
|
227
|
+
Subclasses should not override this method.
|
|
228
|
+
"""
|
|
229
|
+
if len(self) < 1:
|
|
230
|
+
raise ValueError("empty binning")
|
|
231
|
+
if domain.step is not None:
|
|
232
|
+
raise NotImplementedError("step not supported: %s" % repr(domain))
|
|
233
|
+
# avoid symbol look-ups in the sampling loop
|
|
234
|
+
isinf = math.isinf
|
|
235
|
+
uniform = random.uniform
|
|
236
|
+
# determine boundaries and index range
|
|
237
|
+
l = self.lower()
|
|
238
|
+
u = self.upper()
|
|
239
|
+
lo, hi, _ = self[domain].indices(len(l))
|
|
240
|
+
if domain.start is not None:
|
|
241
|
+
assert l[lo] <= domain.start
|
|
242
|
+
l[lo] = domain.start
|
|
243
|
+
if domain.stop is not None:
|
|
244
|
+
assert u[hi - 1] >= domain.stop
|
|
245
|
+
u[hi - 1] = domain.stop
|
|
246
|
+
if isinf(u[lo] - l[lo]):
|
|
247
|
+
lo += 1
|
|
248
|
+
if isinf(u[hi - 1] - l[hi - 1]):
|
|
249
|
+
hi -= 1
|
|
250
|
+
if not lo < hi:
|
|
251
|
+
raise ValueError("slice too small")
|
|
252
|
+
# log() implicitly checks that the boundary adjustments
|
|
253
|
+
# above haven't made any bins <= 0 in size. converting
|
|
254
|
+
# everything to tuples makes the sampling loop faster
|
|
255
|
+
ln_dx = tuple(numpy.log(u - l))
|
|
256
|
+
l = tuple(l)
|
|
257
|
+
u = tuple(u)
|
|
258
|
+
# one last safety check
|
|
259
|
+
if any(map(isinf, ln_dx[lo:hi])):
|
|
260
|
+
raise ValueError("unavoidable infinite bin detected")
|
|
261
|
+
# generate samples
|
|
262
|
+
for i, ln_Pi in iterutils.randindex(lo, hi, n = n):
|
|
263
|
+
yield uniform(l[i], u[i]), ln_Pi - ln_dx[i]
|
|
264
|
+
|
|
265
|
+
#
|
|
266
|
+
# XML I/O related methods and data
|
|
267
|
+
#
|
|
268
|
+
|
|
269
|
+
@staticmethod
|
|
270
|
+
def xml_bins_name_enc(name, suffix = "pylal_rate_bins"):
|
|
271
|
+
"""
|
|
272
|
+
For internal use by XML I/O code. Subclasses should not
|
|
273
|
+
override this method.
|
|
274
|
+
"""
|
|
275
|
+
return "%s:%s" % (name, suffix)
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def xml_bins_name_dec(name, suffix = "pylal_rate_bins"):
|
|
279
|
+
"""
|
|
280
|
+
For internal use by XML I/O code. Subclasses should not
|
|
281
|
+
override this method.
|
|
282
|
+
"""
|
|
283
|
+
name = name.rsplit(":", 1)
|
|
284
|
+
if name[-1] != suffix:
|
|
285
|
+
raise ValueError(name)
|
|
286
|
+
return name[0]
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
def xml_bins_check(cls, elem, name):
|
|
290
|
+
"""
|
|
291
|
+
For internal use by XML I/O code. Subclasses should not
|
|
292
|
+
override this method.
|
|
293
|
+
"""
|
|
294
|
+
return elem.tagName == ligolw.Param.tagName and elem.hasAttribute("Name") and name == cls.xml_bins_name_dec(elem.Name)
|
|
295
|
+
|
|
296
|
+
def to_xml(self):
|
|
297
|
+
"""
|
|
298
|
+
Construct a LIGO Light Weight XML representation of the
|
|
299
|
+
Bins instance. Subclasses must override this method to be
|
|
300
|
+
serializable to LIGO Light Weight XML, otherwise they need
|
|
301
|
+
not override it.
|
|
302
|
+
"""
|
|
303
|
+
raise NotImplementedError
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def from_xml(cls, xml):
|
|
307
|
+
"""
|
|
308
|
+
From the XML Param element at xml, return the Bins object
|
|
309
|
+
it describes. Subclasses must override this method to be
|
|
310
|
+
de-serializable from LIGO Light Weight XML, otherwise they
|
|
311
|
+
need not override it.
|
|
312
|
+
"""
|
|
313
|
+
raise NotImplementedError
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class LoHiCountBins(Bins):
|
|
317
|
+
"""
|
|
318
|
+
Base class to help implement binnings that can be defined by a
|
|
319
|
+
lower bound, an upper bound, and a count of bins. This is not a
|
|
320
|
+
binning.
|
|
321
|
+
"""
|
|
322
|
+
def __init__(self, min, max, n):
|
|
323
|
+
"""
|
|
324
|
+
The three arguments are the minimum and maximum of the
|
|
325
|
+
values spanned by the bins, and the number of bins to place
|
|
326
|
+
between them.
|
|
327
|
+
"""
|
|
328
|
+
if not isinstance(n, int):
|
|
329
|
+
raise TypeError(n)
|
|
330
|
+
if n < 1:
|
|
331
|
+
raise ValueError(n)
|
|
332
|
+
if max <= min:
|
|
333
|
+
raise ValueError((min, max))
|
|
334
|
+
self.min = min
|
|
335
|
+
self.max = max
|
|
336
|
+
self.n = n
|
|
337
|
+
|
|
338
|
+
def __len__(self):
|
|
339
|
+
return self.n
|
|
340
|
+
|
|
341
|
+
def __eq__(self, other):
|
|
342
|
+
"""
|
|
343
|
+
Two binnings are the same if they are instances of the same
|
|
344
|
+
class, have the same lower and upper bounds, and the same
|
|
345
|
+
count of bins.
|
|
346
|
+
"""
|
|
347
|
+
return isinstance(other, type(self)) and (self.min, self.max, self.n) == (other.min, other.max, other.n)
|
|
348
|
+
|
|
349
|
+
#
|
|
350
|
+
# XML I/O related methods and data
|
|
351
|
+
#
|
|
352
|
+
|
|
353
|
+
def to_xml(self):
|
|
354
|
+
"""
|
|
355
|
+
Construct a LIGO Light Weight XML representation of the
|
|
356
|
+
Bins instance. Subclasses must define the .xml_bins_name
|
|
357
|
+
class attribute.
|
|
358
|
+
"""
|
|
359
|
+
return ligolw.Param.from_pyvalue(self.xml_bins_name_enc(self.xml_bins_name), "%s,%s,%s" % (ligolw_types.FormatFunc["real_8"](self.min), ligolw_types.FormatFunc["real_8"](self.max), ligolw_types.FormatFunc["int_8s"](self.n)))
|
|
360
|
+
|
|
361
|
+
@classmethod
|
|
362
|
+
def from_xml(cls, xml):
|
|
363
|
+
"""
|
|
364
|
+
From the XML Param element at xml, return the Bins object
|
|
365
|
+
it describes. Subclasses must define the .xml_bins_name
|
|
366
|
+
class attribute.
|
|
367
|
+
"""
|
|
368
|
+
if not cls.xml_bins_check(xml, cls.xml_bins_name):
|
|
369
|
+
raise ValueError("not a %s" % repr(cls))
|
|
370
|
+
lo, hi, n = xml.pcdata.split(",")
|
|
371
|
+
lo = ligolw_types.ToPyType["real_8"](lo)
|
|
372
|
+
hi = ligolw_types.ToPyType["real_8"](hi)
|
|
373
|
+
n = ligolw_types.ToPyType["int_8s"](n)
|
|
374
|
+
return cls(lo, hi, n)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class IrregularBins(Bins):
|
|
378
|
+
"""
|
|
379
|
+
Bins with arbitrary, irregular spacing. We only require strict
|
|
380
|
+
monotonicity of the bin boundaries. N boundaries define N-1 bins.
|
|
381
|
+
|
|
382
|
+
Example:
|
|
383
|
+
|
|
384
|
+
>>> x = IrregularBins([0.0, 11.0, 15.0, numpy.inf])
|
|
385
|
+
>>> len(x)
|
|
386
|
+
3
|
|
387
|
+
>>> x[1]
|
|
388
|
+
0
|
|
389
|
+
>>> x[1.5]
|
|
390
|
+
0
|
|
391
|
+
>>> x[13]
|
|
392
|
+
1
|
|
393
|
+
>>> x[25]
|
|
394
|
+
2
|
|
395
|
+
>>> x[4:17]
|
|
396
|
+
slice(0, 3, None)
|
|
397
|
+
>>> IrregularBins([0.0, 15.0, 11.0])
|
|
398
|
+
Traceback (most recent call last):
|
|
399
|
+
...
|
|
400
|
+
ValueError: non-monotonic boundaries provided
|
|
401
|
+
>>> y = IrregularBins([0.0, 11.0, 15.0, numpy.inf])
|
|
402
|
+
>>> x == y
|
|
403
|
+
True
|
|
404
|
+
>>> import sys
|
|
405
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
406
|
+
<Param Type="lstring" Name="irregularbins:pylal_rate_bins:param">0,11,15,inf</Param>
|
|
407
|
+
>>> IrregularBins.from_xml(x.to_xml()) == x
|
|
408
|
+
True
|
|
409
|
+
"""
|
|
410
|
+
def __init__(self, boundaries):
|
|
411
|
+
"""
|
|
412
|
+
Initialize a set of custom bins with the bin boundaries.
|
|
413
|
+
This includes all left edges plus the right edge. The
|
|
414
|
+
boundaries must be monotonic and there must be at least two
|
|
415
|
+
elements.
|
|
416
|
+
"""
|
|
417
|
+
# check pre-conditions
|
|
418
|
+
if len(boundaries) < 2:
|
|
419
|
+
raise ValueError("less than two boundaries provided")
|
|
420
|
+
self.boundaries = numpy.array(boundaries)
|
|
421
|
+
if (self.boundaries[:-1] > self.boundaries[1:]).any():
|
|
422
|
+
raise ValueError("non-monotonic boundaries provided")
|
|
423
|
+
self.lo, self.hi = float(self.boundaries[0]), float(self.boundaries[-1])
|
|
424
|
+
|
|
425
|
+
def __eq__(self, other):
|
|
426
|
+
"""
|
|
427
|
+
Two binnings are the same if they are instances of the same
|
|
428
|
+
class, and have the same boundaries.
|
|
429
|
+
"""
|
|
430
|
+
return isinstance(other, type(self)) and (self.boundaries == other.boundaries).all()
|
|
431
|
+
|
|
432
|
+
def __len__(self):
|
|
433
|
+
return len(self.boundaries) - 1
|
|
434
|
+
|
|
435
|
+
def __getitem__(self, x):
|
|
436
|
+
# slice cannot be sub-classed so no need to use
|
|
437
|
+
# isinstance()
|
|
438
|
+
if type(x) is slice:
|
|
439
|
+
return super(IrregularBins, self).__getitem__(x)
|
|
440
|
+
if self.lo <= x < self.hi:
|
|
441
|
+
return self.boundaries.searchsorted(x, side = "right") - 1
|
|
442
|
+
# special measure-zero edge case
|
|
443
|
+
if x == self.hi:
|
|
444
|
+
return len(self.boundaries) - 2
|
|
445
|
+
raise IndexError(x)
|
|
446
|
+
|
|
447
|
+
def lower(self):
|
|
448
|
+
return self.boundaries[:-1]
|
|
449
|
+
|
|
450
|
+
def upper(self):
|
|
451
|
+
return self.boundaries[1:]
|
|
452
|
+
|
|
453
|
+
def centres(self):
|
|
454
|
+
return (self.lower() + self.upper()) / 2.0
|
|
455
|
+
|
|
456
|
+
#
|
|
457
|
+
# XML I/O related methods and data
|
|
458
|
+
#
|
|
459
|
+
|
|
460
|
+
xml_bins_name = "irregularbins"
|
|
461
|
+
|
|
462
|
+
def to_xml(self):
|
|
463
|
+
"""
|
|
464
|
+
Construct a LIGO Light Weight XML representation of the
|
|
465
|
+
Bins instance.
|
|
466
|
+
"""
|
|
467
|
+
return ligolw.Param.from_pyvalue(self.xml_bins_name_enc(self.xml_bins_name), ",".join(map(ligolw_types.FormatFunc["real_8"], self.boundaries)))
|
|
468
|
+
|
|
469
|
+
@classmethod
|
|
470
|
+
def from_xml(cls, xml):
|
|
471
|
+
"""
|
|
472
|
+
From the XML Param element at xml, return the Bins object
|
|
473
|
+
it describes.
|
|
474
|
+
"""
|
|
475
|
+
if not cls.xml_bins_check(xml, cls.xml_bins_name):
|
|
476
|
+
raise ValueError("not a %s" % repr(cls))
|
|
477
|
+
return cls(map(ligolw_types.ToPyType["real_8"], xml.pcdata.split(",")))
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class LinearBins(LoHiCountBins):
|
|
481
|
+
"""
|
|
482
|
+
Linearly-spaced bins. There are n bins of equal size, the first
|
|
483
|
+
bin starts on the lower bound and the last bin ends on the upper
|
|
484
|
+
bound inclusively.
|
|
485
|
+
|
|
486
|
+
Example:
|
|
487
|
+
|
|
488
|
+
>>> x = LinearBins(1.0, 25.0, 3)
|
|
489
|
+
>>> x.lower()
|
|
490
|
+
array([ 1., 9., 17.])
|
|
491
|
+
>>> x.upper()
|
|
492
|
+
array([ 9., 17., 25.])
|
|
493
|
+
>>> x.centres()
|
|
494
|
+
array([ 5., 13., 21.])
|
|
495
|
+
>>> x[1]
|
|
496
|
+
0
|
|
497
|
+
>>> x[1.5]
|
|
498
|
+
0
|
|
499
|
+
>>> x[10]
|
|
500
|
+
1
|
|
501
|
+
>>> x[25]
|
|
502
|
+
2
|
|
503
|
+
>>> x[0:27]
|
|
504
|
+
Traceback (most recent call last):
|
|
505
|
+
...
|
|
506
|
+
IndexError: 0
|
|
507
|
+
>>> x[1:25]
|
|
508
|
+
slice(0, 3, None)
|
|
509
|
+
>>> x[:25]
|
|
510
|
+
slice(0, 3, None)
|
|
511
|
+
>>> x[10:16.9]
|
|
512
|
+
slice(1, 2, None)
|
|
513
|
+
>>> x[10:17]
|
|
514
|
+
slice(1, 3, None)
|
|
515
|
+
>>> x[10:]
|
|
516
|
+
slice(1, 3, None)
|
|
517
|
+
>>> import sys
|
|
518
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
519
|
+
<Param Type="lstring" Name="linbins:pylal_rate_bins:param">1,25,3</Param>
|
|
520
|
+
>>> LinearBins.from_xml(x.to_xml()) == x
|
|
521
|
+
True
|
|
522
|
+
"""
|
|
523
|
+
def __init__(self, min, max, n):
|
|
524
|
+
super(LinearBins, self).__init__(min, max, n)
|
|
525
|
+
self.delta = float(max - min) / n
|
|
526
|
+
|
|
527
|
+
def __getitem__(self, x):
|
|
528
|
+
# slice cannot be sub-classed so no need to use
|
|
529
|
+
# isinstance()
|
|
530
|
+
if type(x) is slice:
|
|
531
|
+
return super(LinearBins, self).__getitem__(x)
|
|
532
|
+
if self.min <= x < self.max:
|
|
533
|
+
return int(math.floor((x - self.min) / self.delta))
|
|
534
|
+
if x == self.max:
|
|
535
|
+
# special "measure zero" corner case
|
|
536
|
+
return len(self) - 1
|
|
537
|
+
raise IndexError(x)
|
|
538
|
+
|
|
539
|
+
def lower(self):
|
|
540
|
+
return numpy.linspace(self.min, self.max - self.delta, len(self))
|
|
541
|
+
|
|
542
|
+
def centres(self):
|
|
543
|
+
return numpy.linspace(self.min + self.delta / 2., self.max - self.delta / 2., len(self))
|
|
544
|
+
|
|
545
|
+
def upper(self):
|
|
546
|
+
return numpy.linspace(self.min + self.delta, self.max, len(self))
|
|
547
|
+
|
|
548
|
+
#
|
|
549
|
+
# XML I/O related methods and data
|
|
550
|
+
#
|
|
551
|
+
|
|
552
|
+
xml_bins_name = "linbins"
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
class LinearPlusOverflowBins(LoHiCountBins):
|
|
556
|
+
"""
|
|
557
|
+
Linearly-spaced bins with overflow at the edges. There are n-2
|
|
558
|
+
bins of equal size. The bin 1 starts on the lower bound and bin
|
|
559
|
+
n-2 ends on the upper bound. Bins 0 and n-1 are overflow going
|
|
560
|
+
from -infinity to the lower bound and from the upper bound to
|
|
561
|
+
+infinity respectively. Must have n >= 3.
|
|
562
|
+
|
|
563
|
+
Example:
|
|
564
|
+
|
|
565
|
+
>>> x = LinearPlusOverflowBins(1.0, 25.0, 5)
|
|
566
|
+
>>> x.centres()
|
|
567
|
+
array([-inf, 5., 13., 21., inf])
|
|
568
|
+
>>> x.lower()
|
|
569
|
+
array([-inf, 1., 9., 17., 25.])
|
|
570
|
+
>>> x.upper()
|
|
571
|
+
array([ 1., 9., 17., 25., inf])
|
|
572
|
+
>>> x[float("-inf")]
|
|
573
|
+
0
|
|
574
|
+
>>> x[0]
|
|
575
|
+
0
|
|
576
|
+
>>> x[1]
|
|
577
|
+
1
|
|
578
|
+
>>> x[10]
|
|
579
|
+
2
|
|
580
|
+
>>> x[24.99999999]
|
|
581
|
+
3
|
|
582
|
+
>>> x[25]
|
|
583
|
+
4
|
|
584
|
+
>>> x[100]
|
|
585
|
+
4
|
|
586
|
+
>>> x[float("+inf")]
|
|
587
|
+
4
|
|
588
|
+
>>> x[float("-inf"):9]
|
|
589
|
+
slice(0, 3, None)
|
|
590
|
+
>>> x[9:float("+inf")]
|
|
591
|
+
slice(2, 5, None)
|
|
592
|
+
>>> import sys
|
|
593
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
594
|
+
<Param Type="lstring" Name="linplusoverflowbins:pylal_rate_bins:param">1,25,5</Param>
|
|
595
|
+
>>> LinearPlusOverflowBins.from_xml(x.to_xml()) == x
|
|
596
|
+
True
|
|
597
|
+
"""
|
|
598
|
+
def __init__(self, min, max, n):
|
|
599
|
+
if n < 3:
|
|
600
|
+
raise ValueError("n must be >= 3")
|
|
601
|
+
super(LinearPlusOverflowBins, self).__init__(min, max, n)
|
|
602
|
+
self.delta = float(max - min) / (n - 2)
|
|
603
|
+
|
|
604
|
+
def __getitem__(self, x):
|
|
605
|
+
# slice cannot be sub-classed so no need to use
|
|
606
|
+
# isinstance()
|
|
607
|
+
if type(x) is slice:
|
|
608
|
+
return super(LinearPlusOverflowBins, self).__getitem__(x)
|
|
609
|
+
if self.min <= x < self.max:
|
|
610
|
+
return int(math.floor((x - self.min) / self.delta)) + 1
|
|
611
|
+
if x >= self.max:
|
|
612
|
+
# +infinity overflow bin
|
|
613
|
+
return len(self) - 1
|
|
614
|
+
if x < self.min:
|
|
615
|
+
# -infinity overflow bin
|
|
616
|
+
return 0
|
|
617
|
+
raise IndexError(x)
|
|
618
|
+
|
|
619
|
+
def lower(self):
|
|
620
|
+
return numpy.concatenate((numpy.array([NegInf]), numpy.linspace(self.min, self.max - self.delta, len(self) - 1)))
|
|
621
|
+
|
|
622
|
+
def centres(self):
|
|
623
|
+
return numpy.concatenate((numpy.array([NegInf]), numpy.linspace(self.min + self.delta / 2., self.max - self.delta / 2., len(self) - 2), numpy.array([PosInf])))
|
|
624
|
+
|
|
625
|
+
def upper(self):
|
|
626
|
+
return numpy.concatenate((numpy.linspace(self.min + self.delta, self.max, len(self) - 1), numpy.array([PosInf])))
|
|
627
|
+
|
|
628
|
+
#
|
|
629
|
+
# XML I/O related methods and data
|
|
630
|
+
#
|
|
631
|
+
|
|
632
|
+
xml_bins_name = "linplusoverflowbins"
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class LogarithmicBins(LoHiCountBins):
|
|
636
|
+
"""
|
|
637
|
+
Logarithmically-spaced bins. There are n bins, each of whose upper
|
|
638
|
+
and lower bounds differ by the same factor. The first bin starts
|
|
639
|
+
on the lower bound, and the last bin ends on the upper bound
|
|
640
|
+
inclusively.
|
|
641
|
+
|
|
642
|
+
Example:
|
|
643
|
+
|
|
644
|
+
>>> x = LogarithmicBins(1.0, 25.0, 3)
|
|
645
|
+
>>> x[1]
|
|
646
|
+
0
|
|
647
|
+
>>> x[5]
|
|
648
|
+
1
|
|
649
|
+
>>> x[25]
|
|
650
|
+
2
|
|
651
|
+
>>> import sys
|
|
652
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
653
|
+
<Param Type="lstring" Name="logbins:pylal_rate_bins:param">1,25,3</Param>
|
|
654
|
+
>>> LogarithmicBins.from_xml(x.to_xml()) == x
|
|
655
|
+
True
|
|
656
|
+
"""
|
|
657
|
+
def __init__(self, min, max, n):
|
|
658
|
+
super(LogarithmicBins, self).__init__(min, max, n)
|
|
659
|
+
self.logmin = math.log(min)
|
|
660
|
+
self.logmax = math.log(max)
|
|
661
|
+
self.delta = (self.logmax - self.logmin) / n
|
|
662
|
+
|
|
663
|
+
def __getitem__(self, x):
|
|
664
|
+
# slice cannot be sub-classed so no need to use
|
|
665
|
+
# isinstance()
|
|
666
|
+
if type(x) is slice:
|
|
667
|
+
return super(LogarithmicBins, self).__getitem__(x)
|
|
668
|
+
if self.min <= x < self.max:
|
|
669
|
+
return int(math.floor((math.log(x) - self.logmin) / self.delta))
|
|
670
|
+
if x == self.max:
|
|
671
|
+
# special "measure zero" corner case
|
|
672
|
+
return len(self) - 1
|
|
673
|
+
raise IndexError(x)
|
|
674
|
+
|
|
675
|
+
def lower(self):
|
|
676
|
+
return numpy.exp(numpy.linspace(self.logmin, self.logmax - self.delta, len(self)))
|
|
677
|
+
|
|
678
|
+
def centres(self):
|
|
679
|
+
return numpy.exp(numpy.linspace(self.logmin, self.logmax - self.delta, len(self)) + self.delta / 2.)
|
|
680
|
+
|
|
681
|
+
def upper(self):
|
|
682
|
+
return numpy.exp(numpy.linspace(self.logmin + self.delta, self.logmax, len(self)))
|
|
683
|
+
|
|
684
|
+
#
|
|
685
|
+
# XML I/O related methods and data
|
|
686
|
+
#
|
|
687
|
+
|
|
688
|
+
xml_bins_name = "logbins"
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
class LogarithmicPlusOverflowBins(LoHiCountBins):
|
|
692
|
+
"""
|
|
693
|
+
Logarithmically-spaced bins plus one bin at each end that goes to
|
|
694
|
+
zero and positive infinity respectively. There are n-2 bins each
|
|
695
|
+
of whose upper and lower bounds differ by the same factor. Bin 1
|
|
696
|
+
starts on the lower bound, and bin n-2 ends on the upper bound
|
|
697
|
+
inclusively. Bins 0 and n-1 are overflow bins extending from 0 to
|
|
698
|
+
the lower bound and from the upper bound to +infinity respectively.
|
|
699
|
+
Must have n >= 3.
|
|
700
|
+
|
|
701
|
+
Example:
|
|
702
|
+
|
|
703
|
+
>>> x = LogarithmicPlusOverflowBins(1.0, 25.0, 5)
|
|
704
|
+
>>> x[0]
|
|
705
|
+
0
|
|
706
|
+
>>> x[1]
|
|
707
|
+
1
|
|
708
|
+
>>> x[5]
|
|
709
|
+
2
|
|
710
|
+
>>> x[24.999]
|
|
711
|
+
3
|
|
712
|
+
>>> x[25]
|
|
713
|
+
4
|
|
714
|
+
>>> x[100]
|
|
715
|
+
4
|
|
716
|
+
>>> x.lower()
|
|
717
|
+
array([ 0. , 1. , 2.92401774, 8.54987973, 25. ])
|
|
718
|
+
>>> x.upper()
|
|
719
|
+
array([ 1. , 2.92401774, 8.54987973, 25. , inf])
|
|
720
|
+
>>> x.centres()
|
|
721
|
+
array([ 0. , 1.70997595, 5. , 14.62008869, inf])
|
|
722
|
+
>>> import sys
|
|
723
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
724
|
+
<Param Type="lstring" Name="logplusoverflowbins:pylal_rate_bins:param">1,25,5</Param>
|
|
725
|
+
>>> LogarithmicPlusOverflowBins.from_xml(x.to_xml()) == x
|
|
726
|
+
True
|
|
727
|
+
"""
|
|
728
|
+
def __init__(self, min, max, n):
|
|
729
|
+
if n < 3:
|
|
730
|
+
raise ValueError("n must be >= 3")
|
|
731
|
+
super(LogarithmicPlusOverflowBins, self).__init__(min, max, n)
|
|
732
|
+
self.logmin = math.log(min)
|
|
733
|
+
self.logmax = math.log(max)
|
|
734
|
+
self.delta = (self.logmax - self.logmin) / (n - 2)
|
|
735
|
+
|
|
736
|
+
def __getitem__(self, x):
|
|
737
|
+
# slice cannot be sub-classed so no need to use
|
|
738
|
+
# isinstance()
|
|
739
|
+
if type(x) is slice:
|
|
740
|
+
return super(LogarithmicPlusOverflowBins, self).__getitem__(x)
|
|
741
|
+
if self.min <= x < self.max:
|
|
742
|
+
return 1 + int(math.floor((math.log(x) - self.logmin) / self.delta))
|
|
743
|
+
if x >= self.max:
|
|
744
|
+
# infinity overflow bin
|
|
745
|
+
return len(self) - 1
|
|
746
|
+
if x < self.min:
|
|
747
|
+
# zero overflow bin
|
|
748
|
+
return 0
|
|
749
|
+
raise IndexError(x)
|
|
750
|
+
|
|
751
|
+
def lower(self):
|
|
752
|
+
return numpy.concatenate((numpy.array([0.]), numpy.exp(numpy.linspace(self.logmin, self.logmax, len(self) - 1))))
|
|
753
|
+
|
|
754
|
+
def centres(self):
|
|
755
|
+
return numpy.concatenate((numpy.array([0.]), numpy.exp(numpy.linspace(self.logmin, self.logmax - self.delta, len(self) - 2) + self.delta / 2.), numpy.array([PosInf])))
|
|
756
|
+
|
|
757
|
+
def upper(self):
|
|
758
|
+
return numpy.concatenate((numpy.exp(numpy.linspace(self.logmin, self.logmax, len(self) - 1)), numpy.array([PosInf])))
|
|
759
|
+
|
|
760
|
+
#
|
|
761
|
+
# XML I/O related methods and data
|
|
762
|
+
#
|
|
763
|
+
|
|
764
|
+
xml_bins_name = "logplusoverflowbins"
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
class ATanBins(LoHiCountBins):
|
|
768
|
+
"""
|
|
769
|
+
Bins spaced uniformly in tan^-1 x. Provides approximately linear
|
|
770
|
+
binning in the middle portion, with the bin density dropping
|
|
771
|
+
asymptotically to 0 as x goes to +/- \\infty. The min and max
|
|
772
|
+
parameters set the bounds of the region of approximately
|
|
773
|
+
uniformly-spaced bins. In a sense, these are where the roll-over
|
|
774
|
+
from uniformly-spaced bins to asymptotically diminishing bin
|
|
775
|
+
density occurs. There is a total of n bins.
|
|
776
|
+
|
|
777
|
+
Example:
|
|
778
|
+
|
|
779
|
+
>>> x = ATanBins(-1.0, +1.0, 11)
|
|
780
|
+
>>> x[float("-inf")]
|
|
781
|
+
0
|
|
782
|
+
>>> x[0]
|
|
783
|
+
5
|
|
784
|
+
>>> x[float("+inf")]
|
|
785
|
+
10
|
|
786
|
+
>>> x.centres()
|
|
787
|
+
array([-4.42778777, -1.39400285, -0.73469838, -0.40913068, -0.18692843,
|
|
788
|
+
0. , 0.18692843, 0.40913068, 0.73469838, 1.39400285,
|
|
789
|
+
4.42778777])
|
|
790
|
+
>>> import sys
|
|
791
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
792
|
+
<Param Type="lstring" Name="atanbins:pylal_rate_bins:param">-1,1,11</Param>
|
|
793
|
+
>>> ATanBins.from_xml(x.to_xml()) == x
|
|
794
|
+
True
|
|
795
|
+
"""
|
|
796
|
+
def __init__(self, min, max, n):
|
|
797
|
+
super(ATanBins, self).__init__(min, max, n)
|
|
798
|
+
self.mid = (min + max) / 2.0
|
|
799
|
+
self.scale = math.pi / float(max - min)
|
|
800
|
+
self.delta = 1.0 / n
|
|
801
|
+
|
|
802
|
+
def __getitem__(self, x):
|
|
803
|
+
# slice cannot be sub-classed so no need to use
|
|
804
|
+
# isinstance()
|
|
805
|
+
if type(x) is slice:
|
|
806
|
+
return super(ATanBins, self).__getitem__(x)
|
|
807
|
+
# map to the domain [0, 1]
|
|
808
|
+
x = math.atan(float(x - self.mid) * self.scale) / math.pi + 0.5
|
|
809
|
+
if x < 1.:
|
|
810
|
+
return int(math.floor(x / self.delta))
|
|
811
|
+
# x == 1, special "measure zero" corner case
|
|
812
|
+
return len(self) - 1
|
|
813
|
+
|
|
814
|
+
def lower(self):
|
|
815
|
+
x = numpy.tan(numpy.linspace(-math.pi / 2., +math.pi / 2., len(self), endpoint = False)) / self.scale + self.mid
|
|
816
|
+
x[0] = NegInf
|
|
817
|
+
return x
|
|
818
|
+
|
|
819
|
+
def centres(self):
|
|
820
|
+
offset = 0.5 * math.pi * self.delta
|
|
821
|
+
return numpy.tan(numpy.linspace(-math.pi / 2. + offset, +math.pi / 2. + offset, len(self), endpoint = False)) / self.scale + self.mid
|
|
822
|
+
|
|
823
|
+
def upper(self):
|
|
824
|
+
offset = math.pi * self.delta
|
|
825
|
+
x = numpy.tan(numpy.linspace(-math.pi / 2. + offset, +math.pi / 2. + offset, len(self), endpoint = False)) / self.scale + self.mid
|
|
826
|
+
x[-1] = PosInf
|
|
827
|
+
return x
|
|
828
|
+
|
|
829
|
+
#
|
|
830
|
+
# XML I/O related methods and data
|
|
831
|
+
#
|
|
832
|
+
|
|
833
|
+
xml_bins_name = "atanbins"
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
class ATanLogarithmicBins(LoHiCountBins, IrregularBins):
|
|
837
|
+
"""
|
|
838
|
+
Provides the same binning as the ATanBins class but in the
|
|
839
|
+
logarithm of the variable. The min and max parameters set the
|
|
840
|
+
bounds of the interval of approximately logarithmically-spaced
|
|
841
|
+
bins. In a sense, these are where the roll-over from
|
|
842
|
+
logarithmically-spaced bins to asymptotically diminishing bin
|
|
843
|
+
density occurs.
|
|
844
|
+
|
|
845
|
+
Example:
|
|
846
|
+
|
|
847
|
+
>>> x = ATanLogarithmicBins(+1.0, +1000.0, 11)
|
|
848
|
+
>>> x[0]
|
|
849
|
+
0
|
|
850
|
+
>>> x[30]
|
|
851
|
+
5
|
|
852
|
+
>>> x[float("+inf")]
|
|
853
|
+
10
|
|
854
|
+
>>> x.centres()
|
|
855
|
+
array([ 7.21636246e-06, 2.56445876e-01, 2.50007148e+00,
|
|
856
|
+
7.69668960e+00, 1.65808715e+01, 3.16227766e+01,
|
|
857
|
+
6.03104608e+01, 1.29925988e+02, 3.99988563e+02,
|
|
858
|
+
3.89945831e+03, 1.38573971e+08])
|
|
859
|
+
>>> import sys
|
|
860
|
+
>>> x.to_xml().write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
861
|
+
<Param Type="lstring" Name="atanlogbins:pylal_rate_bins:param">1,1000,11</Param>
|
|
862
|
+
>>> ATanLogarithmicBins.from_xml(x.to_xml()) == x
|
|
863
|
+
True
|
|
864
|
+
|
|
865
|
+
It is relatively easy to choose limits and a count of bins that
|
|
866
|
+
result in numerical overflows and underflows when computing bin
|
|
867
|
+
boundaries. When this happens, one or more bins at the ends of the
|
|
868
|
+
binning ends up with identical upper and lower boundaries (either
|
|
869
|
+
0, or +inf), and this class behaves as though those bins simply
|
|
870
|
+
don't exist. That is, the actual number of bins can be less than
|
|
871
|
+
the number requested. len() returns the actual number of bins ---
|
|
872
|
+
how large an array the binning corresponds to.
|
|
873
|
+
"""
|
|
874
|
+
def __init__(self, min, max, n):
|
|
875
|
+
if not isinstance(n, int):
|
|
876
|
+
raise TypeError(n)
|
|
877
|
+
if n < 1:
|
|
878
|
+
raise ValueError(n)
|
|
879
|
+
if max <= min:
|
|
880
|
+
raise ValueError((min, max))
|
|
881
|
+
self.mid = (math.log(min) + math.log(max)) / 2.0
|
|
882
|
+
self.scale = math.pi / (math.log(max) - math.log(min))
|
|
883
|
+
self.delta = 1.0 / n
|
|
884
|
+
boundaries = numpy.tan(-math.pi / 2 + math.pi * self.delta * numpy.arange(n)) / self.scale + self.mid
|
|
885
|
+
with numpy.errstate(over = "ignore"):
|
|
886
|
+
boundaries = numpy.exp(boundaries)
|
|
887
|
+
boundaries = numpy.hstack((boundaries, [PosInf, 0.]))
|
|
888
|
+
keepers = boundaries[:-1] != boundaries[1:]
|
|
889
|
+
IrregularBins.__init__(self, boundaries[:-1][keepers])
|
|
890
|
+
self.keepers = keepers[:-1]
|
|
891
|
+
self.min = min
|
|
892
|
+
self.max = max
|
|
893
|
+
self.n = n
|
|
894
|
+
|
|
895
|
+
__len__ = IrregularBins.__len__
|
|
896
|
+
|
|
897
|
+
def centres(self):
|
|
898
|
+
offset = 0.5 * math.pi * self.delta
|
|
899
|
+
centres = numpy.tan(numpy.linspace(-math.pi / 2. + offset, +math.pi / 2. + offset, self.n, endpoint = False)) / self.scale + self.mid
|
|
900
|
+
with numpy.errstate(over = "ignore"):
|
|
901
|
+
return numpy.exp(centres)[self.keepers]
|
|
902
|
+
|
|
903
|
+
#
|
|
904
|
+
# XML I/O related methods and data
|
|
905
|
+
#
|
|
906
|
+
|
|
907
|
+
xml_bins_name = "atanlogbins"
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
class Categories(Bins):
|
|
911
|
+
"""
|
|
912
|
+
Categories is a many-to-one mapping from a value to an integer
|
|
913
|
+
category index. A value belongs to a category if it is contained
|
|
914
|
+
in the category's defining collection. If a value is contained in
|
|
915
|
+
more than one category's defining collection, it belongs to the
|
|
916
|
+
category with the smallest index. IndexError is raised if a value
|
|
917
|
+
is not contained in any category's defining collection.
|
|
918
|
+
|
|
919
|
+
Example with discrete values:
|
|
920
|
+
|
|
921
|
+
>>> categories = Categories([
|
|
922
|
+
... set((frozenset(("H1", "L1")), frozenset(("H1", "V1")))),
|
|
923
|
+
... set((frozenset(("H1", "L1", "V1")),))
|
|
924
|
+
... ])
|
|
925
|
+
>>> categories[set(("H1", "L1"))]
|
|
926
|
+
0
|
|
927
|
+
>>> categories[set(("H1", "V1"))]
|
|
928
|
+
0
|
|
929
|
+
>>> categories[set(("H1", "L1", "V1"))]
|
|
930
|
+
1
|
|
931
|
+
|
|
932
|
+
Example with continuous values:
|
|
933
|
+
|
|
934
|
+
>>> from igwn_segments import *
|
|
935
|
+
>>> categories = Categories([
|
|
936
|
+
... segmentlist([segment(1, 3), segment(5, 7)]),
|
|
937
|
+
... segmentlist([segment(0, PosInfinity)])
|
|
938
|
+
... ])
|
|
939
|
+
>>> categories[2]
|
|
940
|
+
0
|
|
941
|
+
>>> categories[4]
|
|
942
|
+
1
|
|
943
|
+
>>> categories[-1]
|
|
944
|
+
Traceback (most recent call last):
|
|
945
|
+
...
|
|
946
|
+
IndexError: -1
|
|
947
|
+
|
|
948
|
+
This last example demonstrates the behaviour when the intersection
|
|
949
|
+
of the categories is not the empty set.
|
|
950
|
+
"""
|
|
951
|
+
def __init__(self, categories):
|
|
952
|
+
"""
|
|
953
|
+
categories is an iterable of containers (objects that
|
|
954
|
+
support the "in" operator) defining the categories.
|
|
955
|
+
Objects will be mapped to the integer index of the
|
|
956
|
+
container that contains them.
|
|
957
|
+
"""
|
|
958
|
+
# make immutable copy
|
|
959
|
+
self.containers = tuple(categories)
|
|
960
|
+
|
|
961
|
+
def __len__(self):
|
|
962
|
+
return len(self.containers)
|
|
963
|
+
|
|
964
|
+
def __getitem__(self, value):
|
|
965
|
+
"""
|
|
966
|
+
Return i if value is contained in i-th container. If value
|
|
967
|
+
is not contained in any of the containers, raise an
|
|
968
|
+
IndexError. This is O(n).
|
|
969
|
+
"""
|
|
970
|
+
for i, s in enumerate(self.containers):
|
|
971
|
+
if value in s:
|
|
972
|
+
return i
|
|
973
|
+
raise IndexError(value)
|
|
974
|
+
|
|
975
|
+
def __eq__(self, other):
|
|
976
|
+
return isinstance(other, type(self)) and self.containers == other.containers
|
|
977
|
+
|
|
978
|
+
def centres(self):
|
|
979
|
+
return self.containers
|
|
980
|
+
|
|
981
|
+
#
|
|
982
|
+
# XML I/O related methods and data
|
|
983
|
+
#
|
|
984
|
+
|
|
985
|
+
xml_bins_name = "categorybins"
|
|
986
|
+
|
|
987
|
+
def to_xml(self):
|
|
988
|
+
"""
|
|
989
|
+
Construct a LIGO Light Weight XML representation of the
|
|
990
|
+
Bins instance.
|
|
991
|
+
"""
|
|
992
|
+
return ligolw.Param.build(self.xml_bins_name_enc(self.xml_bins_name), "yaml", self.containers)
|
|
993
|
+
|
|
994
|
+
@classmethod
|
|
995
|
+
def from_xml(cls, xml):
|
|
996
|
+
"""
|
|
997
|
+
From the XML Param element at xml, return the Bins object
|
|
998
|
+
it describes.
|
|
999
|
+
"""
|
|
1000
|
+
if not cls.xml_bins_check(xml, cls.xml_bins_name):
|
|
1001
|
+
raise ValueError("not a %s" % repr(cls))
|
|
1002
|
+
return cls(xml.pcdata)
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
class HashableBins(Categories):
|
|
1006
|
+
"""
|
|
1007
|
+
Maps hashable objects (things that can be used as dictionary keys)
|
|
1008
|
+
to integers.
|
|
1009
|
+
|
|
1010
|
+
Example:
|
|
1011
|
+
>>> x = HashableBins([
|
|
1012
|
+
... frozenset(("H1", "L1")),
|
|
1013
|
+
... frozenset(("H1", "V1")),
|
|
1014
|
+
... frozenset(("L1", "V1")),
|
|
1015
|
+
... frozenset(("H1", "L1", "V1"))
|
|
1016
|
+
... ])
|
|
1017
|
+
>>> x[frozenset(("H1", "L1"))]
|
|
1018
|
+
0
|
|
1019
|
+
>>> x[set(("H1", "L1"))] # equal, but not hashable
|
|
1020
|
+
Traceback (most recent call last):
|
|
1021
|
+
...
|
|
1022
|
+
IndexError: set(['H1', 'L1'])
|
|
1023
|
+
>>> x.centres()[2]
|
|
1024
|
+
frozenset(['V1', 'L1'])
|
|
1025
|
+
"""
|
|
1026
|
+
def __init__(self, hashables):
|
|
1027
|
+
super(HashableBins, self).__init__(hashables)
|
|
1028
|
+
self.mapping = dict(zip(self.containers, range(len(self.containers))))
|
|
1029
|
+
|
|
1030
|
+
def __getitem__(self, value):
|
|
1031
|
+
try:
|
|
1032
|
+
return self.mapping[value]
|
|
1033
|
+
except (KeyError, TypeError):
|
|
1034
|
+
raise IndexError(value)
|
|
1035
|
+
|
|
1036
|
+
xml_bins_name = "hashablebins"
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
class NDBins(tuple):
|
|
1040
|
+
"""
|
|
1041
|
+
Multi-dimensional co-ordinate binning. An instance of this object
|
|
1042
|
+
is used to convert a tuple of co-ordinates into a tuple of bin
|
|
1043
|
+
indices. This can be used to allow the contents of an array object
|
|
1044
|
+
to be accessed with real-valued coordinates.
|
|
1045
|
+
|
|
1046
|
+
NDBins is a subclass of the tuple builtin, and is initialized with
|
|
1047
|
+
an iterable of instances of subclasses of Bins. Each Bins subclass
|
|
1048
|
+
instance describes the binning to apply in the corresponding
|
|
1049
|
+
co-ordinate direction, and the number of them sets the dimensions
|
|
1050
|
+
of the binning.
|
|
1051
|
+
|
|
1052
|
+
Example:
|
|
1053
|
+
|
|
1054
|
+
>>> x = NDBins((LinearBins(1, 25, 3), LogarithmicBins(1, 25, 3)))
|
|
1055
|
+
>>> x[1, 1]
|
|
1056
|
+
(0, 0)
|
|
1057
|
+
>>> x[1.5, 1]
|
|
1058
|
+
(0, 0)
|
|
1059
|
+
>>> x[10, 1]
|
|
1060
|
+
(1, 0)
|
|
1061
|
+
>>> x[1, 5]
|
|
1062
|
+
(0, 1)
|
|
1063
|
+
>>> x[1, 1:5]
|
|
1064
|
+
(0, slice(0, 2, None))
|
|
1065
|
+
>>> x.centres()
|
|
1066
|
+
(array([ 5., 13., 21.]), array([ 1.70997595, 5. , 14.62008869]))
|
|
1067
|
+
>>> y = NDBins((LinearBins(1, 25, 3), LogarithmicBins(1, 25, 3)))
|
|
1068
|
+
>>> x == y
|
|
1069
|
+
True
|
|
1070
|
+
>>> y = NDBins((LinearBins(1, 25, 4), LogarithmicBins(1, 25, 3)))
|
|
1071
|
+
>>> x == y
|
|
1072
|
+
False
|
|
1073
|
+
>>> y = NDBins((LogarithmicBins(1, 25, 3), LogarithmicBins(1, 25, 3)))
|
|
1074
|
+
>>> x == y
|
|
1075
|
+
False
|
|
1076
|
+
>>> from igwn_ligolw.ligolw import LIGO_LW
|
|
1077
|
+
>>> import sys
|
|
1078
|
+
>>> elem = x.to_xml(LIGO_LW())
|
|
1079
|
+
>>> elem.write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
1080
|
+
<LIGO_LW>
|
|
1081
|
+
<Param Type="lstring" Name="linbins:pylal_rate_bins:param">1,25,3</Param>
|
|
1082
|
+
<Param Type="lstring" Name="logbins:pylal_rate_bins:param">1,25,3</Param>
|
|
1083
|
+
</LIGO_LW>
|
|
1084
|
+
>>> NDBins.from_xml(elem) == x
|
|
1085
|
+
True
|
|
1086
|
+
|
|
1087
|
+
Note that the co-ordinates to be converted must be a tuple, even if
|
|
1088
|
+
it is only a 1-dimensional co-ordinate.
|
|
1089
|
+
"""
|
|
1090
|
+
def __init__(self, binnings):
|
|
1091
|
+
self._getitems = tuple(binning.__getitem__ for binning in binnings)
|
|
1092
|
+
# instances cannot define a .__call__() attribute to make
|
|
1093
|
+
# themselves callable, Python always looks up .__call__()
|
|
1094
|
+
# on the class. so we define .__realcall__() here and then
|
|
1095
|
+
# have .__call__() chain to it. Python3 does not transfer
|
|
1096
|
+
# the current variable scope into exec() so we have to do
|
|
1097
|
+
# it for it ... whatever.
|
|
1098
|
+
define__realcall__ = """def __realcall__(self, %s):
|
|
1099
|
+
_getitems = self._getitems
|
|
1100
|
+
return %s""" % (", ".join("x%d" % i for i in range(len(binnings))), ", ".join("_getitems[%d](x%d)" % (i, i) for i in range(len(binnings))))
|
|
1101
|
+
l = {}
|
|
1102
|
+
exec(define__realcall__, globals(), l)
|
|
1103
|
+
__realcall__ = l["__realcall__"]
|
|
1104
|
+
self.__realcall__ = __realcall__.__get__(self)
|
|
1105
|
+
|
|
1106
|
+
def __getitem__(self, coords):
|
|
1107
|
+
"""
|
|
1108
|
+
When coords is a tuple this is a synonym for self(*coords),
|
|
1109
|
+
otherwise coords is interpreted as an index into ourselves
|
|
1110
|
+
and the Bins object at that index is returned
|
|
1111
|
+
|
|
1112
|
+
Example:
|
|
1113
|
+
|
|
1114
|
+
>>> x = NDBins((LinearBins(1, 25, 3), LogarithmicBins(1, 25, 3)))
|
|
1115
|
+
>>> x[1, 1]
|
|
1116
|
+
(0, 0)
|
|
1117
|
+
>>> # slices can be given syntactically
|
|
1118
|
+
>>> x[10:12, 1]
|
|
1119
|
+
(slice(1, 2, None), 0)
|
|
1120
|
+
>>> type(x[1])
|
|
1121
|
+
<class 'lal.rate.LogarithmicBins'>
|
|
1122
|
+
|
|
1123
|
+
Note that if the argument is to be interpreted as a
|
|
1124
|
+
co-ordinate it must be a tuple even if it is only a
|
|
1125
|
+
1-dimensional co-ordinate.
|
|
1126
|
+
|
|
1127
|
+
Example:
|
|
1128
|
+
|
|
1129
|
+
>>> x = NDBins((LinearBins(1, 25, 3),))
|
|
1130
|
+
>>> x[1,]
|
|
1131
|
+
(0,)
|
|
1132
|
+
"""
|
|
1133
|
+
return self(*coords) if isinstance(coords, tuple) else tuple.__getitem__(self, coords)
|
|
1134
|
+
|
|
1135
|
+
def __call__(self, *coords):
|
|
1136
|
+
"""
|
|
1137
|
+
Convert an N-dimensional co-ordinate to an N-tuple of bin
|
|
1138
|
+
indices using the Bins instances in this object. Calling
|
|
1139
|
+
the NDBins instance instead of using indexing (the "[]"
|
|
1140
|
+
operator) provides a more direct, faster, interface to the
|
|
1141
|
+
Bins instances contained herein, but slices cannot be given
|
|
1142
|
+
syntactically in the argument list
|
|
1143
|
+
|
|
1144
|
+
Example:
|
|
1145
|
+
|
|
1146
|
+
>>> x = NDBins((LinearBins(1, 25, 3), LogarithmicBins(1, 25, 3)))
|
|
1147
|
+
>>> x[1, 1] # access using index operator
|
|
1148
|
+
(0, 0)
|
|
1149
|
+
>>> x(1, 1) # access by calling
|
|
1150
|
+
(0, 0)
|
|
1151
|
+
>>> x = NDBins((LinearBins(1, 25, 3),))
|
|
1152
|
+
>>> x(1) # explicit tuples not required for 1D
|
|
1153
|
+
(0,)
|
|
1154
|
+
>>> x = NDBins((LinearBins(1, 25, 1000),))
|
|
1155
|
+
>>> x(slice(10, 12)) # slices (constructed manually)
|
|
1156
|
+
(slice(375, 459, None),)
|
|
1157
|
+
>>> x = NDBins((Categories([set(("Cow", "Chicken", "Goat")), set(("Tractor", "Plough")), set(("Barn", "House"))]),))
|
|
1158
|
+
>>> x("Cow")
|
|
1159
|
+
(0,)
|
|
1160
|
+
|
|
1161
|
+
Each co-ordinate can be anything the corresponding Bins
|
|
1162
|
+
instance will accept.
|
|
1163
|
+
"""
|
|
1164
|
+
return self.__realcall__(*coords)
|
|
1165
|
+
|
|
1166
|
+
@property
|
|
1167
|
+
def shape(self):
|
|
1168
|
+
"""
|
|
1169
|
+
Tuple of the number of bins along each dimension.
|
|
1170
|
+
|
|
1171
|
+
Example:
|
|
1172
|
+
|
|
1173
|
+
>>> NDBins((LinearBins(0, 6, 3), LinearBins(0, 10, 5))).shape
|
|
1174
|
+
(3, 5)
|
|
1175
|
+
"""
|
|
1176
|
+
return tuple(len(b) for b in self)
|
|
1177
|
+
|
|
1178
|
+
def lower(self):
|
|
1179
|
+
"""
|
|
1180
|
+
Return a tuple of arrays, where each array contains the
|
|
1181
|
+
locations of the lower boundaries of the bins in the
|
|
1182
|
+
corresponding dimension.
|
|
1183
|
+
|
|
1184
|
+
Example:
|
|
1185
|
+
|
|
1186
|
+
>>> NDBins((LinearBins(0, 6, 3), LinearBins(0, 10, 5))).lower()
|
|
1187
|
+
(array([ 0., 2., 4.]), array([ 0., 2., 4., 6., 8.]))
|
|
1188
|
+
"""
|
|
1189
|
+
return tuple(b.lower() for b in self)
|
|
1190
|
+
|
|
1191
|
+
def centres(self):
|
|
1192
|
+
"""
|
|
1193
|
+
Return a tuple of arrays, where each array contains the
|
|
1194
|
+
locations of the bin centres for the corresponding
|
|
1195
|
+
dimension.
|
|
1196
|
+
|
|
1197
|
+
Example:
|
|
1198
|
+
|
|
1199
|
+
>>> NDBins((LinearBins(0, 6, 3), LinearBins(0, 10, 5))).centres()
|
|
1200
|
+
(array([ 1., 3., 5.]), array([ 1., 3., 5., 7., 9.]))
|
|
1201
|
+
"""
|
|
1202
|
+
return tuple(b.centres() for b in self)
|
|
1203
|
+
|
|
1204
|
+
def upper(self):
|
|
1205
|
+
"""
|
|
1206
|
+
Return a tuple of arrays, where each array contains the
|
|
1207
|
+
locations of the upper boundaries of the bins in the
|
|
1208
|
+
corresponding dimension.
|
|
1209
|
+
|
|
1210
|
+
Example:
|
|
1211
|
+
|
|
1212
|
+
>>> NDBins((LinearBins(0, 6, 3), LinearBins(0, 10, 5))).upper()
|
|
1213
|
+
(array([ 2., 4., 6.]), array([ 2., 4., 6., 8., 10.]))
|
|
1214
|
+
"""
|
|
1215
|
+
return tuple(b.upper() for b in self)
|
|
1216
|
+
|
|
1217
|
+
def volumes(self):
|
|
1218
|
+
"""
|
|
1219
|
+
Return an n-dimensional array of the bin volumes.
|
|
1220
|
+
|
|
1221
|
+
Example:
|
|
1222
|
+
|
|
1223
|
+
>>> # 3x5 grid of bins, each 2 units by 2 units
|
|
1224
|
+
>>> x = NDBins((LinearBins(0, 6, 3), LinearBins(0, 10, 5)))
|
|
1225
|
+
>>> x.volumes()
|
|
1226
|
+
array([[ 4., 4., 4., 4., 4.],
|
|
1227
|
+
[ 4., 4., 4., 4., 4.],
|
|
1228
|
+
[ 4., 4., 4., 4., 4.]])
|
|
1229
|
+
"""
|
|
1230
|
+
volumes = tuple(u - l for u, l in zip(self.upper(), self.lower()))
|
|
1231
|
+
if len(volumes) == 1:
|
|
1232
|
+
# 1D short-cut
|
|
1233
|
+
return volumes[0]
|
|
1234
|
+
try:
|
|
1235
|
+
return numpy.einsum(",".join("abcdefghijklmnopqrstuvwxyz"[:len(volumes)]), *volumes)
|
|
1236
|
+
except AttributeError:
|
|
1237
|
+
# numpy < 1.6
|
|
1238
|
+
result = reduce(numpy.outer, volumes)
|
|
1239
|
+
result.shape = tuple(len(v) for v in volumes)
|
|
1240
|
+
return result
|
|
1241
|
+
|
|
1242
|
+
def randcoord(self, ns = None, domain = None):
|
|
1243
|
+
"""
|
|
1244
|
+
Generator yielding a sequence of (x0, x1, ...), ln(P(x0,
|
|
1245
|
+
x1, ...)) tuples where (x0, x1, ...) is a randomly-chosen
|
|
1246
|
+
co-ordinate in the N-dimensional binning and P(x0, x1, ...)
|
|
1247
|
+
is the PDF from which the co-ordinate tuple has been drawn
|
|
1248
|
+
evaluated at those co-ordinates. If ns is not None it must
|
|
1249
|
+
be a sequence of floats whose length matches the dimension
|
|
1250
|
+
of the binning. The floats will set the exponents, in
|
|
1251
|
+
order, of the CDFs for the generators used for each
|
|
1252
|
+
co-ordinate. If domain is not None it must be a sequence
|
|
1253
|
+
of slice objects whose length matches the dimension of the
|
|
1254
|
+
binning. The slice objects will be passed, in order, as
|
|
1255
|
+
the domain keyword argument to the .randcoord() method
|
|
1256
|
+
corresponding to each dimension. For more information on
|
|
1257
|
+
how each of the co-ordinates is drawn, see
|
|
1258
|
+
Bins.randcoord().
|
|
1259
|
+
|
|
1260
|
+
Example:
|
|
1261
|
+
|
|
1262
|
+
>>> binning = NDBins((LinearBins(0, 10, 5), LinearBins(0, 10, 5)))
|
|
1263
|
+
>>> coord = binning.randcoord()
|
|
1264
|
+
>>> next(coord) # doctest: +ELLIPSIS
|
|
1265
|
+
((..., ...), -4.6051701859880909)
|
|
1266
|
+
"""
|
|
1267
|
+
if ns is None:
|
|
1268
|
+
ns = (1.,) * len(self)
|
|
1269
|
+
if domain is None:
|
|
1270
|
+
domain = (slice(None, None),) * len(self)
|
|
1271
|
+
coordgens = tuple(iter(binning.randcoord(n, domain = d)) for binning, n, d in zip(self, ns, domain))
|
|
1272
|
+
while 1:
|
|
1273
|
+
seq = sum((next(coordgen) for coordgen in coordgens), ())
|
|
1274
|
+
yield seq[0::2], sum(seq[1::2])
|
|
1275
|
+
|
|
1276
|
+
#
|
|
1277
|
+
# XML I/O methods and data
|
|
1278
|
+
#
|
|
1279
|
+
|
|
1280
|
+
xml_bins_name_mapping = dict((cls.xml_bins_name, cls) for cls in (LinearBins, LinearPlusOverflowBins, LogarithmicBins, LogarithmicPlusOverflowBins, ATanBins, ATanLogarithmicBins, Categories, HashableBins))
|
|
1281
|
+
xml_bins_name_mapping.update(list(zip(xml_bins_name_mapping.values(), xml_bins_name_mapping.keys())))
|
|
1282
|
+
|
|
1283
|
+
def to_xml(self, elem):
|
|
1284
|
+
"""
|
|
1285
|
+
Construct a LIGO Light Weight XML representation of the
|
|
1286
|
+
NDBins instance. The representation is an in-order list of
|
|
1287
|
+
Param elements, typically inserted inside a LIGO_LW
|
|
1288
|
+
element. The elem argument provides the XML element to
|
|
1289
|
+
which the Param elements should be appended as children.
|
|
1290
|
+
|
|
1291
|
+
NOTE: The decoding process will require a specific parent
|
|
1292
|
+
element to be provided, and all Param elements that are
|
|
1293
|
+
immediate children of that element and contain the correct
|
|
1294
|
+
suffix will be used to reconstruct the NDBins. At this
|
|
1295
|
+
time the suffix is undocumented, so to guarantee
|
|
1296
|
+
compatibility the Param elements should not be inserted
|
|
1297
|
+
into an element that might contain other, unrelated, Params
|
|
1298
|
+
among its immediate children.
|
|
1299
|
+
"""
|
|
1300
|
+
for binning in self:
|
|
1301
|
+
elem.appendChild(binning.to_xml())
|
|
1302
|
+
return elem
|
|
1303
|
+
|
|
1304
|
+
@classmethod
|
|
1305
|
+
def from_xml(cls, xml):
|
|
1306
|
+
"""
|
|
1307
|
+
From the XML document tree rooted at xml construct an
|
|
1308
|
+
return an NDBins object described by the Param elements
|
|
1309
|
+
therein. Note, the XML element must be the immediate
|
|
1310
|
+
parent of the Param elements describing the NDBins.
|
|
1311
|
+
"""
|
|
1312
|
+
params = []
|
|
1313
|
+
for elem in xml.childNodes:
|
|
1314
|
+
if elem.tagName != ligolw.Param.tagName:
|
|
1315
|
+
continue
|
|
1316
|
+
try:
|
|
1317
|
+
Bins.xml_bins_name_dec(elem.Name)
|
|
1318
|
+
except ValueError:
|
|
1319
|
+
continue
|
|
1320
|
+
params.append(elem)
|
|
1321
|
+
if not params:
|
|
1322
|
+
raise ValueError("no Param elements found at '%s'" % repr(xml))
|
|
1323
|
+
return cls([cls.xml_bins_name_mapping[Bins.xml_bins_name_dec(elem.Name)].from_xml(elem) for elem in params])
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
#
|
|
1327
|
+
# =============================================================================
|
|
1328
|
+
#
|
|
1329
|
+
# Segments and Bins
|
|
1330
|
+
#
|
|
1331
|
+
# =============================================================================
|
|
1332
|
+
#
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
def bins_spanned(bins, seglist):
|
|
1336
|
+
"""
|
|
1337
|
+
Input is a Bins subclass instance and a igwn_segments.segmentlist
|
|
1338
|
+
instance. The output is an array object the length of the binning,
|
|
1339
|
+
which each element in the array set to the interval in the
|
|
1340
|
+
corresponding bin spanned by the segment list.
|
|
1341
|
+
|
|
1342
|
+
Example:
|
|
1343
|
+
|
|
1344
|
+
>>> from igwn_segments import *
|
|
1345
|
+
>>> s = segmentlist([segment(1.5, 10.333), segment(15.8, 24)])
|
|
1346
|
+
>>> b = LinearBins(0, 30, 100)
|
|
1347
|
+
>>> bins_spanned(b, s)
|
|
1348
|
+
array([ 0. , 0. , 0. , 0. , 0. , 0.3 , 0.3 , 0.3 ,
|
|
1349
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1350
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1351
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1352
|
+
0.3 , 0.3 , 0.133, 0. , 0. , 0. , 0. , 0. ,
|
|
1353
|
+
0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ,
|
|
1354
|
+
0. , 0. , 0. , 0. , 0.1 , 0.3 , 0.3 , 0.3 ,
|
|
1355
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1356
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1357
|
+
0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 , 0.3 ,
|
|
1358
|
+
0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ,
|
|
1359
|
+
0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ,
|
|
1360
|
+
0. , 0. , 0. , 0. ])
|
|
1361
|
+
"""
|
|
1362
|
+
lower = bins.lower()
|
|
1363
|
+
upper = bins.upper()
|
|
1364
|
+
# performance improvement: pre-clip segments to the domain of the
|
|
1365
|
+
# binning
|
|
1366
|
+
seglist = seglist & segments.segmentlist([segments.segment(lower[0], upper[-1])])
|
|
1367
|
+
return numpy.fromiter((abs(seglist & segments.segmentlist([seg])) for seg in zip(lower, upper)), dtype = lower.dtype, count = len(bins))
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
#
|
|
1371
|
+
# =============================================================================
|
|
1372
|
+
#
|
|
1373
|
+
# Binned Array
|
|
1374
|
+
#
|
|
1375
|
+
# =============================================================================
|
|
1376
|
+
#
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
class BinnedArray(object):
|
|
1380
|
+
"""
|
|
1381
|
+
A convenience wrapper, using the NDBins class to provide access to
|
|
1382
|
+
the elements of an array object. Technical reasons preclude
|
|
1383
|
+
providing a subclass of the array object, so the array data is made
|
|
1384
|
+
available as the "array" attribute of this class.
|
|
1385
|
+
|
|
1386
|
+
Examples:
|
|
1387
|
+
|
|
1388
|
+
Note that even for 1 dimensional arrays the index must be a tuple.
|
|
1389
|
+
|
|
1390
|
+
>>> x = BinnedArray(NDBins((LinearBins(0, 10, 5),)))
|
|
1391
|
+
>>> x.at_centres()
|
|
1392
|
+
array([ 0., 0., 0., 0., 0.])
|
|
1393
|
+
>>> x[0,] += 1
|
|
1394
|
+
>>> x[0.5,] += 1
|
|
1395
|
+
>>> x.at_centres()
|
|
1396
|
+
array([ 2., 0., 0., 0., 0.])
|
|
1397
|
+
>>> x.argmax()
|
|
1398
|
+
(1.0,)
|
|
1399
|
+
|
|
1400
|
+
Note the relationship between the binning limits, the bin centres,
|
|
1401
|
+
and the co-ordinates of the BinnedArray
|
|
1402
|
+
|
|
1403
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 1.5, 2), LinearBins(-0.5, 1.5, 2))))
|
|
1404
|
+
>>> x.bins.centres()
|
|
1405
|
+
(array([ 0., 1.]), array([ 0., 1.]))
|
|
1406
|
+
>>> x[0, 0] = 0
|
|
1407
|
+
>>> x[0, 1] = 1
|
|
1408
|
+
>>> x[1, 0] = 2
|
|
1409
|
+
>>> x[1, 1] = 4
|
|
1410
|
+
>>> x.at_centres()
|
|
1411
|
+
array([[ 0., 1.],
|
|
1412
|
+
[ 2., 4.]])
|
|
1413
|
+
>>> x[0, 0]
|
|
1414
|
+
0.0
|
|
1415
|
+
>>> x[0, 1]
|
|
1416
|
+
1.0
|
|
1417
|
+
>>> x[1, 0]
|
|
1418
|
+
2.0
|
|
1419
|
+
>>> x[1, 1]
|
|
1420
|
+
4.0
|
|
1421
|
+
>>> x.argmin()
|
|
1422
|
+
(0.0, 0.0)
|
|
1423
|
+
>>> x.argmax()
|
|
1424
|
+
(1.0, 1.0)
|
|
1425
|
+
|
|
1426
|
+
A BinnedArray can be initialized from an existing array if the
|
|
1427
|
+
array's shape is the same as the binning's.
|
|
1428
|
+
|
|
1429
|
+
>>> import numpy
|
|
1430
|
+
>>> x = BinnedArray(NDBins((LinearBins(0, 10, 5),)), array = numpy.zeros((5,)))
|
|
1431
|
+
>>> x = BinnedArray(NDBins((LinearBins(0, 10, 5),)), array = numpy.zeros((5,1)))
|
|
1432
|
+
Traceback (most recent call last):
|
|
1433
|
+
...
|
|
1434
|
+
ValueError: bins (shape = (5,)) and array (shape = (5, 1)), if supplied, must have the same shape
|
|
1435
|
+
|
|
1436
|
+
A BinnedArray can be serialized to LIGO Light Weight XML.
|
|
1437
|
+
|
|
1438
|
+
>>> import sys
|
|
1439
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 1.5, 2), LinearBins(-0.5, 1.5, 2))))
|
|
1440
|
+
>>> elem = x.to_xml("test")
|
|
1441
|
+
>>> elem.write(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
|
|
1442
|
+
<LIGO_LW Name="test:pylal_rate_binnedarray">
|
|
1443
|
+
<Param Type="lstring" Name="linbins:pylal_rate_bins:param">-0.5,1.5,2</Param>
|
|
1444
|
+
<Param Type="lstring" Name="linbins:pylal_rate_bins:param">-0.5,1.5,2</Param>
|
|
1445
|
+
<Array Type="real_8" Name="array:array">
|
|
1446
|
+
<Dim>2</Dim>
|
|
1447
|
+
<Dim>2</Dim>
|
|
1448
|
+
<Stream Delimiter=" " Type="Local">
|
|
1449
|
+
0 0
|
|
1450
|
+
0 0
|
|
1451
|
+
</Stream>
|
|
1452
|
+
</Array>
|
|
1453
|
+
</LIGO_LW>
|
|
1454
|
+
>>> y = BinnedArray.from_xml(elem, "test")
|
|
1455
|
+
>>> y.bins == x.bins
|
|
1456
|
+
True
|
|
1457
|
+
>>> (y.array == x.array).all()
|
|
1458
|
+
True
|
|
1459
|
+
"""
|
|
1460
|
+
def __init__(self, bins, array = None, dtype = "double"):
|
|
1461
|
+
self.bins = bins
|
|
1462
|
+
if array is None:
|
|
1463
|
+
self.array = numpy.zeros(bins.shape, dtype = dtype)
|
|
1464
|
+
elif array.shape != bins.shape:
|
|
1465
|
+
raise ValueError("bins (shape = %s) and array (shape = %s), if supplied, must have the same shape" % (str(bins.shape), str(array.shape)))
|
|
1466
|
+
else:
|
|
1467
|
+
self.array = array
|
|
1468
|
+
|
|
1469
|
+
def __getitem__(self, coords):
|
|
1470
|
+
return self.array[self.bins(*coords)]
|
|
1471
|
+
|
|
1472
|
+
def __setitem__(self, coords, val):
|
|
1473
|
+
self.array[self.bins(*coords)] = val
|
|
1474
|
+
|
|
1475
|
+
def __len__(self):
|
|
1476
|
+
return len(self.array)
|
|
1477
|
+
|
|
1478
|
+
def __iadd__(self, other):
|
|
1479
|
+
"""
|
|
1480
|
+
Add the contents of another BinnedArray object to this one.
|
|
1481
|
+
Both must have identical binnings.
|
|
1482
|
+
|
|
1483
|
+
Example:
|
|
1484
|
+
|
|
1485
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 1.5, 2), LinearBins(-0.5, 1.5, 2))))
|
|
1486
|
+
>>> x[0, 0] = 0
|
|
1487
|
+
>>> x[0, 1] = 1
|
|
1488
|
+
>>> x[1, 0] = 2
|
|
1489
|
+
>>> x[1, 1] = 4
|
|
1490
|
+
>>> x.at_centres()
|
|
1491
|
+
array([[ 0., 1.],
|
|
1492
|
+
[ 2., 4.]])
|
|
1493
|
+
>>> x += x
|
|
1494
|
+
>>> x.at_centres()
|
|
1495
|
+
array([[ 0., 2.],
|
|
1496
|
+
[ 4., 8.]])
|
|
1497
|
+
"""
|
|
1498
|
+
if self.bins != other.bins:
|
|
1499
|
+
raise TypeError("incompatible binning: %s" % repr(other))
|
|
1500
|
+
self.array += other.array
|
|
1501
|
+
return self
|
|
1502
|
+
|
|
1503
|
+
def __add__(self, other):
|
|
1504
|
+
"""
|
|
1505
|
+
Add two BinnedArray objects together.
|
|
1506
|
+
|
|
1507
|
+
Example:
|
|
1508
|
+
|
|
1509
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 1.5, 2), LinearBins(-0.5, 1.5, 2))))
|
|
1510
|
+
>>> x[0, 0] = 0
|
|
1511
|
+
>>> x[0, 1] = 1
|
|
1512
|
+
>>> x[1, 0] = 2
|
|
1513
|
+
>>> x[1, 1] = 4
|
|
1514
|
+
>>> x.at_centres()
|
|
1515
|
+
array([[ 0., 1.],
|
|
1516
|
+
[ 2., 4.]])
|
|
1517
|
+
>>> (x + x).at_centres()
|
|
1518
|
+
array([[ 0., 2.],
|
|
1519
|
+
[ 4., 8.]])
|
|
1520
|
+
"""
|
|
1521
|
+
self = self.copy()
|
|
1522
|
+
self += other
|
|
1523
|
+
return self
|
|
1524
|
+
|
|
1525
|
+
def copy(self):
|
|
1526
|
+
"""
|
|
1527
|
+
Return a copy of the BinnedArray. The .bins attribute is
|
|
1528
|
+
shared with the original.
|
|
1529
|
+
"""
|
|
1530
|
+
return type(self)(self.bins, self.array.copy())
|
|
1531
|
+
|
|
1532
|
+
def centres(self):
|
|
1533
|
+
"""
|
|
1534
|
+
Return a tuple of arrays containing the bin centres for
|
|
1535
|
+
each dimension.
|
|
1536
|
+
"""
|
|
1537
|
+
return self.bins.centres()
|
|
1538
|
+
|
|
1539
|
+
def at_centres(self):
|
|
1540
|
+
"""
|
|
1541
|
+
Return an array of the BinnedArray's value evaluated at the
|
|
1542
|
+
bin centres. In many cases this is simply a reference to
|
|
1543
|
+
the internal array object, but for subclasses that override
|
|
1544
|
+
item retrieval and assignment some additional work might be
|
|
1545
|
+
required to obtain this array. In those cases, this method
|
|
1546
|
+
is a convenience wrapper to avoid coding the evaluation
|
|
1547
|
+
logic in the calling code.
|
|
1548
|
+
|
|
1549
|
+
Because subclasses expect to be able to override this, in
|
|
1550
|
+
almost all cases calling code that wishes to access the
|
|
1551
|
+
values stored in the internal array directly should
|
|
1552
|
+
probably use this method to do so.
|
|
1553
|
+
|
|
1554
|
+
NOTE:
|
|
1555
|
+
|
|
1556
|
+
- The return value might be a newly-constructed object or a
|
|
1557
|
+
reference to an internal object.
|
|
1558
|
+
"""
|
|
1559
|
+
return self.array
|
|
1560
|
+
|
|
1561
|
+
def argmin(self):
|
|
1562
|
+
"""
|
|
1563
|
+
Return the co-ordinates of the bin centre containing the
|
|
1564
|
+
minimum value. Same as numpy.argmin(), converting the
|
|
1565
|
+
indexes to bin co-ordinates.
|
|
1566
|
+
"""
|
|
1567
|
+
array = self.at_centres()
|
|
1568
|
+
return tuple(centres[index] for centres, index in zip(self.centres(), numpy.unravel_index(array.argmin(), array.shape)))
|
|
1569
|
+
|
|
1570
|
+
def argmax(self):
|
|
1571
|
+
"""
|
|
1572
|
+
Return the co-ordinates of the bin centre containing the
|
|
1573
|
+
maximum value. Same as numpy.argmax(), converting the
|
|
1574
|
+
indexes to bin co-ordinates.
|
|
1575
|
+
"""
|
|
1576
|
+
array = self.at_centres()
|
|
1577
|
+
return tuple(centres[index] for centres, index in zip(self.centres(), numpy.unravel_index(array.argmax(), array.shape)))
|
|
1578
|
+
|
|
1579
|
+
def to_xml(self, name):
|
|
1580
|
+
"""
|
|
1581
|
+
Retrun an XML document tree describing a rate.BinnedArray
|
|
1582
|
+
object.
|
|
1583
|
+
"""
|
|
1584
|
+
elem = ligolw.LIGO_LW()
|
|
1585
|
+
elem.Name = "%s:pylal_rate_binnedarray" % name
|
|
1586
|
+
self.bins.to_xml(elem)
|
|
1587
|
+
elem.appendChild(ligolw.Array.build("array", self.array))
|
|
1588
|
+
return elem
|
|
1589
|
+
|
|
1590
|
+
@classmethod
|
|
1591
|
+
def get_xml_root(cls, xml, name):
|
|
1592
|
+
name = "%s:pylal_rate_binnedarray" % name
|
|
1593
|
+
elem = [elem for elem in xml.getElementsByTagName(ligolw.LIGO_LW.tagName) if elem.hasAttribute("Name") and elem.Name == name]
|
|
1594
|
+
try:
|
|
1595
|
+
elem, = elem
|
|
1596
|
+
except ValueError:
|
|
1597
|
+
raise ValueError("XML tree at '%s' must contain exactly one '%s' LIGO_LW element" % (repr(xml), name))
|
|
1598
|
+
return elem
|
|
1599
|
+
|
|
1600
|
+
@classmethod
|
|
1601
|
+
def from_xml(cls, xml, name):
|
|
1602
|
+
"""
|
|
1603
|
+
Search for the description of a rate.BinnedArray object
|
|
1604
|
+
named "name" in the XML document tree rooted at xml, and
|
|
1605
|
+
construct and return a new rate.BinnedArray object from the
|
|
1606
|
+
data contained therein.
|
|
1607
|
+
|
|
1608
|
+
NOTE: the .array attribute is a reference to the .array
|
|
1609
|
+
attribute of the XML element. Changes to the contents of
|
|
1610
|
+
the BinnedArray object affect the XML document tree.
|
|
1611
|
+
"""
|
|
1612
|
+
elem = cls.get_xml_root(xml, name)
|
|
1613
|
+
self = cls(NDBins.from_xml(elem), array = ligolw.Array.get_array(elem, "array").array)
|
|
1614
|
+
# sanity check
|
|
1615
|
+
if self.bins.shape != self.array.shape:
|
|
1616
|
+
raise ValueError("'%s' binning shape does not match array shape: %s != %s" % (name, self.bins.shape, self.array.shape))
|
|
1617
|
+
# done
|
|
1618
|
+
return self
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
#
|
|
1622
|
+
# =============================================================================
|
|
1623
|
+
#
|
|
1624
|
+
# Binned Array Interpolator
|
|
1625
|
+
#
|
|
1626
|
+
# =============================================================================
|
|
1627
|
+
#
|
|
1628
|
+
|
|
1629
|
+
|
|
1630
|
+
def InterpBinnedArray(binnedarray, fill_value = 0.0):
|
|
1631
|
+
"""
|
|
1632
|
+
Wrapper constructing a scipy.interpolate interpolator from the
|
|
1633
|
+
contents of a BinnedArray. Only piecewise linear interpolators are
|
|
1634
|
+
supported. In 1 and 2 dimensions, scipy.interpolate.interp1d and
|
|
1635
|
+
.interp2d is used, respectively. In more than 2 dimensions
|
|
1636
|
+
scipy.interpolate.LinearNDInterpolator is used.
|
|
1637
|
+
|
|
1638
|
+
Example:
|
|
1639
|
+
|
|
1640
|
+
One dimension
|
|
1641
|
+
|
|
1642
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 2.5, 3),)))
|
|
1643
|
+
>>> x[0,] = 0
|
|
1644
|
+
>>> x[1,] = 1
|
|
1645
|
+
>>> x[2,] = 3
|
|
1646
|
+
>>> y = InterpBinnedArray(x)
|
|
1647
|
+
>>> y(0)
|
|
1648
|
+
0.0
|
|
1649
|
+
>>> y(1)
|
|
1650
|
+
1.0
|
|
1651
|
+
>>> y(2)
|
|
1652
|
+
3.0
|
|
1653
|
+
>>> y(0.5)
|
|
1654
|
+
0.5
|
|
1655
|
+
>>> y(1.5)
|
|
1656
|
+
2.0
|
|
1657
|
+
|
|
1658
|
+
Two dimensions
|
|
1659
|
+
|
|
1660
|
+
>>> x = BinnedArray(NDBins((LinearBins(-0.5, 2.5, 3), LinearBins(-0.5, 1.5, 2))))
|
|
1661
|
+
>>> x[0, 0] = 0
|
|
1662
|
+
>>> x[0, 1] = 1
|
|
1663
|
+
>>> x[1, 0] = 2
|
|
1664
|
+
>>> x[1, 1] = 4
|
|
1665
|
+
>>> x[2, 0] = 2
|
|
1666
|
+
>>> x[2, 1] = 4
|
|
1667
|
+
>>> y = InterpBinnedArray(x)
|
|
1668
|
+
>>> y(0, 0)
|
|
1669
|
+
0.0
|
|
1670
|
+
>>> y(0, 1)
|
|
1671
|
+
1.0
|
|
1672
|
+
>>> y(1, 0)
|
|
1673
|
+
2.0
|
|
1674
|
+
>>> y(1, 1)
|
|
1675
|
+
4.0
|
|
1676
|
+
>>> y(2, 0)
|
|
1677
|
+
2.0
|
|
1678
|
+
>>> y(2, 1)
|
|
1679
|
+
4.0
|
|
1680
|
+
>>> y(0, 0.25)
|
|
1681
|
+
0.25
|
|
1682
|
+
>>> y(0, 0.75)
|
|
1683
|
+
0.75
|
|
1684
|
+
>>> y(0.25, 0)
|
|
1685
|
+
0.5
|
|
1686
|
+
>>> y(0.75, 0)
|
|
1687
|
+
1.5
|
|
1688
|
+
>>> y(0.25, 1)
|
|
1689
|
+
1.75
|
|
1690
|
+
>>> y(0.75, 1)
|
|
1691
|
+
3.25
|
|
1692
|
+
>>> y(1, 0.25)
|
|
1693
|
+
2.5
|
|
1694
|
+
>>> y(1, 0.75)
|
|
1695
|
+
3.5
|
|
1696
|
+
|
|
1697
|
+
BUGS: Due to bugs in some versions of scipy and numpy, if an old
|
|
1698
|
+
version of scipy and/or numpy is detected this code falls back to
|
|
1699
|
+
home-grown piece-wise linear interpolator code for 1- and 2
|
|
1700
|
+
dimensions that is slow, and in 3- and higher dimensions the
|
|
1701
|
+
fall-back is to nearest-neighbour "interpolation".
|
|
1702
|
+
"""
|
|
1703
|
+
# the upper and lower boundaries of the binnings are added as
|
|
1704
|
+
# additional co-ordinates with the array being assumed to equal
|
|
1705
|
+
# fill_value at those points. this solves the problem of providing
|
|
1706
|
+
# a valid function in the outer halves of the first and last bins.
|
|
1707
|
+
|
|
1708
|
+
# coords[0] = co-ordinates along 1st dimension,
|
|
1709
|
+
# coords[1] = co-ordinates along 2nd dimension,
|
|
1710
|
+
# ...
|
|
1711
|
+
coords = tuple(numpy.hstack((l[0], c, u[-1])) for l, c, u in zip(binnedarray.bins.lower(), binnedarray.bins.centres(), binnedarray.bins.upper()))
|
|
1712
|
+
|
|
1713
|
+
# pad the contents of the binned array with 1 element of fill_value
|
|
1714
|
+
# on each side in each dimension
|
|
1715
|
+
z = binnedarray.at_centres()
|
|
1716
|
+
z = numpy.pad(z, [(1, 1)] * z.ndim, mode = "constant", constant_values = [(fill_value, fill_value)] * z.ndim)
|
|
1717
|
+
|
|
1718
|
+
# if any co-ordinates are infinite, remove them. also remove
|
|
1719
|
+
# degenerate co-ordinates from ends
|
|
1720
|
+
slices = []
|
|
1721
|
+
for c in coords:
|
|
1722
|
+
finite_indexes, = numpy.isfinite(c).nonzero()
|
|
1723
|
+
assert len(finite_indexes) != 0
|
|
1724
|
+
|
|
1725
|
+
lo, hi = finite_indexes.min(), finite_indexes.max()
|
|
1726
|
+
|
|
1727
|
+
while lo < hi and c[lo + 1] == c[lo]:
|
|
1728
|
+
lo += 1
|
|
1729
|
+
while lo < hi and c[hi - 1] == c[hi]:
|
|
1730
|
+
hi -= 1
|
|
1731
|
+
assert lo < hi
|
|
1732
|
+
|
|
1733
|
+
slices.append(slice(lo, hi + 1))
|
|
1734
|
+
coords = tuple(c[s] for c, s in zip(coords, slices))
|
|
1735
|
+
z = z[tuple(slices)]
|
|
1736
|
+
|
|
1737
|
+
# build the interpolator from the co-ordinates and array data.
|
|
1738
|
+
# scipy/numpy interpolators return an array-like thing so we have
|
|
1739
|
+
# to wrap them, in turn, in a float cast
|
|
1740
|
+
if len(coords) == 1:
|
|
1741
|
+
try:
|
|
1742
|
+
interp1d
|
|
1743
|
+
except NameError:
|
|
1744
|
+
# FIXME: remove when we can rely on a new-enough scipy
|
|
1745
|
+
coords0 = coords[0]
|
|
1746
|
+
lo, hi = coords[0][0], coords[0][-1]
|
|
1747
|
+
with numpy.errstate(invalid = "ignore"):
|
|
1748
|
+
dz_over_dcoords0 = (z[1:] - z[:-1]) / (coords0[1:] - coords0[:-1])
|
|
1749
|
+
isinf = math.isinf
|
|
1750
|
+
def interp(x):
|
|
1751
|
+
if not lo < x < hi:
|
|
1752
|
+
return fill_value
|
|
1753
|
+
i = coords0.searchsorted(x) - 1
|
|
1754
|
+
if isinf(z[i]):
|
|
1755
|
+
return z[i]
|
|
1756
|
+
return z[i] + (x - coords0[i]) * dz_over_dcoords0[i]
|
|
1757
|
+
return interp
|
|
1758
|
+
interp = interp1d(coords[0], z, kind = "linear", copy = False, bounds_error = False, fill_value = fill_value)
|
|
1759
|
+
return lambda *coords: float(interp(*coords))
|
|
1760
|
+
elif len(coords) == 2:
|
|
1761
|
+
try:
|
|
1762
|
+
interp2d
|
|
1763
|
+
except NameError:
|
|
1764
|
+
# FIXME: remove when we can rely on a new-enough scipy
|
|
1765
|
+
coords0 = coords[0]
|
|
1766
|
+
coords1 = coords[1]
|
|
1767
|
+
lox, hix = coords0[0], coords0[-1]
|
|
1768
|
+
loy, hiy = coords1[0], coords1[-1]
|
|
1769
|
+
dcoords0 = coords0[1:] - coords0[:-1]
|
|
1770
|
+
dcoords1 = coords1[1:] - coords1[:-1]
|
|
1771
|
+
with numpy.errstate(invalid = "ignore"):
|
|
1772
|
+
dz0 = z[1:,:] - z[:-1,:]
|
|
1773
|
+
dz1 = z[:,1:] - z[:,:-1]
|
|
1774
|
+
isinf = math.isinf
|
|
1775
|
+
def interp(x, y):
|
|
1776
|
+
if not (lox < x < hix and loy < y < hiy):
|
|
1777
|
+
return fill_value
|
|
1778
|
+
i = coords0.searchsorted(x) - 1
|
|
1779
|
+
j = coords1.searchsorted(y) - 1
|
|
1780
|
+
dx = (x - coords0[i]) / dcoords0[i]
|
|
1781
|
+
dy = (y - coords1[j]) / dcoords1[j]
|
|
1782
|
+
if dx + dy <= 1.:
|
|
1783
|
+
if isinf(z[i, j]):
|
|
1784
|
+
return z[i, j]
|
|
1785
|
+
return z[i, j] + dx * dz0[i, j] + dy * dz1[i, j]
|
|
1786
|
+
if isinf(z[i + 1, j + 1]):
|
|
1787
|
+
return z[i + 1, j + 1]
|
|
1788
|
+
return z[i + 1, j + 1] + (1. - dx) * -dz0[i, j + 1] + (1. - dy) * -dz1[i + 1, j]
|
|
1789
|
+
return interp
|
|
1790
|
+
interp = interp2d(coords[0], coords[1], z.T, kind = "linear", copy = False, bounds_error = False, fill_value = fill_value)
|
|
1791
|
+
return lambda *coords: float(interp(*coords))
|
|
1792
|
+
else:
|
|
1793
|
+
try:
|
|
1794
|
+
LinearNDInterpolator
|
|
1795
|
+
except NameError:
|
|
1796
|
+
# FIXME: remove when we can rely on a new-enough scipy
|
|
1797
|
+
def interp(*coords):
|
|
1798
|
+
try:
|
|
1799
|
+
return binnedarray[coords]
|
|
1800
|
+
except IndexError:
|
|
1801
|
+
return fill_value
|
|
1802
|
+
return interp
|
|
1803
|
+
interp = LinearNDInterpolator(list(itertools.product(*coords)), z.flat, fill_value = fill_value)
|
|
1804
|
+
return lambda *coords: float(interp(*coords))
|
|
1805
|
+
|
|
1806
|
+
|
|
1807
|
+
#
|
|
1808
|
+
# =============================================================================
|
|
1809
|
+
#
|
|
1810
|
+
# BinnedDensity
|
|
1811
|
+
#
|
|
1812
|
+
# =============================================================================
|
|
1813
|
+
#
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
class BinnedDensity(BinnedArray):
|
|
1817
|
+
"""
|
|
1818
|
+
Variant of the BinnedArray type that interprets its contents as a
|
|
1819
|
+
density. When initialized, the volumes of the NDBins used to
|
|
1820
|
+
create the BinnedDensity are computed and stored in the .volume
|
|
1821
|
+
attribute. When a value is retrieved from a bin, the value
|
|
1822
|
+
reported is the value stored in the bin divided by the bin's
|
|
1823
|
+
volume. When a value is assigned to a bin, the value recorded is
|
|
1824
|
+
the assigned value multiplied by the bin's volume. The true values
|
|
1825
|
+
recorded in the bins can be accessed via the .count attribute,
|
|
1826
|
+
which is a BinnedArray object wrapping the same array and binning.
|
|
1827
|
+
|
|
1828
|
+
Because the internal array stores counts, not the densities, when
|
|
1829
|
+
the data in an instance of this class is processed with the
|
|
1830
|
+
filter_array() function the result is the density of smoothed
|
|
1831
|
+
counts, not the smoothed density. This is best illustrated with an
|
|
1832
|
+
example.
|
|
1833
|
+
|
|
1834
|
+
Example: linear bins
|
|
1835
|
+
|
|
1836
|
+
>>> # bins are 2 units, each
|
|
1837
|
+
>>> x = BinnedDensity(NDBins((LinearBins(0, 10, 5),)))
|
|
1838
|
+
>>> x.volume
|
|
1839
|
+
array([ 2., 2., 2., 2., 2.])
|
|
1840
|
+
>>> # set count at 5 to 1
|
|
1841
|
+
>>> x.count[5.0,] = 1
|
|
1842
|
+
>>> # internal array set to 1 in that bin
|
|
1843
|
+
>>> x.array
|
|
1844
|
+
array([ 0., 0., 1., 0., 0.])
|
|
1845
|
+
>>> # density reported is 0.5
|
|
1846
|
+
>>> print x[5.0,]
|
|
1847
|
+
0.5
|
|
1848
|
+
>>> # convolve counts with 3-bin top hat window
|
|
1849
|
+
>>> filter_array(x.array, tophat_window(3))
|
|
1850
|
+
array([ 0. , 0.33333333, 0.33333333, 0.33333333, 0. ])
|
|
1851
|
+
>>> # density at 5 is now 1/6 counts / unit interval
|
|
1852
|
+
>>> print x[5.0,]
|
|
1853
|
+
0.166666666667
|
|
1854
|
+
>>> # total count has been preserved
|
|
1855
|
+
>>> print x.array.sum()
|
|
1856
|
+
1.0
|
|
1857
|
+
|
|
1858
|
+
Example: logarithmic bins
|
|
1859
|
+
|
|
1860
|
+
>>> # bins increase in size by a factor of 2, each
|
|
1861
|
+
>>> x = BinnedDensity(NDBins((LogarithmicBins(1, 32, 5),)))
|
|
1862
|
+
>>> x.volume
|
|
1863
|
+
array([ 1., 2., 4., 8., 16.])
|
|
1864
|
+
>>> # set count at 5 to 1
|
|
1865
|
+
>>> x.count[5.0,] = 1
|
|
1866
|
+
>>> # internal array set to 1 in that bin
|
|
1867
|
+
>>> x.array
|
|
1868
|
+
array([ 0., 0., 1., 0., 0.])
|
|
1869
|
+
>>> # density reported is 0.25
|
|
1870
|
+
>>> print x[5.0,]
|
|
1871
|
+
0.25
|
|
1872
|
+
>>> # convolve counts with 3-bin top hat window
|
|
1873
|
+
>>> filter_array(x.array, tophat_window(3))
|
|
1874
|
+
array([ 0. , 0.33333333, 0.33333333, 0.33333333, 0. ])
|
|
1875
|
+
>>> # density at 5 is now 1/12 counts / unit interval
|
|
1876
|
+
>>> print x[5.0,]
|
|
1877
|
+
0.0833333333333
|
|
1878
|
+
>>> # density is 1/6 in bin below
|
|
1879
|
+
>>> print x[3,]
|
|
1880
|
+
0.166666666667
|
|
1881
|
+
>>> # and 1/24 in bin above
|
|
1882
|
+
>>> print x[10,]
|
|
1883
|
+
0.0416666666667
|
|
1884
|
+
>>> # total count has been preserved
|
|
1885
|
+
>>> print x.array.sum()
|
|
1886
|
+
1.0
|
|
1887
|
+
|
|
1888
|
+
Explanation. In the linear case there are five bins spanning the
|
|
1889
|
+
interval [0, 10], making each bin 2 "units" in size. A single
|
|
1890
|
+
count is placed at 5.0, which is bin number 2. The averaging
|
|
1891
|
+
filter is a top-hat window 3 bins wide. The single count in bin
|
|
1892
|
+
#2, when averaged over the three bins around it, becomes an average
|
|
1893
|
+
of 1/3 count per bin, each of which is 2 units in size, so the
|
|
1894
|
+
average density is 1/6 events / unit. The count has been spread
|
|
1895
|
+
out over several bins but the integral of the density, the total
|
|
1896
|
+
count, is unchanged. The logarithmic case is identical but because
|
|
1897
|
+
the bin sizes are non-uniform, when the single count is spread
|
|
1898
|
+
across the three bins the density is non-uniform. The integral is
|
|
1899
|
+
still preserved.
|
|
1900
|
+
|
|
1901
|
+
Some binnings have infinite-sized bins. For such bins, the counts
|
|
1902
|
+
may be manipulated directly, as usual, but for all finite counts a
|
|
1903
|
+
density of 0 will be reported for those bins (and NaN for infinite
|
|
1904
|
+
counts), and ValueError will be raised if an attempt is made to
|
|
1905
|
+
assign a density to those bins.
|
|
1906
|
+
|
|
1907
|
+
NOTES:
|
|
1908
|
+
|
|
1909
|
+
- While it is technically possible to modify the binning parameters
|
|
1910
|
+
after creating an instance of this class, the steps required to
|
|
1911
|
+
bring all internal data back into consistency following such a
|
|
1912
|
+
change are undocumented. One should consider the metadata
|
|
1913
|
+
carried by these objects to be immutable.
|
|
1914
|
+
"""
|
|
1915
|
+
def __init__(self, *args, **kwargs):
|
|
1916
|
+
super(BinnedDensity, self).__init__(*args, **kwargs)
|
|
1917
|
+
self.count = BinnedArray(self.bins, array = self.array)
|
|
1918
|
+
self.volume = self.bins.volumes()
|
|
1919
|
+
|
|
1920
|
+
def __getitem__(self, coords):
|
|
1921
|
+
coords = self.bins(*coords)
|
|
1922
|
+
return self.array[coords] / self.volume[coords]
|
|
1923
|
+
|
|
1924
|
+
def __setitem__(self, coords, val):
|
|
1925
|
+
coords = self.bins(*coords)
|
|
1926
|
+
vol = self.volume[coords]
|
|
1927
|
+
if numpy.isinf(vol).any():
|
|
1928
|
+
raise ValueError("cannot assign density values to infinite-volume bins, try assigning a count instead")
|
|
1929
|
+
self.array[coords] = val * vol
|
|
1930
|
+
|
|
1931
|
+
def at_centres(self):
|
|
1932
|
+
return self.array / self.volume
|
|
1933
|
+
|
|
1934
|
+
def marginalize(self, dim):
|
|
1935
|
+
"""
|
|
1936
|
+
Return a new BinnedDensity object containing the density
|
|
1937
|
+
integrated over dimension dim.
|
|
1938
|
+
|
|
1939
|
+
Example:
|
|
1940
|
+
|
|
1941
|
+
>>> # 5x5 mesh of bins, each with volume = 4
|
|
1942
|
+
>>> x = BinnedDensity(NDBins((LinearBins(0, 10, 5), LinearBins(0, 10, 5))))
|
|
1943
|
+
>>> # set count at 5,5 to 1
|
|
1944
|
+
>>> x.count[5.0, 5.0] = 1
|
|
1945
|
+
>>> # density in central bin is 1/4
|
|
1946
|
+
>>> x.at_centres()
|
|
1947
|
+
array([[ 0. , 0. , 0. , 0. , 0. ],
|
|
1948
|
+
[ 0. , 0. , 0. , 0. , 0. ],
|
|
1949
|
+
[ 0. , 0. , 0.25, 0. , 0. ],
|
|
1950
|
+
[ 0. , 0. , 0. , 0. , 0. ],
|
|
1951
|
+
[ 0. , 0. , 0. , 0. , 0. ]])
|
|
1952
|
+
>>> # convolve counts with 3-bin top-hat window
|
|
1953
|
+
>>> filter_array(x.array, tophat_window(3, 3))
|
|
1954
|
+
array([[ 0. , 0. , 0. , 0. , 0. ],
|
|
1955
|
+
[ 0. , 0.11111111, 0.11111111, 0.11111111, 0. ],
|
|
1956
|
+
[ 0. , 0.11111111, 0.11111111, 0.11111111, 0. ],
|
|
1957
|
+
[ 0. , 0.11111111, 0.11111111, 0.11111111, 0. ],
|
|
1958
|
+
[ 0. , 0. , 0. , 0. , 0. ]])
|
|
1959
|
+
>>> # density is now 1/(4 * 9) = 1/36 in 9 central bins
|
|
1960
|
+
>>> x.at_centres()
|
|
1961
|
+
array([[ 0. , 0. , 0. , 0. , 0. ],
|
|
1962
|
+
[ 0. , 0.02777778, 0.02777778, 0.02777778, 0. ],
|
|
1963
|
+
[ 0. , 0.02777778, 0.02777778, 0.02777778, 0. ],
|
|
1964
|
+
[ 0. , 0.02777778, 0.02777778, 0.02777778, 0. ],
|
|
1965
|
+
[ 0. , 0. , 0. , 0. , 0. ]])
|
|
1966
|
+
>>> # densities still sum (not integrate) to 1/4
|
|
1967
|
+
>>> x.at_centres().sum()
|
|
1968
|
+
0.25
|
|
1969
|
+
>>> # integrate over dimension 1
|
|
1970
|
+
>>> x = x.marginalize(1)
|
|
1971
|
+
>>> # bin volumes are now 2
|
|
1972
|
+
>>> # densities in 3 central bins are = 1/(2 * 3) = 1/6
|
|
1973
|
+
>>> x.at_centres()
|
|
1974
|
+
array([ 0. , 0.16666667, 0.16666667, 0.16666667, 0. ])
|
|
1975
|
+
>>> # densities sum (not integrate) to 1/2
|
|
1976
|
+
>>> x.at_centres().sum()
|
|
1977
|
+
0.5
|
|
1978
|
+
"""
|
|
1979
|
+
return type(self)(NDBins(self.bins[:dim] + self.bins[dim+1:]), self.array.sum(axis = dim))
|
|
1980
|
+
|
|
1981
|
+
|
|
1982
|
+
#
|
|
1983
|
+
# A discretely sampled PDF.
|
|
1984
|
+
#
|
|
1985
|
+
|
|
1986
|
+
|
|
1987
|
+
class BinnedLnPDF(BinnedDensity):
|
|
1988
|
+
"""
|
|
1989
|
+
Variant of the BinnedDensity class that (i) tracks a normalization
|
|
1990
|
+
which it uses to rescale the reported density so that its integral
|
|
1991
|
+
is one, and (ii) reports the natural logarithm of that normalized
|
|
1992
|
+
density.
|
|
1993
|
+
|
|
1994
|
+
The .normalize() method needs to be invoked after any manipulation
|
|
1995
|
+
of the contents of the array before the results of .__getitem__()
|
|
1996
|
+
are meaningful.
|
|
1997
|
+
|
|
1998
|
+
How the normalization is tracked should be assumed to be
|
|
1999
|
+
undocumented. In this class the .norm attribute contains the
|
|
2000
|
+
natural log of the sum of the counts in all bins and this value is
|
|
2001
|
+
subtracted from the natural log of the (unnormalized) density in
|
|
2002
|
+
the .__getitem__() method, but subclasses are free to implement
|
|
2003
|
+
whatever unique mechanism is appropriate for themselves.
|
|
2004
|
+
|
|
2005
|
+
As with the BinnedDensity class, the internal array contains counts
|
|
2006
|
+
(not densities, nor natural logarithms of densities), and the
|
|
2007
|
+
.count attribute continues to be a BinnedArray interface to those
|
|
2008
|
+
counts. The intention is for the counts themselves to provide an
|
|
2009
|
+
additional degree of freedom apart from the normalized density.
|
|
2010
|
+
For example, see the .__iadd__() method where it is assumed that
|
|
2011
|
+
the total count encodes a relative weight to be used when
|
|
2012
|
+
marginalizing over two PDFs.
|
|
2013
|
+
|
|
2014
|
+
Example:
|
|
2015
|
+
|
|
2016
|
+
>>> # 5x5 mesh of bins each with volume = 4
|
|
2017
|
+
>>> x = BinnedLnPDF(NDBins((LinearBins(0, 10, 5), LinearBins(0, 10, 5))))
|
|
2018
|
+
>>> # set count at 5,5 to 36 and normalize
|
|
2019
|
+
>>> x.count[5.0, 5.0] = 36
|
|
2020
|
+
>>> x.normalize()
|
|
2021
|
+
>>> # log probability density = ln 1/4 = -1.3862943611198906
|
|
2022
|
+
>>> x.at_centres()
|
|
2023
|
+
array([[ -inf, -inf, -inf, -inf, -inf],
|
|
2024
|
+
[ -inf, -inf, -inf, -inf, -inf],
|
|
2025
|
+
[ -inf, -inf, -1.38629436, -inf, -inf],
|
|
2026
|
+
[ -inf, -inf, -inf, -inf, -inf],
|
|
2027
|
+
[ -inf, -inf, -inf, -inf, -inf]])
|
|
2028
|
+
>>> # convolve with 3x3 top-hat window. in general one must renormalize after this, but we'll skip that here because the demo is constructed so as to not need it
|
|
2029
|
+
>>> filter_array(x.array, tophat_window(3, 3))
|
|
2030
|
+
array([[ 0., 0., 0., 0., 0.],
|
|
2031
|
+
[ 0., 4., 4., 4., 0.],
|
|
2032
|
+
[ 0., 4., 4., 4., 0.],
|
|
2033
|
+
[ 0., 4., 4., 4., 0.],
|
|
2034
|
+
[ 0., 0., 0., 0., 0.]])
|
|
2035
|
+
>>> # ln probability density = ln 1/(4 * 9) = -3.58351893845611
|
|
2036
|
+
>>> x.at_centres()
|
|
2037
|
+
array([[ -inf, -inf, -inf, -inf, -inf],
|
|
2038
|
+
[ -inf, -3.58351894, -3.58351894, -3.58351894, -inf],
|
|
2039
|
+
[ -inf, -3.58351894, -3.58351894, -3.58351894, -inf],
|
|
2040
|
+
[ -inf, -3.58351894, -3.58351894, -3.58351894, -inf],
|
|
2041
|
+
[ -inf, -inf, -inf, -inf, -inf]])
|
|
2042
|
+
>>> # .marginzlize() preserves normalization
|
|
2043
|
+
>>> y = x.marginalize(1)
|
|
2044
|
+
>>> y.count.at_centres()
|
|
2045
|
+
array([ 0., 12., 12., 12., 0.])
|
|
2046
|
+
>>> # ln probability density = ln 1/(2 * 3) = -1.791759469228055
|
|
2047
|
+
>>> y.at_centres()
|
|
2048
|
+
array([ -inf, -1.79175947, -1.79175947, -1.79175947, -inf])
|
|
2049
|
+
>>> # assuming \\sqrt{N} counting fluctuations, compute the fractional uncertainty
|
|
2050
|
+
>>> import numpy
|
|
2051
|
+
>>> d = BinnedArray(x.bins, 1. / numpy.sqrt(x.count.at_centres()))
|
|
2052
|
+
>>> d.at_centres()
|
|
2053
|
+
array([[ inf, inf, inf, inf, inf],
|
|
2054
|
+
[ inf, 0.5, 0.5, 0.5, inf],
|
|
2055
|
+
[ inf, 0.5, 0.5, 0.5, inf],
|
|
2056
|
+
[ inf, 0.5, 0.5, 0.5, inf],
|
|
2057
|
+
[ inf, inf, inf, inf, inf]])
|
|
2058
|
+
"""
|
|
2059
|
+
def __init__(self, *args, **kwargs):
|
|
2060
|
+
super(BinnedLnPDF, self).__init__(*args, **kwargs)
|
|
2061
|
+
self.normalize()
|
|
2062
|
+
|
|
2063
|
+
def __getitem__(self, coords):
|
|
2064
|
+
return numpy.log(super(BinnedLnPDF, self).__getitem__(coords)) - self.norm
|
|
2065
|
+
|
|
2066
|
+
def __setitem__(self, coords, val):
|
|
2067
|
+
#
|
|
2068
|
+
# the relationship between the density, p, the
|
|
2069
|
+
# volume, Delta, the count, x, and the overall
|
|
2070
|
+
# normalization, N, for a bin i is
|
|
2071
|
+
#
|
|
2072
|
+
# p_i = x_i / (Delta_i * N)
|
|
2073
|
+
#
|
|
2074
|
+
# where N = sum_j x_j, and 0 <= p_i * Delta_i <= 1.
|
|
2075
|
+
#
|
|
2076
|
+
# to maintain the requirement that the density integrate to
|
|
2077
|
+
# 1, it is not possible to change the density in one bin
|
|
2078
|
+
# without changing the densities in all other bins.
|
|
2079
|
+
# because there is no unique way to assign new densities to
|
|
2080
|
+
# all other bins that preserves the normalization invariant
|
|
2081
|
+
# there is an ambiguity in how to implement the assignment
|
|
2082
|
+
# operation. instead of choosing one, we elect to forbid
|
|
2083
|
+
# this operation.
|
|
2084
|
+
#
|
|
2085
|
+
# one interpretation could be to require the counts in all
|
|
2086
|
+
# other bins to be preserved, and solve for the new count
|
|
2087
|
+
# for the bin in question (and new total count). because
|
|
2088
|
+
# the counts in other bins are held constant the likelihood
|
|
2089
|
+
# ratios among them are preserved.
|
|
2090
|
+
#
|
|
2091
|
+
# solving for x_i,
|
|
2092
|
+
#
|
|
2093
|
+
# p_i * Delta_i
|
|
2094
|
+
# x_i = ----------------- sum_(j != i) x_j.
|
|
2095
|
+
# 1 - p_i * Delta_i
|
|
2096
|
+
#
|
|
2097
|
+
# the normalization would then need to be recomputed given
|
|
2098
|
+
# the new count for bin i. forbidden cases include:
|
|
2099
|
+
# infinite bin size, total count is initially 0,
|
|
2100
|
+
# p_i*Delta_i = 1 unless bin i is the only non-zero bin to
|
|
2101
|
+
# begin with.
|
|
2102
|
+
#
|
|
2103
|
+
# another interpretation could be to require the total
|
|
2104
|
+
# count to be preserved and also the likelihood ratios
|
|
2105
|
+
# among all other bins. because the total count is being
|
|
2106
|
+
# preserved, the new x_i can be obtained immediately as
|
|
2107
|
+
#
|
|
2108
|
+
# x_i' = N * p_i * Delta_i
|
|
2109
|
+
#
|
|
2110
|
+
# the counts in all other bins are obtained by rescaling by
|
|
2111
|
+
# the after-to-before ratio of the non-bin-i count.
|
|
2112
|
+
#
|
|
2113
|
+
# x_(j != i) = x_(j != i) * (N - x_i') / (N - x_i)
|
|
2114
|
+
#
|
|
2115
|
+
# because they are scaled by a common factor, the ratios
|
|
2116
|
+
# between them are preserved. forbidden cases include:
|
|
2117
|
+
# infinite bin size, total count is initially 0.
|
|
2118
|
+
#
|
|
2119
|
+
raise NotImplementedError("item assignment operation not defined. assign to .count then invoke .normalize()")
|
|
2120
|
+
|
|
2121
|
+
def mkinterp(self):
|
|
2122
|
+
"""
|
|
2123
|
+
Return an interpolator to evaluate the density as a smooth
|
|
2124
|
+
function of co-ordinates. If the density has not been
|
|
2125
|
+
normalized the interpolator's behaviour is undefined.
|
|
2126
|
+
|
|
2127
|
+
NOTE: the interpolator is the InterpBinnedArray object
|
|
2128
|
+
which might have limitations in 3 or more dimensions. See
|
|
2129
|
+
its documentation for more information.
|
|
2130
|
+
|
|
2131
|
+
NOTE: in the future this is likely to be replaced with
|
|
2132
|
+
some sort of internal mechanism.
|
|
2133
|
+
|
|
2134
|
+
Example:
|
|
2135
|
+
|
|
2136
|
+
>>> # 5-bin linear mesh of bins each with volume = 2
|
|
2137
|
+
>>> x = BinnedLnPDF(NDBins((LinearBins(0, 10, 5), )))
|
|
2138
|
+
>>> # set some counts and normalize
|
|
2139
|
+
>>> x.count[3,] = x.count[7,] = 1
|
|
2140
|
+
>>> x.count[5,] = 2
|
|
2141
|
+
>>> x.normalize()
|
|
2142
|
+
>>> x.count.at_centres()
|
|
2143
|
+
array([ 0., 1., 2., 1., 0.])
|
|
2144
|
+
>>> x.at_centres()
|
|
2145
|
+
array([ -inf, -2.07944154, -1.38629436, -2.07944154, -inf])
|
|
2146
|
+
>>> # construct interpolator object
|
|
2147
|
+
>>> x = x.mkinterp()
|
|
2148
|
+
>>> # log(P) is interpolated linearly, so it can be counter-intuitive where the density drops to 0 in neighbouring bins
|
|
2149
|
+
>>> x(3)
|
|
2150
|
+
-inf
|
|
2151
|
+
>>> # otherwise behaviour is as expected
|
|
2152
|
+
>>> x(4)
|
|
2153
|
+
-1.732867951399863
|
|
2154
|
+
>>> x(4.5)
|
|
2155
|
+
-1.5595811562598769
|
|
2156
|
+
>>> x(5)
|
|
2157
|
+
-1.3862943611198906
|
|
2158
|
+
"""
|
|
2159
|
+
return InterpBinnedArray(self)
|
|
2160
|
+
|
|
2161
|
+
def at_centres(self):
|
|
2162
|
+
with numpy.errstate(divide = "ignore", invalid = "ignore"):
|
|
2163
|
+
return numpy.log(super(BinnedLnPDF, self).at_centres()) - self.norm
|
|
2164
|
+
|
|
2165
|
+
def marginalize(self, dim):
|
|
2166
|
+
new = super(BinnedLnPDF, self).marginalize(dim)
|
|
2167
|
+
new.norm = self.norm
|
|
2168
|
+
return new
|
|
2169
|
+
|
|
2170
|
+
def __iadd__(self, other):
|
|
2171
|
+
"""
|
|
2172
|
+
Adds the counts array and normalization of other to self.
|
|
2173
|
+
If the original two PDFs were normalized then the result is
|
|
2174
|
+
also normalized, otherwise .normalize() needs to be invoked
|
|
2175
|
+
to normalize the result. Once normalized, the result is
|
|
2176
|
+
the PDF marginalized over the two original PDFs (the sum of
|
|
2177
|
+
the two PDFs weighted by the relative total frequency of
|
|
2178
|
+
events in each).
|
|
2179
|
+
|
|
2180
|
+
Example:
|
|
2181
|
+
|
|
2182
|
+
>>> # 5-bin linear mesh of bins each with volume = 2
|
|
2183
|
+
>>> x = BinnedLnPDF(NDBins((LinearBins(0, 10, 5), )))
|
|
2184
|
+
>>> y = BinnedLnPDF(NDBins((LinearBins(0, 10, 5), )))
|
|
2185
|
+
>>> x.count[5,] = 2
|
|
2186
|
+
>>> y.count[3,] = 2
|
|
2187
|
+
>>> x.normalize()
|
|
2188
|
+
>>> y.normalize()
|
|
2189
|
+
>>> x.at_centres()
|
|
2190
|
+
array([ -inf, -inf, -0.69314718, -inf, -inf])
|
|
2191
|
+
>>> x += y
|
|
2192
|
+
>>> x.at_centres()
|
|
2193
|
+
array([ -inf, -1.38629436, -1.38629436, -inf, -inf])
|
|
2194
|
+
"""
|
|
2195
|
+
super(BinnedLnPDF, self).__iadd__(other)
|
|
2196
|
+
# how to add two numbers when all you know are their
|
|
2197
|
+
# logarithms:
|
|
2198
|
+
#
|
|
2199
|
+
# c = a + b
|
|
2200
|
+
#
|
|
2201
|
+
# if b is smaller than a:
|
|
2202
|
+
#
|
|
2203
|
+
# c = a (1 + b/a)
|
|
2204
|
+
# log c = log a + log1p(b / a)
|
|
2205
|
+
# log c = log a + log1p(exp(log b - log a))
|
|
2206
|
+
#
|
|
2207
|
+
# otherwise, if a is smaller than b, do the same but swap a
|
|
2208
|
+
# and b.
|
|
2209
|
+
if self.norm >= other.norm:
|
|
2210
|
+
self.norm += math.log1p(math.exp(other.norm - self.norm))
|
|
2211
|
+
else:
|
|
2212
|
+
self.norm = other.norm + math.log1p(math.exp(self.norm - other.norm))
|
|
2213
|
+
return self
|
|
2214
|
+
|
|
2215
|
+
def __add__(self, other):
|
|
2216
|
+
self = super(BinnedLnPDF, self).__add__(other)
|
|
2217
|
+
self.normalize()
|
|
2218
|
+
return self
|
|
2219
|
+
|
|
2220
|
+
def copy(self):
|
|
2221
|
+
new = super(BinnedLnPDF, self).copy()
|
|
2222
|
+
new.norm = self.norm
|
|
2223
|
+
return new
|
|
2224
|
+
|
|
2225
|
+
def normalize(self):
|
|
2226
|
+
"""
|
|
2227
|
+
Updates the internal normalization. Subclasses override
|
|
2228
|
+
this with custom normalization mechanisms if required.
|
|
2229
|
+
|
|
2230
|
+
Note that counts contained in infinite-sized bins are
|
|
2231
|
+
included in the normalization. In this way the relative
|
|
2232
|
+
frequency with which counts occur in those bins is
|
|
2233
|
+
accounted for in the normalization although the density
|
|
2234
|
+
reported for those bins will be 0.
|
|
2235
|
+
"""
|
|
2236
|
+
self.norm = self.array.sum()
|
|
2237
|
+
assert self.norm >= 0. or math.isnan(self.norm)
|
|
2238
|
+
self.norm = math.log(self.norm) if self.norm != 0. else NegInf
|
|
2239
|
+
|
|
2240
|
+
def to_xml(self, *args, **kwargs):
|
|
2241
|
+
elem = super(BinnedLnPDF, self).to_xml(*args, **kwargs)
|
|
2242
|
+
elem.appendChild(ligolw.Param.from_pyvalue("norm", self.norm))
|
|
2243
|
+
return elem
|
|
2244
|
+
|
|
2245
|
+
@classmethod
|
|
2246
|
+
def from_xml(cls, xml, name):
|
|
2247
|
+
elem = cls.get_xml_root(xml, name)
|
|
2248
|
+
self = super(BinnedLnPDF, cls).from_xml(elem, name)
|
|
2249
|
+
self.norm = ligolw.Param.get_param(elem, "norm").value
|
|
2250
|
+
return self
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
#
|
|
2254
|
+
# =============================================================================
|
|
2255
|
+
#
|
|
2256
|
+
# Windows
|
|
2257
|
+
#
|
|
2258
|
+
# =============================================================================
|
|
2259
|
+
#
|
|
2260
|
+
|
|
2261
|
+
|
|
2262
|
+
def gaussian_window(*bins, **kwargs):
|
|
2263
|
+
"""
|
|
2264
|
+
Generate a normalized (integral = 1) Gaussian window in N
|
|
2265
|
+
dimensions. The bins parameters set the width of the window in bin
|
|
2266
|
+
counts in each dimension. The optional keyword argument sigma,
|
|
2267
|
+
which defaults to 10, sets the size of the array in all dimensions
|
|
2268
|
+
in units of the width in each dimension. The sizes are adjusted so
|
|
2269
|
+
that the array has an odd number of samples in each dimension, and
|
|
2270
|
+
the Gaussian is peaked on the middle sample.
|
|
2271
|
+
|
|
2272
|
+
Example:
|
|
2273
|
+
|
|
2274
|
+
>>> # 2D window with width of 1.5 bins in first dimension,
|
|
2275
|
+
>>> # 1 bin in second dimension, 3 widths long (rounded to odd
|
|
2276
|
+
>>> # integer = 5 x 3 bins) in each dimension
|
|
2277
|
+
>>> gaussian_window(1.5, 1, sigma = 3)
|
|
2278
|
+
array([[ 0.00161887, 0.01196189, 0.00161887],
|
|
2279
|
+
[ 0.02329859, 0.17215456, 0.02329859],
|
|
2280
|
+
[ 0.05667207, 0.41875314, 0.05667207],
|
|
2281
|
+
[ 0.02329859, 0.17215456, 0.02329859],
|
|
2282
|
+
[ 0.00161887, 0.01196189, 0.00161887]])
|
|
2283
|
+
"""
|
|
2284
|
+
if not bins:
|
|
2285
|
+
raise ValueError("function requires at least 1 width")
|
|
2286
|
+
if min(bins) < 0.:
|
|
2287
|
+
raise ValueError("widths must be non-negative, got %s" % str(bins))
|
|
2288
|
+
sigma = kwargs.pop("sigma", 10)
|
|
2289
|
+
if kwargs:
|
|
2290
|
+
raise ValueError("unrecognized keyword argument(s): %s" % ",".join(kwargs))
|
|
2291
|
+
windows = []
|
|
2292
|
+
for b in bins:
|
|
2293
|
+
l = int(math.floor(sigma * b / 2.0)) * 2
|
|
2294
|
+
w = lal.CreateGaussREAL8Window(l + 1, l / float(b) if b else PosInf)
|
|
2295
|
+
windows.append(w.data.data / w.sum)
|
|
2296
|
+
if len(windows) == 1:
|
|
2297
|
+
# 1D short-cut
|
|
2298
|
+
return windows[0]
|
|
2299
|
+
try:
|
|
2300
|
+
# only works upto 26 dimensions, but that's 2 trillion bins
|
|
2301
|
+
# if there are just 3 bins along each side, so it's
|
|
2302
|
+
# unlikely to be a practical limitation; for a while at
|
|
2303
|
+
# least
|
|
2304
|
+
assert len(windows) <= 26
|
|
2305
|
+
return numpy.einsum(",".join("abcdefghijklmnopqrstuvwxyz"[:len(windows)]), *windows)
|
|
2306
|
+
except AttributeError:
|
|
2307
|
+
# numpy < 1.6
|
|
2308
|
+
window = reduce(numpy.outer, windows)
|
|
2309
|
+
window.shape = tuple(len(w) for w in windows)
|
|
2310
|
+
return window
|
|
2311
|
+
|
|
2312
|
+
|
|
2313
|
+
def tophat_window(*bins):
|
|
2314
|
+
"""
|
|
2315
|
+
Generate a normalized (integral = 1) rectangular window in N
|
|
2316
|
+
dimensions. The bins parameters set the width of the window in bin
|
|
2317
|
+
counts in each dimension, each of which must be positive and will
|
|
2318
|
+
be rounded up to the nearest odd integer.
|
|
2319
|
+
|
|
2320
|
+
Example:
|
|
2321
|
+
|
|
2322
|
+
>>> tophat_window(4)
|
|
2323
|
+
array([ 0.2, 0.2, 0.2, 0.2, 0.2])
|
|
2324
|
+
>>> tophat_window(4, 4)
|
|
2325
|
+
array([[ 0.04, 0.04, 0.04, 0.04, 0.04],
|
|
2326
|
+
[ 0.04, 0.04, 0.04, 0.04, 0.04],
|
|
2327
|
+
[ 0.04, 0.04, 0.04, 0.04, 0.04],
|
|
2328
|
+
[ 0.04, 0.04, 0.04, 0.04, 0.04],
|
|
2329
|
+
[ 0.04, 0.04, 0.04, 0.04, 0.04]])
|
|
2330
|
+
"""
|
|
2331
|
+
if not bins:
|
|
2332
|
+
raise ValueError("function requires at least 1 width")
|
|
2333
|
+
if min(bins) <= 0:
|
|
2334
|
+
raise ValueError("widths must be positive, got %s" % str(bins))
|
|
2335
|
+
windows = []
|
|
2336
|
+
for b in bins:
|
|
2337
|
+
w = lal.CreateRectangularREAL8Window(int(math.floor(b / 2.0)) * 2 + 1)
|
|
2338
|
+
windows.append(w.data.data / w.sum)
|
|
2339
|
+
if len(windows) == 1:
|
|
2340
|
+
# 1D short-cut
|
|
2341
|
+
return windows[0]
|
|
2342
|
+
try:
|
|
2343
|
+
# only works upto 26 dimensions, but that's 2 trillion bins
|
|
2344
|
+
# if there are just 3 bins along each side, so it's
|
|
2345
|
+
# unlikely to be a practical limitation; for a while at
|
|
2346
|
+
# least
|
|
2347
|
+
assert len(windows) <= 26
|
|
2348
|
+
return numpy.einsum(",".join("abcdefghijklmnopqrstuvwxyz"[:len(windows)]), *windows)
|
|
2349
|
+
except AttributeError:
|
|
2350
|
+
# numpy < 1.6
|
|
2351
|
+
window = reduce(numpy.outer, windows)
|
|
2352
|
+
window.shape = tuple(len(w) for w in windows)
|
|
2353
|
+
return window
|
|
2354
|
+
|
|
2355
|
+
|
|
2356
|
+
#
|
|
2357
|
+
# =============================================================================
|
|
2358
|
+
#
|
|
2359
|
+
# Filtering
|
|
2360
|
+
#
|
|
2361
|
+
# =============================================================================
|
|
2362
|
+
#
|
|
2363
|
+
|
|
2364
|
+
|
|
2365
|
+
def filter_array(a, window, cyclic = False, use_fft = True):
|
|
2366
|
+
"""
|
|
2367
|
+
Filter an array using the window function. The transformation is
|
|
2368
|
+
done in place. The data are assumed to be 0 outside of their
|
|
2369
|
+
domain of definition. The window function must have an odd number
|
|
2370
|
+
of samples in each dimension; this is done so that it is always
|
|
2371
|
+
clear which sample is at the window's centre, which helps prevent
|
|
2372
|
+
phase errors. If the window function's size exceeds that of the
|
|
2373
|
+
data in one or more dimensions, the largest allowed central portion
|
|
2374
|
+
of the window function in the affected dimensions will be used.
|
|
2375
|
+
This is done silently; to determine if window function truncation
|
|
2376
|
+
will occur, check for yourself that your window function is smaller
|
|
2377
|
+
than your data in all dimensions.
|
|
2378
|
+
|
|
2379
|
+
If use_fft is True, the window is convolved with the array using
|
|
2380
|
+
FFT convolution. This is done by processing the array in bands of
|
|
2381
|
+
relatively small dynamic range each to work around numerical
|
|
2382
|
+
dynamic range limitation in the FFTs, but the window function is
|
|
2383
|
+
not treated similarly. As a result the window is effectively
|
|
2384
|
+
limited to a few orders of magnitude of dynamic range. If use_fft
|
|
2385
|
+
is False there are no dynamic range issues but the convolution can
|
|
2386
|
+
be quite slow.
|
|
2387
|
+
"""
|
|
2388
|
+
assert not cyclic # no longer supported, maybe in future
|
|
2389
|
+
# check that the window and the data have the same number of
|
|
2390
|
+
# dimensions
|
|
2391
|
+
dims = len(a.shape)
|
|
2392
|
+
if dims != len(window.shape):
|
|
2393
|
+
raise ValueError("array and window dimensions mismatch")
|
|
2394
|
+
# check that all of the window's dimensions have an odd size
|
|
2395
|
+
if 0 in map((1).__and__, window.shape):
|
|
2396
|
+
raise ValueError("window size is not an odd integer in at least 1 dimension")
|
|
2397
|
+
# determine how much of the window function can be used
|
|
2398
|
+
window_slices = []
|
|
2399
|
+
for d in range(dims):
|
|
2400
|
+
if window.shape[d] > a.shape[d]:
|
|
2401
|
+
# largest odd integer <= size of a
|
|
2402
|
+
n = ((a.shape[d] + 1) // 2) * 2 - 1
|
|
2403
|
+
first = (window.shape[d] - n) // 2
|
|
2404
|
+
window_slices.append(slice(first, first + n))
|
|
2405
|
+
else:
|
|
2406
|
+
window_slices.append(slice(0, window.shape[d]))
|
|
2407
|
+
window = window[tuple(window_slices)]
|
|
2408
|
+
|
|
2409
|
+
if use_fft:
|
|
2410
|
+
# this loop works around dynamic range limits in the FFT
|
|
2411
|
+
# convolution code. we move data 4 orders of magnitude at
|
|
2412
|
+
# a time from the original array into a work space,
|
|
2413
|
+
# convolve the work space with the filter, zero the
|
|
2414
|
+
# workspace in any elements that are more than 14 orders of
|
|
2415
|
+
# magnitude below the maximum value in the result, and add
|
|
2416
|
+
# the result to the total.
|
|
2417
|
+
result = numpy.zeros_like(a)
|
|
2418
|
+
while a.any():
|
|
2419
|
+
# copy contents of input array to work space
|
|
2420
|
+
workspace = numpy.copy(a)
|
|
2421
|
+
|
|
2422
|
+
# mask = indexes of elements of work space not more
|
|
2423
|
+
# than 4 orders of magnitude larger than the
|
|
2424
|
+
# smallest non-zero element. these are the
|
|
2425
|
+
# elements to be processed in this iteration
|
|
2426
|
+
abs_workspace = abs(workspace)
|
|
2427
|
+
mask = abs_workspace <= abs_workspace[abs_workspace > 0].min() * 1e4
|
|
2428
|
+
del abs_workspace
|
|
2429
|
+
|
|
2430
|
+
# zero the masked elements in the input array, zero
|
|
2431
|
+
# everything except the masked elements in the work
|
|
2432
|
+
# space
|
|
2433
|
+
a[mask] = 0.
|
|
2434
|
+
workspace[~mask] = 0.
|
|
2435
|
+
del mask
|
|
2436
|
+
|
|
2437
|
+
# convolve the work space with the kernel
|
|
2438
|
+
workspace = signaltools.fftconvolve(workspace, window, mode = "same")
|
|
2439
|
+
|
|
2440
|
+
# determine the largest value in the work space,
|
|
2441
|
+
# and set to zero anything more than 14 orders of
|
|
2442
|
+
# magnitude smaller
|
|
2443
|
+
abs_workspace = abs(workspace)
|
|
2444
|
+
workspace[abs_workspace < abs_workspace.max() * 1e-14] = 0.
|
|
2445
|
+
del abs_workspace
|
|
2446
|
+
|
|
2447
|
+
# sum what remains into the result
|
|
2448
|
+
result += workspace
|
|
2449
|
+
del workspace
|
|
2450
|
+
else:
|
|
2451
|
+
result = signaltools.convolve(a, window, mode = "same")
|
|
2452
|
+
# overwrite the input with the result
|
|
2453
|
+
numpy.copyto(a, result, casting = "no")
|
|
2454
|
+
|
|
2455
|
+
return a
|