Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:46

0001 #!/usr/bin/env python
0002 #
0003 # Copyright (c) 2019 Opticks Team. All Rights Reserved.
0004 #
0005 # This file is part of Opticks
0006 # (see https://bitbucket.org/simoncblyth/opticks).
0007 #
0008 # Licensed under the Apache License, Version 2.0 (the "License"); 
0009 # you may not use this file except in compliance with the License.  
0010 # You may obtain a copy of the License at
0011 #
0012 #   http://www.apache.org/licenses/LICENSE-2.0
0013 #
0014 # Unless required by applicable law or agreed to in writing, software 
0015 # distributed under the License is distributed on an "AS IS" BASIS, 
0016 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
0017 # See the License for the specific language governing permissions and 
0018 # limitations under the License.
0019 #
0020 
0021 """
0022 
0023 """
0024 from __future__ import print_function
0025 import os, sys, logging, numpy as np
0026 from collections import OrderedDict as odict
0027 
0028 from opticks.ana.ctx import Ctx
0029 from opticks.ana.cfh import CFH
0030 from opticks.ana.base import json_save_
0031 from opticks.ana.nbase import chi2, chi2one, vnorm
0032 from opticks.ana.nload import np_load
0033 from opticks.ana.decompression import decompression_bins
0034 from opticks.ana.histype import HisType
0035 from opticks.ana.mattype import MatType
0036 from opticks.ana.evt import Evt
0037 from opticks.ana.evt import nb_
0038 from opticks.ana.abstat import ABStat
0039 #from opticks.ana.dv import Dv, DvTab
0040 from opticks.ana.qdv import QDV, QDVTab
0041 from opticks.ana.make_rst_table import recarray_as_rst
0042 from opticks.ana.metadata import CompareMetadata
0043 from opticks.ana.abprofile import ABProfile
0044 from opticks.ana.absmry import ABSmry
0045 from opticks.ana.level import Level
0046 
0047 log = logging.getLogger(__name__)
0048 
0049 lmap = lambda *args:list(map(*args))
0050 
0051 ratio_ = lambda num,den:float(num)/float(den) if den != 0 else -1 
0052 
0053 
0054 class Maligned(object):
0055     def __init__(self, ab):
0056         self.ab = ab 
0057 
0058         tot = len(ab.a.seqhis)
0059         self.tot = tot
0060         self.maligned = ab.maligned
0061         self.aligned = ab.aligned
0062         self.nmal = len(ab.maligned)
0063         self.nmut = len(ab.misutailed) 
0064 
0065         self.fmaligned = ratio_(self.nmal, tot)
0066         self.faligned = ratio_(len(ab.aligned), tot)
0067         self.sli = slice(0,25)
0068 
0069         self.migs = self.count_migtab() 
0070 
0071     def __getitem__(self, sli):
0072          self.sli = sli
0073          return self
0074 
0075     def lines(self):
0076         return ["aligned  %7d/%7d : %.4f : %s " % ( len(self.aligned), self.tot, self.faligned,   ",".join(map(lambda _:"%d"%_, self.aligned[self.sli])) ),
0077                 "maligned %7d/%7d : %.4f : %s " % ( len(self.maligned), self.tot, self.fmaligned,  ",".join(map(lambda _:"%d"%_, self.maligned[self.sli])) ) ]
0078 
0079     def label(self, q ):
0080         return "%16x %30s " % ( q, self.ab.histype.label(q) )
0081 
0082     def count_migtab(self, dump=False):
0083         """
0084         Count occurrences of different history migration pairs 
0085         """
0086         ab = self.ab 
0087         mal = ab.maligned
0088         a = ab.a
0089         b = ab.b
0090         d = odict()
0091         for i,m in enumerate(mal):
0092             k = (a.seqhis[m],b.seqhis[m])
0093             if not k in d: d[k]=0 
0094             d[k] += 1  
0095             if dump:
0096                 print( " %4d   :  %s  %s " % (i, self.label(a.seqhis[m]), self.label(b.seqhis[m])) )
0097             pass
0098         pass
0099         return d 
0100 
0101     def migtab(self):
0102         sitems = sorted(self.migs.items(), key=lambda kv:kv[1], reverse=True)
0103         sitems = sitems[self.sli]
0104         tab = "\n".join([" %4d   %s  %s " % ( kv[1], self.label(kv[0][0]), self.label(kv[0][1]) )  for kv in sitems])
0105         return tab
0106 
0107 
0108 
0109 
0110 
0111     def __repr__(self):
0112         return "\n".join( ["ab.mal"] + self.lines() + [repr(self.sli)]
0113                    + lmap(lambda iq:self.ab.recline(iq), enumerate(self.maligned[self.sli])) + ["ab.mal.migtab"]
0114                    + [self.migtab(), "."]
0115                 ) 
0116 
0117 
0118 
0119 class RC(object):
0120     offset = { "rpost_dv":0, "rpol_dv":1 , "ox_dv":2 } 
0121 
0122     def __init__(self, ab):
0123         rc = {}
0124         log.debug("[ rpost_dv ")
0125         rc["rpost_dv"] = ab.rpost_dv.RC
0126         log.debug("]")
0127         
0128         log.debug("[ rpol_dv ")
0129         rc["rpol_dv"] = ab.rpol_dv.RC 
0130         log.debug("]")
0131 
0132         log.debug("[ ox_dv ")
0133         rc["ox_dv"] = ab.ox_dv.RC 
0134         log.debug("]")
0135 
0136 
0137         assert max(rc.values()) <= 1 
0138         irc = rc["rpost_dv"] << self.offset["rpost_dv"] | rc["rpol_dv"] << self.offset["rpol_dv"] | rc["ox_dv"] << self.offset["ox_dv"]
0139         self.rc = irc 
0140 
0141         levels = list(map(lambda _:_.level, list(filter(None, [ab.rpost_dv.maxlevel,ab.ox_dv.maxlevel,ab.ox_dv.maxlevel]))))
0142         level = max(levels) if len(levels) > 0 else None
0143 
0144         if level is None:
0145             level = Level.MISSING  ## artifical setting whilst debugging 
0146         pass 
0147         self.level = level  
0148 
0149     def __repr__(self):
0150         return "RC 0x%.2x" % self.rc
0151 
0152 
0153 
0154 
0155 class AB(object):
0156     """
0157     AB : Event Pair comparison
0158     =============================
0159 
0160     Selection examples::
0161 
0162          ab.sel = ".6ccd"             
0163          ab.sel = "TO BT BT SC .."     # wildcard selection same as above
0164          ab.sel = None                 # back to default no selection
0165 
0166          ab.aselhis = "TO BT BT SA"    # with align-ment enabled 
0167                                        # (used for non-indep photon samples, such as from emitconfig)
0168 
0169     Subsequently check tables with::
0170 
0171          ab.his
0172          ab.flg
0173          ab.mat
0174 
0175     Histo persisting, provides random access to histos for debugging::
0176 
0177          ab.sel = slice(0,1)
0178          ab.qwn = "Z"
0179          ab.irec = 5
0180 
0181          h = ab.h      # qwn and irec used in creation of histogram, which is persisted
0182 
0183     For example, checking a hi chi2 contributor::
0184 
0185         In [12]: h = ab.h
0186         [2016-11-14 12:21:43,098] p12883 {/Users/blyth/opticks/ana/ab.py:473} INFO - AB.rhist qwn T irec 5 
0187         [2016-11-14 12:21:43,098] p12883 {/Users/blyth/opticks/ana/cfh.py:344} INFO - CFH.load from /tmp/blyth/opticks/CFH/concentric/1/TO_BT_BT_BT_BT_AB/5/T 
0188 
0189         In [13]: h.lhabc
0190         Out[13]: 
0191         array([[     2.9649,      6.9934,      0.    ,      0.    ,      0.    ],
0192                [     6.9934,     11.0218,      0.    ,      0.    ,      0.    ],
0193                [    11.0218,     15.0503,      0.    ,      0.    ,      0.    ],
0194                [    15.0503,     19.0787,      0.    ,      0.    ,      0.    ],
0195                [    19.0787,     23.1072,  14159.    ,  13454.    ,     17.9997],
0196                [    23.1072,     27.1356,  14796.    ,  15195.    ,      5.3083],
0197                [    27.1356,     31.164 ,      0.    ,      0.    ,      0.    ],
0198                [    31.164 ,     35.1925,      0.    ,      0.    ,      0.    ],
0199                [    35.1925,     39.2209,      0.    ,      0.    ,      0.    ],
0200                [    39.2209,     43.2494,      0.    ,      0.    ,      0.    ]], dtype=float32)
0201 
0202     """
0203     C2CUT = 30
0204     STREAM = sys.stderr
0205 
0206     @classmethod  
0207     def print_(cls, obj):
0208         print(str(obj), file=cls.STREAM)
0209 
0210     def _get_maligned(self):
0211         return np.where(self.a.seqhis != self.b.seqhis)[0]
0212     maligned = property(_get_maligned)
0213 
0214     def _get_aligned(self):
0215         return np.where(self.a.seqhis == self.b.seqhis)[0]
0216     aligned = property(_get_aligned)
0217     
0218     def _get_misutailed(self):
0219         return np.where(self.a.utail != self.b.utail)[0]
0220     misutailed = property(_get_misutailed)
0221 
0222     def _get_utailed(self):
0223         return np.where(self.a.utail == self.b.utail)[0]
0224     utailed = property(_get_utailed)
0225 
0226 
0227     def recline(self, iq, u=False):
0228         """
0229         :param iq: i,q 2-tuple of i:enumeration index 0,1,2,... and q:photon index
0230         :return line: comparing a and b seqhis labels  
0231         """
0232         i, q = iq
0233         al = self.histype.label(self.a.seqhis[q])
0234         bl = self.histype.label(self.b.seqhis[q])
0235         mk = " " if al == bl else "*"
0236         head = " %6d %6d : %s : %50s %50s " % ( i, q, mk, al, bl )
0237         if u==True: 
0238             u = self.u 
0239             uu = u[q].ravel()        # randoms for photon record_id q
0240             ua = self.a.utail[q]
0241             ub = self.b.utail[q]
0242             wa = np.where( uu == ua )[0][0]
0243             wb = -1 if ub == 0 else np.where( uu == ub )[0][0]
0244             wd = 0 if wb == -1 else wb - wa 
0245             tail = " ua %6.4f ub %6.4f wa %2d wb %2d wd %2d " % ( ua, ub, wa, wb, wd ) 
0246         else:
0247             tail = None
0248         pass
0249         return " ".join(filter(None, [head, tail]))
0250 
0251     def dumpline(self, wh, u=False):
0252         if type(wh) is slice:
0253             start = wh.start if wh.start is not None else 0
0254             stop = wh.stop if wh.stop is not None else 1
0255             step = wh.step if wh.step is not None else 1
0256             wh = range(start,stop, step) 
0257         pass
0258         self.print_( "\n".join( map(lambda iq:self.recline(iq, u=u), enumerate(wh))))
0259 
0260 
0261     def check_utaildebug(self):
0262         """
0263         notes/issues/ts-box-utaildebug-decouple-maligned-from-deviant.rst
0264 
0265         Looking at utail mismatches that do not correspond to seqhis mismatches. 
0266 
0267         Hmm can use this approach to examine the random consumption of each photon 
0268 
0269 
0270         Hmm for analysis of events created with a mask active the q will not 
0271         correspond.
0272         """
0273 
0274         if self.cfm.utaildebug == 0:
0275             log.debug("requires both A and B to have been run with --utaildebug option")
0276             return 
0277         pass 
0278 
0279         mask = self.ok.mask
0280         if not mask is None:
0281             qmask = map(int, mask.split(","))
0282             assert len(qmask) == len(self.a.seqhis)
0283         else:
0284             qmask = None
0285         pass 
0286 
0287         u = self.u 
0288         w = np.where(np.logical_and( self.a.utail != self.b.utail, self.a.seqhis == self.b.seqhis ))[0]
0289         log.debug("utail mismatch but seqhis matched u.shape:%r w.shape: %r " % (u.shape, w.shape) )
0290         for i,q in enumerate(w):
0291             q_ = q if qmask is None else qmask[q]            
0292             uu = u[q_].ravel()        # randoms for photon record_id q 
0293             ua = self.a.utail[q]
0294             ub = self.b.utail[q]
0295             wa = np.where( uu == ua )[0][0]
0296             wb = -1 if ub == 0 else np.where( uu == ub )[0][0]
0297             wd = 0 if wb == -1 else wb - wa 
0298             print(" i %5d q_ %7d ua %10.4f ub %10.4f  wa %3d wb %3d wd %3d : %s  " % (i, q_, ua, ub, wa, wb, wd, self.recline((i,q))  ))
0299         pass
0300 
0301 
0302 
0303     def dump(self):
0304         log.debug("[")
0305         if self.is_comparable: 
0306             if not self.pro.missing: 
0307                 self.print_(self.pro)  
0308             pass
0309             self.print_("#ab.cfm")  
0310             self.print_(self.cfm)  
0311             self.print_("#ab.mal")  
0312             self.print_(self.mal)
0313             self.print_("#ab")  
0314             self.print_(self)
0315             self.print_("#ab.cfm")  
0316             self.print_(self.cfm)  
0317             self.print_("#ab.brief")  
0318             self.print_(self.brief)
0319             self.print_("#ab.rpost_dv")  
0320             self.print_(self.rpost_dv)
0321             self.print_("#ab.rpol_dv")  
0322             self.print_(self.rpol_dv)
0323             self.print_("#ab.ox_dv")  
0324             self.print_(self.ox_dv)
0325             self.print_("#ab.brief")  
0326             self.print_(self.brief)
0327             self.print_("#ab.rc_")  
0328             self.print_(self.rc_)
0329             self.print_("#ab.cfm")  
0330             self.print_(self.cfm)  
0331         pass
0332         log.debug("]")
0333 
0334 
0335     def __init__(self, ok, overridetag="0"):
0336         """
0337         :param ok: arguments instance from opticks_main
0338         :param overridetag: when not "0" overrides the tags of events to be loaded NOT YET IMPLEMENTED
0339         """
0340         log.debug("[")
0341         self.ok = ok
0342         self.overridetag = overridetag
0343         self.histype = HisType()
0344         self.tabs = []
0345         self.dvtabs = []
0346         self.stream = sys.stderr
0347         self._sel = None
0348         self.dshape = None
0349 
0350         self.load()
0351         self.load_u()
0352 
0353         self.is_comparable = self.valid and not self.a.ph.missing and not self.b.ph.missing 
0354         log.debug("[ABProfile")
0355         self.pro = ABProfile(self.a.tagdir, self.b.tagdir)
0356         log.debug("]ABProfile")
0357 
0358         if self.is_comparable and ok.compare: 
0359             self.cfm = self.compare_meta()
0360             self.mal = self.check_alignment()
0361             self.compare_shapes()
0362             self.compare_domains()
0363             self.compare()
0364             self.init_point()
0365 
0366             log.debug("ABSmry.Make self.level : %s " % self.level)  
0367 
0368             self.smry = ABSmry.Make(self)
0369             self.smry.save()  
0370         pass 
0371         log.debug("]")
0372 
0373     def load(self):
0374         """
0375         It takes aound 4s on Precision Workstation to load 1M full AB evt pair. 
0376         So avoid needing to duplicate that.
0377         """
0378         pyver = "{major}.{minor}.{micro}".format(major=sys.version_info.major,minor=sys.version_info.minor,micro=sys.version_info.micro)  
0379         log.debug("[ smry:%s np.__version__ %s py%s" % (self.ok.smry, np.__version__, pyver) )
0380         args = self.ok
0381 
0382         if self.overridetag != "0":
0383             atag = "%s" % self.overridetag
0384             btag = "-%s" % self.overridetag
0385         elif args.utag is None:
0386             assert len(args.utags) == 2, ( "expecting 2 utags ", args.utags )
0387             atag = "%s" % args.utags[0]
0388             btag = "%s" % args.utags[1]
0389         else:
0390             atag = "%s" % args.utag
0391             btag = "-%s" % args.utag
0392         pass
0393 
0394 
0395         log.debug( "overridetag %s  atag %s btag %s  " % (self.overridetag, atag, btag) )
0396 
0397         a = Evt(tag=atag, src=args.src, det=args.det, pfx=args.pfx, args=args, nom="A" )
0398         b = Evt(tag=btag, src=args.src, det=args.det, pfx=args.pfx, args=args, nom="B" )
0399 
0400         pass
0401         self.a = a
0402         self.b = b 
0403 
0404         valid = a.valid and b.valid 
0405         if not valid:
0406             log.fatal("invalid loads : a.valid:%s b.valid:%s " % (a.valid, b.valid))
0407         pass
0408         self.valid = valid 
0409 
0410         self.align = None
0411         self._dirty = False
0412 
0413         ## property setters
0414         if valid:
0415             self.sel = None
0416             self.irec = 0
0417             self.qwn = "X"
0418         pass
0419         log.debug("] ")
0420 
0421 
0422     def _get_brief(self):
0423         abn = "AB(%s,%s,%s)  %s %s    %s " % (self.ok.tag, self.ok.src, self.ok.det, self.sel, self.irec, self.dshape )
0424         return abn
0425     brief = property(_get_brief)
0426 
0427     def __repr__(self):
0428         abn = self.brief
0429         abr = "A %s " % self.a.brief 
0430         bbr = "B %s " % self.b.brief 
0431 
0432         amd = ",".join(self.a.metadata.csgbnd)
0433         acsgp = self.a.metadata.TestCSGPath
0434 
0435         if self.b.valid:
0436             bmd = ",".join(self.b.metadata.csgbnd)
0437             bcsgp = self.b.metadata.TestCSGPath
0438             assert amd == bmd
0439             assert acsgp == bcsgp
0440         else:
0441             pass
0442         pass
0443         return "\n".join(filter(None,["ab", abn, abr,bbr, amd, acsgp,"." ]))
0444 
0445     def __str__(self):
0446         lmx = self.ok.lmx
0447         if len(self.his.lines) > lmx:
0448             self.his.sli = slice(0,lmx)
0449         if len(self.mat.lines) > lmx:
0450             self.mat.sli = slice(0,lmx)
0451         pass
0452         return "\n".join([repr(self),"#ab.ahis",repr(self.ahis),"#ab.flg",repr(self.flg),"#ab.mat",repr(self.mat)])
0453 
0454 
0455     def load_u(self):
0456         """
0457         This array of randoms is used by below check_utaildebug.
0458 
0459         Default u array is (100k,16,16) containing doubles. 
0460         To generate an extra large array (1M,16,16)::
0461 
0462             TRngBuf_NI=1000000 TRngBufTest 
0463 
0464         Which creates a 2GB file::  
0465 
0466             In [6]: 16*16*1000000*8/1e9
0467             Out[6]: 2.048
0468 
0469             [blyth@localhost sysrap]$ ls -l "$TMP/TRngBufTest_0_1000000.npy"
0470             -rw-rw-r--. 1 blyth blyth 2048000096 Jul 28 12:02 /home/blyth/local/opticks/tmp/TRngBufTest_0_1000000.npy
0471 
0472         """
0473         log.debug("[")
0474         upath0 = "$TMP/TRngBufTest_0.npy"
0475         upath1 = "$TMP/TRngBufTest_0_1000000.npy"
0476 
0477         if os.path.exists(os.path.expandvars(upath1)):
0478             upath = upath1 
0479             log.debug("using the extra large random array %s " % upath ) 
0480         else: 
0481             upath = upath0
0482         pass
0483         u,u_paths = np_load(upath)
0484         log.info("\n".join(u_paths))
0485 
0486         u = None if u is None else u.astype(np.float32)
0487         self.u = u 
0488         log.debug("]")
0489 
0490 
0491     def compare_domains(self):
0492         assert np.all( self.a.fdom == self.b.fdom )
0493         self.fdom = self.a.fdom
0494 
0495         isli = slice(2,None)  ## permit rngmax difference 
0496         assert np.all( self.a.idom[isli] == self.b.idom[isli] )
0497         self.idom = self.a.idom
0498 
0499     def compare_shapes(self):
0500         assert self.a.dshape == self.b.dshape, (self.a.dshape, self.b.dshape)   
0501         self.dshape = self.a.dshape  
0502 
0503         # description of the load_slice
0504         assert self.a.dsli == self.b.dsli, ( self.a.dsli, self.b.dsli )
0505         self.dsli = self.a.dsli 
0506 
0507 
0508  
0509     def compare_meta(self):
0510         cfm = CompareMetadata(self.a.metadata, self.b.metadata)
0511 
0512         non_aligned_skips = "SC AB RE" 
0513         #non_aligned_skips = "RE" 
0514 
0515         self.dvskips = "" if cfm.align == 1 else non_aligned_skips   
0516 
0517         return cfm 
0518 
0519     def check_alignment(self):
0520         log.debug("[")
0521         mal = Maligned(self)
0522         log.debug("]")
0523         return mal 
0524         
0525     def compare(self):
0526         """
0527         * Taking around 5s for 1M events, all in RC 
0528         * prohis and promat are progressively masked tables, not enabled by default
0529         """
0530         log.debug("[")
0531 
0532         self.ahis = self._get_cf("all_seqhis_ana", "ab.ahis")
0533         self.amat = self._get_cf("all_seqmat_ana", "ab.amat")
0534 
0535         if self.ok.prohis:self.prohis()
0536         if self.ok.promat:self.promat()
0537 
0538         self.rc_ = RC(self) 
0539 
0540         log.debug("]")
0541 
0542     def _get_RC(self):
0543         return self.rc_.rc if self.is_comparable else 255
0544     RC = property(_get_RC) 
0545 
0546     def _get_level(self):
0547         return self.rc_.level if self.is_comparable else 255
0548     level = property(_get_level) 
0549 
0550 
0551     numPhotons = property(lambda self:self.cfm.numPhotons) 
0552 
0553 
0554     def init_point(self):
0555         log.debug("[")
0556         self.point = self.make_point()
0557         log.debug("]")
0558 
0559     def point_dtype(self):
0560         dtype=[
0561                 ('irl', int), 
0562                 ('isel', int), 
0563                 ('irec', int), 
0564                 ('nrec', int), 
0565                 ('reclab', "|S64")
0566               ]
0567         return dtype
0568 
0569     def _get_recpoint(self):
0570         """
0571         Returns None when single line selection is not active
0572         otherwise returns selected recpoint
0573 
0574         ::
0575 
0576             In [2]: ab.sel = "TO BT BT RE BT BT [SA]"
0577 
0578             In [3]: ab.recpoint
0579             Out[3]: (85, 12, 6, 7, 'TO BT BT RE BT BT [SA]')
0580 
0581             In [4]: ab.recpoint.isel
0582             Out[4]: 12
0583 
0584             In [5]: ab.recpoint.nrec
0585             Out[5]: 7
0586 
0587             In [6]: ab.recpoint.irec
0588             Out[6]: 6
0589 
0590         """
0591         rl = self.reclab
0592         if rl is None:
0593             return None
0594         pass 
0595         rp = self.point[self.point.reclab == rl]
0596         assert len(rp) == 1, rp
0597         return rp[0]
0598     recpoint = property(_get_recpoint)
0599 
0600     def make_point(self):
0601         """
0602         :return point: recarray for holding point level metadata
0603 
0604         ::
0605 
0606             In [5]: print "\n".join(map(repr, ab.point[:8]))
0607             (0, 0, 0, '[TO] BT BT BT BT SA')
0608             (1, 0, 1, 'TO [BT] BT BT BT SA')
0609             (2, 0, 2, 'TO BT [BT] BT BT SA')
0610             (3, 0, 3, 'TO BT BT [BT] BT SA')
0611             (4, 0, 4, 'TO BT BT BT [BT] SA')
0612             (5, 0, 5, 'TO BT BT BT BT [SA]')
0613             (6, 1, 0, '[TO] AB')
0614             (7, 1, 1, 'TO [AB]')
0615 
0616             In [6]: print "\n".join(map(repr, ab.point[-8:]))
0617             (64257, 4847, 8, 'TO RE RE RE RE BT BT BT [SC] BR BR BR BR BR BR BR')
0618             (64258, 4847, 9, 'TO RE RE RE RE BT BT BT SC [BR] BR BR BR BR BR BR')
0619             (64259, 4847, 10, 'TO RE RE RE RE BT BT BT SC BR [BR] BR BR BR BR BR')
0620             (64260, 4847, 11, 'TO RE RE RE RE BT BT BT SC BR BR [BR] BR BR BR BR')
0621             (64261, 4847, 12, 'TO RE RE RE RE BT BT BT SC BR BR BR [BR] BR BR BR')
0622             (64262, 4847, 13, 'TO RE RE RE RE BT BT BT SC BR BR BR BR [BR] BR BR')
0623             (64263, 4847, 14, 'TO RE RE RE RE BT BT BT SC BR BR BR BR BR [BR] BR')
0624             (64264, 4847, 15, 'TO RE RE RE RE BT BT BT SC BR BR BR BR BR BR [BR]')
0625 
0626         """ 
0627         rls = self.reclabs(0,None)
0628         nrls = len(rls)
0629 
0630         point = np.recarray((nrls,), dtype=self.point_dtype())
0631         point.reclab = rls
0632         point.irl = np.arange(0, nrls)
0633 
0634         offset = 0 
0635         for isel,clab in enumerate(self.clabels[0:None]):
0636             sls = Ctx.reclabs_(clab)
0637             nsls = len(list(sls))
0638             point.isel[offset:offset+nsls] = np.repeat( isel, nsls)
0639             point.irec[offset:offset+nsls] = np.arange( 0   , nsls)
0640             point.nrec[offset:offset+nsls] = np.repeat( nsls, nsls)
0641             offset += nsls 
0642         pass
0643         return point
0644 
0645 
0646 
0647     def prohis(self, rng=range(1,8)):
0648         log.debug("[")
0649         for imsk in rng:
0650             setattr(self, "his_%d" % imsk, self.cf("seqhis_ana_%d" % imsk)) 
0651         pass
0652         log.debug("]")
0653     def promat(self, rng=range(1,8)):
0654         log.debug("[")
0655         for imsk in rng:
0656             setattr(self, "mat_%d" % imsk, self.cf("seqmat_ana_%d" % imsk)) 
0657         pass
0658         log.debug("]")
0659 
0660     def tabname(self, ana):
0661         if ana.endswith("_dv"):
0662             tn = ana + "tab"
0663         else:
0664             tn = ana.replace("_ana", "_tab")
0665         pass
0666         return tn
0667 
0668     def _make_cf(self, ana, shortname):
0669         """
0670         all_ tables have no selection applied so they are not dirtied by changing selection
0671         """
0672         ordering = self.ok.cfordering 
0673         assert ordering in self.ok.allowed_cfordering 
0674 
0675         c_tab = Evt.compare_ana( self.a, self.b, ana, lmx=self.ok.lmx, cmx=self.ok.cmx, c2max=None, cf=True, ordering=ordering, shortname=shortname )
0676         if not ana[0:3] == "all":
0677             self.tabs.append(c_tab)
0678         pass 
0679         tabname = self.tabname(ana)
0680         setattr(self, tabname, c_tab)
0681         return c_tab 
0682 
0683 
0684 
0685     
0686 
0687 
0688 
0689     def _make_dv(self, ana):
0690         """
0691         :param ana: ox_dv, rpost_dv, rpol_dv
0692         """  
0693         log.debug("[ %s " % ana )
0694         we = self.warn_empty 
0695         self.warn_empty = False
0696         seqtab = self.ahis
0697 
0698         #dv_tab = DvTab(ana, seqtab, self, skips=self.dvskips, selbase="ALIGN" ) 
0699 
0700         use_utaildebug = self.cfm.utaildebug   
0701         log.debug("_make_dv ana %s use_utaildebug %s " % (ana, use_utaildebug)) 
0702         dv_tab = QDVTab(ana, self, use_utaildebug=use_utaildebug ) 
0703         self.dvtabs.append(dv_tab)
0704 
0705         self.warn_empty = we
0706         log.debug("] %s " % ana )
0707         return dv_tab 
0708 
0709     def _get_dv(self, ana):
0710         log.debug("[ %s " % ana )
0711         tabname = self.tabname(ana)
0712         dv_tab = getattr(self, tabname, None)
0713         if dv_tab is None:
0714             dv_tab = self._make_dv(ana) 
0715         elif dv_tab.dirty:
0716             dv_tab = self._make_dv(ana) 
0717         else:
0718             pass 
0719         pass
0720         setattr(self, tabname, dv_tab) 
0721         log.debug("] %s " % ana )
0722         return dv_tab
0723 
0724 
0725     def _get_ox_dv(self):
0726         return self._get_dv("ox_dv")
0727     ox_dv = property(_get_ox_dv)
0728 
0729     def _get_rpost_dv(self):
0730         return self._get_dv("rpost_dv")
0731     rpost_dv = property(_get_rpost_dv)
0732 
0733     def _get_rpol_dv(self):
0734         return self._get_dv("rpol_dv")
0735     rpol_dv = property(_get_rpol_dv)
0736 
0737 
0738     def _set_dirty(self, dirty):
0739         for tab in self.tabs:
0740             tab.dirty = dirty
0741         pass
0742     def _get_dirty(self):
0743         dtabs = filter(lambda tab:tab.dirty, self.tabs)
0744         return len(dtabs) > 0 
0745     dirty = property(_get_dirty, _set_dirty)
0746 
0747 
0748 
0749 
0750     mxs = property(lambda self:dict(map(lambda _:[_.name,_.maxdvmax], self.dvtabs )))
0751     mxs_max = property(lambda self:max(self.mxs.values()))
0752 
0753     rmxs = property(lambda self:dict(map(lambda _:[_.name,_.maxdvmax], filter(lambda _:_.name[0] == 'r',self.dvtabs ))))
0754     rmxs_max = property(lambda self:max(self.rmxs.values()))
0755 
0756     pmxs = property(lambda self:dict(map(lambda _:[_.name,_.maxdvmax], filter(lambda _:_.name[0] != 'r',self.dvtabs ))))
0757     pmxs_max = property(lambda self:max(self.pmxs.values()))
0758 
0759 
0760 
0761     fds = property(lambda self:dict(map(lambda _:[_.name,_.fdiscmax], self.dvtabs )))
0762     fds_max = property(lambda self:max(self.fds.values()))
0763 
0764 
0765     c2p = property(lambda self:dict(map(lambda _:[_.title,_.c2p], self.tabs )))
0766     c2p_max = property(lambda self:max(self.c2p.values()))
0767 
0768 
0769     def _get_cf(self, ana, shortname):
0770         """
0771         Changing *sel* property invokes _set_sel 
0772         resulting in a change to the SeqAna in the A B Evt,
0773         thus all AB comparison tables are marked dirty, causing 
0774         them to be recreated at next access.
0775         """
0776         log.debug("[ %s " % shortname ) 
0777         tabname = self.tabname(ana)
0778         tab = getattr(self, tabname, None)
0779         if tab is None:
0780             tab = self._make_cf(ana, shortname) 
0781         elif tab.dirty:
0782             tab = self._make_cf(ana, shortname) 
0783         else:
0784             pass 
0785         log.debug("] %s " % shortname ) 
0786         return tab
0787 
0788     def _get_his(self):
0789         return self._get_cf("seqhis_ana", "ab.his")
0790     def _get_mat(self):
0791         return self._get_cf("seqmat_ana", "ab.mat")
0792     def _get_flg(self):
0793         return self._get_cf("pflags_ana", "ab.flg")
0794 
0795     his = property(_get_his)
0796     mat = property(_get_mat)
0797     flg = property(_get_flg)
0798 
0799     # high level *sel* selection only, for lower level *psel* selections 
0800     # apply individually to evt a and b 
0801 
0802     def _get_sel(self):
0803         return self._sel
0804     def _set_sel(self, sel, nom="sel"):
0805         """
0806         NB slice selection at AB level must be
0807         converted into seq string selection at evt level
0808         as the labels can diverge, expecially out in the tail
0809 
0810         slice selection forces into seqhis selection
0811 
0812 
0813         Have observed that slice selection only works for 
0814         single element slices, eg::
0815 
0816             ab.sel = slice(10,11)
0817 
0818         ## TODO: if that is the intent, might as well allow: ab.sel = 10 
0819 
0820         ::
0821 
0822             ab.sel = "[TO] BT BT BT BT SA" 
0823             hh = ab.hh
0824 
0825         """
0826         log.debug("[ %s " % repr(sel))
0827 
0828         if type(sel) is slice:
0829             clabels = self.clabels
0830             seqs = clabels[sel]  
0831             assert len(seqs) == 1, seqs
0832             log.debug("AB._set_set convert slice selection %r into common label seq selection  %s " % (sel, seqs[0]))
0833             sel = seqs[0]
0834             self.flv = "seqhis"
0835         pass
0836 
0837         if nom == "sel":
0838             self.align = None
0839             self.a.sel = sel
0840             self.b.sel = sel
0841         elif nom == "selhis":
0842             self.align = None
0843             self.a.selhis = sel
0844             self.b.selhis = sel
0845         elif nom == "aselhis":
0846             self.align = "seqhis"
0847             self.a.selhis = sel
0848             self.b.selhis = sel
0849         elif nom == "selmat":
0850             self.align = None
0851             self.a.selmat = sel
0852             self.b.selmat = sel
0853         elif nom == "selflg":
0854             self.align = None
0855             self.a.selflg = sel
0856             self.b.selflg = sel
0857         else:
0858             assert 0, nom 
0859         pass
0860 
0861         self._sel = sel 
0862         self.dirty = True  
0863         log.debug("] %s " % repr(sel))
0864 
0865     sel = property(_get_sel, _set_sel)
0866     
0867     def _set_selmat(self, sel):
0868         self._set_sel( sel, nom="selmat")
0869     def _set_selhis(self, sel):
0870         self._set_sel( sel, nom="selhis")
0871     def _set_aselhis(self, sel):
0872         self._set_sel( sel, nom="aselhis")
0873     def _set_selflg(self, sel):
0874         self._set_sel( sel, nom="selflg")
0875 
0876     selmat = property(_get_sel, _set_selmat)
0877     selhis = property(_get_sel, _set_selhis)
0878     selflg = property(_get_sel, _set_selflg)
0879     aselhis = property(_get_sel, _set_aselhis)
0880 
0881 
0882 
0883 
0884     def _get_align(self):
0885         return self._align 
0886     def _set_align(self, align):
0887         """
0888         CAUTION: currently need to set sel after align for it to have an effect
0889                  unless use aselhis
0890         """
0891         if align is None:
0892             _align = None
0893         elif type(align) is str:
0894             if align == "seqhis":
0895                  _align = self.a.seqhis == self.b.seqhis 
0896             else:
0897                  assert False, (align, "not implemented" ) 
0898         else:
0899             _align = align
0900         pass
0901 
0902         self._align = _align
0903         self.a.align = _align
0904         self.b.align = _align
0905         self.dirty = True  
0906 
0907     align = property(_get_align, _set_align)    
0908 
0909 
0910 
0911     def _set_flv(self, flv):
0912         self.a.flv = flv
0913         self.b.flv = flv
0914     def _get_flv(self):
0915         a_flv = self.a.flv    
0916         b_flv = self.b.flv    
0917         assert a_flv == b_flv
0918         return a_flv
0919     flv = property(_get_flv, _set_flv)
0920 
0921     def _set_irec(self, irec):
0922         self.a.irec = irec 
0923         self.b.irec = irec 
0924     def _get_irec(self):
0925         a_irec = self.a.irec
0926         b_irec = self.b.irec
0927         assert a_irec == b_irec
0928         return a_irec
0929     irec = property(_get_irec, _set_irec)
0930 
0931     def _get_reclab(self):
0932         a_reclab = self.a.reclab
0933         b_reclab = self.b.reclab
0934         assert a_reclab == b_reclab
0935         return a_reclab
0936     reclab = property(_get_reclab)
0937 
0938     def _get_label0(self):
0939         a_label0 = self.a.label0
0940         b_label0 = self.b.label0
0941         assert a_label0 == b_label0
0942         return a_label0
0943     label0 = property(_get_label0)
0944 
0945     def _get_seq0(self):
0946         lab0 = self.label0
0947         if lab0 is None:
0948             return None
0949         return lab0.replace(" ","_")
0950     seq0 = property(_get_seq0)
0951 
0952     def count(self, line=0, col=1):
0953         """standard selects have only one sequence line""" 
0954         cu = self.seq.cu 
0955         if cu is None:
0956             return None
0957         pass
0958         return cu[line,col]
0959 
0960     def _get_aseq(self):
0961         """
0962         aseq is not changed by the current selection 
0963         """
0964         flv = self.flv
0965         if flv == "seqhis":
0966             return self.ahis
0967         elif flv == "seqmat":
0968             return self.amat
0969         else:
0970             pass
0971         return None
0972     aseq = property(_get_aseq)
0973 
0974     def _get_seq(self):
0975         """
0976         seq is changed by current selection
0977         """
0978         flv = self.flv
0979         if flv == "seqhis":
0980             return self.his
0981         elif flv == "seqmat":
0982             return self.mat
0983         else:
0984             pass
0985         return None
0986     seq = property(_get_seq)
0987 
0988     def _get_achi2(self):
0989         aseq = self.aseq 
0990         assert not aseq is None
0991         return aseq.c2
0992     achi2 = property(_get_achi2)
0993 
0994     def a_count(self, line=0):
0995         return self.count(line,1)
0996 
0997     def b_count(self, line=0):
0998         return self.count(line,2)
0999 
1000     def ab_count(self, line=0):
1001         ac = self.a_count(line)
1002         bc = self.b_count(line)
1003         return "%d/%d" % (ac,bc)
1004 
1005     def _get_suptitle(self):
1006         abc = self.ab_count()
1007         title = "%s/%s/%s : %s  :  %s " % (self.ok.tag, self.ok.det, self.ok.src, abc, self.reclab )
1008         return title
1009     suptitle = property(_get_suptitle)
1010 
1011     def _get_ctx(self):
1012         """
1013         hmm at some point seq0 lost its underscores
1014         """
1015         return Ctx({'det':self.ok.det, 'tag':self.ok.tag, 'src':self.ok.src, 'seq0':self.label0, 'lab':self.reclab, 'irec':self.irec, 'qwn':self.qwn })
1016     ctx = property(_get_ctx)
1017 
1018     pctx = property(lambda self:self.ctx.pctx)
1019     qctx = property(lambda self:self.ctx.qctx)
1020 
1021 
1022     def _get_nrec(self):
1023         """
1024         :return: number of record points, when a single seq selection is active
1025         """ 
1026         a_nr = self.a.nrec
1027         b_nr = self.b.nrec
1028         assert a_nr == b_nr, (a_nr, b_nr, "A and B should have same nrec ?")
1029         if a_nr == b_nr and a_nr != -1:
1030             nr = a_nr
1031         else:
1032             nr = -1
1033         pass
1034         log.debug(" a_nr:%d b_nr:%d nr:%d " % (a_nr, b_nr, nr) )
1035         return nr 
1036     nrec = property(_get_nrec)
1037 
1038 
1039     def iflg(self, flg):
1040         """
1041         :param flg: eg "SC"
1042         :return: number of record points, when a single seq selection is active
1043         """ 
1044         a_ifl = self.a.iflg(flg)
1045         b_ifl = self.b.iflg(flg)
1046         assert a_ifl == b_ifl, (a_ifl, b_ifl, "A and B should have same iflg ?")
1047         if a_ifl == b_ifl and a_ifl != None:
1048             ifl = a_ifl
1049         else:
1050             ifl = None
1051         pass
1052         log.debug(" a_ifl:%s b_ifl:%s ifl:%s " % (a_ifl, b_ifl, ifl) )
1053         return ifl 
1054 
1055     def nrecs(self, start=0, stop=None, step=1):
1056         """
1057         """
1058         sli = slice(start, stop, step)
1059         labels = self.clabels[sli] 
1060         nrs = np.zeros(len(labels), dtype=np.int32) 
1061         for ilab, lab in enumerate(labels):
1062             nrs[ilab] = len(lab.split())
1063         pass
1064         return nrs
1065 
1066     def stats_dtype(self, qwns=None):
1067         if qwns is None:
1068             qwns = self.ok.qwns
1069         pass
1070         dtype = [(i,np.int32) for i in "iv is na nb".split()] 
1071         dtype += [(s,"|S64") for s in "reclab".split()]
1072         dtype += [(q,np.float32) for q in list(qwns.replace(",",""))]
1073         dtype += [(q,np.float32) for q in "seqc2 distc2".split()]
1074         return dtype
1075 
1076 
1077     def reclabs(self, start=0, stop=5):
1078         """
1079         :param start: common label slice starting index
1080         :param stop: common label slice stop index, can be None
1081         :return rls: list of reclabels
1082 
1083         Full reclabs is very large list::
1084 
1085             In [5]: rl = ab.reclabs(0,None)
1086 
1087             In [6]: len(rl)
1088             Out[6]: 64265
1089 
1090             In [7]: len(ab.clabels)
1091             Out[7]: 4848
1092 
1093             In [8]: 64265./4848.        ## average seq0 length is 13 points 
1094             Out[8]: 13.255981848184819
1095 
1096             In [9]: rl[:5]
1097             Out[9]: 
1098             ['[TO] BT BT BT BT SA',
1099              'TO [BT] BT BT BT SA',
1100              'TO BT [BT] BT BT SA',
1101              'TO BT BT [BT] BT SA',
1102              'TO BT BT BT [BT] SA']
1103 
1104         """
1105         l = []
1106         for clab in self.clabels[start:stop]:
1107             l.extend(Ctx.reclabs_(clab))
1108         pass
1109         return l
1110 
1111 
1112     def stats_(self, reclabs, qwns=None, rehist=False, c2shape=True):
1113         """
1114         ::
1115 
1116             st = ab.stats_(ab.point.reclab[:10])
1117 
1118         """
1119         if qwns is None:
1120             qwns = self.ok.qwns
1121         pass
1122 
1123         nrl = len(reclabs)
1124         stat = np.recarray((nrl,), dtype=self.stats_dtype(qwns))
1125 
1126         for ival, rl in enumerate(reclabs):
1127 
1128             self.sel = rl     ## setting single line selection
1129 
1130             rp = self.recpoint
1131 
1132             isel = rp.isel
1133 
1134             # NB OrderedDict setting order must match dtype name order
1135             od = odict()
1136             od["iv"] = rp.irl
1137             od["is"] = isel
1138             od["na"] = self.aseq.cu[isel,1] 
1139             od["nb"] = self.aseq.cu[isel,2]
1140             od["reclab"] = rl
1141 
1142             hh = self.rhist(rehist=rehist, c2shape=c2shape)
1143             qd = odict(map(lambda h:(h.qwn,h.c2p), hh))
1144             od.update(qd)
1145 
1146             od["seqc2"] = self.seq.c2[0]
1147             od["distc2"] = CFH.c2per_(hh)
1148 
1149             stat[ival] = tuple(od.values())
1150         pass
1151 
1152         st = ABStat(stat) 
1153         st.save()
1154         return st 
1155 
1156  
1157     def stats(self, start=0, stop=5, qwns=None, rehist=False, c2shape=True):
1158         """
1159         slice selection in AB has to be converted into common seq selection 
1160         in the evt 
1161         """
1162         if qwns is None:
1163             qwns = self.ok.qwns
1164         pass
1165         qwns = qwns.replace(",","")
1166 
1167         dtype = self.stats_dtype(qwns)
1168 
1169         nrs = self.nrecs(start, stop)
1170         trs = nrs.sum()
1171 
1172         log.debug("AB.stats : %s :  trs %d nrs %s " % ( qwns, trs, repr(nrs)) )
1173         
1174         stat = np.recarray((trs,), dtype=dtype)
1175         ival = 0
1176         for i,isel in enumerate(range(start, stop)):
1177 
1178             self.sel = slice(isel, isel+1)    ## changing single seq line selection 
1179 
1180             nr = self.nrec
1181             assert nrs[i] == nr, (i, nrs[i], nr )  
1182 
1183             for irec in range(nr):
1184 
1185                 self.irec = irec 
1186 
1187                 self.qwn = qwns   # for collective qctx 
1188 
1189                 qctx = self.qctx
1190 
1191                 key = self.suptitle
1192 
1193                 log.debug("stats irec %d nrec %d ival %d key %s qctx %s " % (irec, nr, ival, key, qctx))
1194 
1195 
1196                 # NB OrderedDict setting order must match dtype name order
1197                 od = odict()
1198                 od["iv"] = ival
1199                 od["is"] = isel
1200                 od["na"] = self.a_count(0)    # single line selection so line=0
1201                 od["nb"] = self.b_count(0)    # single line selection so line=0
1202                 od["reclab"] = self.reclab 
1203 
1204                 hh = self.rhist(qwns, irec, rehist=rehist, c2shape=c2shape)
1205                 qd = odict(map(lambda h:(h.qwn,h.c2p), hh))
1206                 od.update(qd)
1207 
1208                 od["seqc2"] = self.seq.c2[0]
1209                 od["distc2"] = CFH.c2per_(hh)
1210 
1211                 stat[ival] = tuple(od.values())
1212                 ival += 1
1213             pass
1214         pass
1215         log.debug("AB.stats histogramming done")
1216         assert ival == trs, (ival, trs )
1217 
1218         st = ABStat(self.ok, stat) 
1219         st.save()
1220         return st 
1221 
1222     def totrec(self, start=0, stop=None, step=1):
1223         """
1224         :param sli:
1225 
1226         ::
1227 
1228             In [2]: ab.totrec()    # union of labels brings in a lot more of them
1229             Out[2]: 64265
1230 
1231             In [3]: ab.a.totrec()
1232             Out[3]: 43177
1233 
1234             In [4]: ab.b.totrec()
1235             Out[4]: 43085
1236 
1237         """
1238         nrs = self.nrecs(start, stop, step)
1239         return int(nrs.sum())
1240 
1241     def _get_clabels(self):
1242         """
1243         :return clabels: all labels of current flv 
1244         """
1245         flv = self.flv
1246         if flv == "seqhis":
1247             clabels = self.ahis.labels
1248         elif flv == "seqmat":
1249             clabels = self.amat.labels
1250         else:
1251             clabels = []
1252         pass
1253         return clabels
1254     clabels = property(_get_clabels)
1255 
1256     def _get_rposta_dv(self):
1257         """
1258         absolute a.rposta - b.rposta covering entire record array 
1259         """
1260         return np.abs( self.a.rposta - self.b.rposta ) 
1261     rposta_dv = property(_get_rposta_dv) 
1262 
1263     def rpost(self):
1264         """
1265         Sliced decompression within a selection 
1266         works via the rx replacement with rx[psel] 
1267         done in Evt.init_selection.
1268         """
1269         self.checkrec()
1270         av = self.a.rpost()
1271         bv = self.b.rpost()
1272         return av, bv
1273 
1274     def rpost_dv_max(self):
1275         """
1276         :return max a-b item deviations: 
1277 
1278         The np.amax with axis=(1,2) tuple of ints argument 
1279         acts to collapse those dimensions in the aggregation
1280         """ 
1281         av = self.a.rpost()
1282         bv = self.b.rpost()
1283         dv = np.abs( av - bv )
1284         return dv.max(axis=(1,2)) 
1285          
1286     def rpost_dv_where(self, cut):
1287         """
1288         :return photon indices with item deviations exceeding the cut: 
1289         """
1290         av = self.a.rpost()
1291         bv = self.b.rpost()
1292         dv = np.abs( av - bv )
1293         return self.a.where[np.where(dv.max(axis=(1,2)) > cut) ]  
1294 
1295 
1296     def rpol_dv_max(self):
1297         """
1298         :return max a-b item deviations: 
1299         """ 
1300         av = self.a.rpol()
1301         bv = self.b.rpol()
1302         dv = np.abs( av - bv )
1303         return dv.max(axis=(1,2)) 
1304 
1305 
1306     def rpol_dv_where_(self, cut):
1307         """
1308         :return photon indices with item deviations exceeding the cut: 
1309         """
1310         av = self.a.rpol()
1311         bv = self.b.rpol()
1312         dv = np.abs( av - bv )
1313         return np.where(dv.max(axis=(1,2)) > cut)   
1314 
1315     def rpol_dv_where(self, cut):
1316         """
1317         :return photon indices with item deviations exceeding the cut: 
1318         """
1319         av = self.a.rpol()
1320         bv = self.b.rpol()
1321         dv = np.abs( av - bv )
1322         return self.a.where[np.where(dv.max(axis=(1,2)) > cut) ]  
1323 
1324 
1325     def _set_warn_empty(self, we):
1326         self.a.warn_empty = we
1327         self.b.warn_empty = we
1328     def _get_warn_empty(self):
1329         a_we = self.a.warn_empty
1330         b_we = self.b.warn_empty
1331         assert a_we == b_we
1332         return a_we
1333     warn_empty = property(_get_warn_empty, _set_warn_empty) 
1334 
1335 
1336     def checkrec(self,fr=0,to=0):
1337         nr = self.nrec
1338         if nr > -1 and fr < nr and to < nr:
1339             return True 
1340         pass
1341         log.fatal("checkrec requires a single label selection nr %d fr %d to %d" % (nr,fr,to))
1342         return False
1343 
1344     def rdir(self, fr=0, to=1):
1345         if not self.checkrec(fr,to):
1346             return None
1347         aval = self.a.rdir(fr,to)
1348         bval = self.b.rdir(fr,to)
1349         return aval, bval
1350 
1351     def rpol_(self, fr):
1352         if not self.checkrec(fr):
1353             return None
1354         aval = self.a.rpol_(fr)
1355         bval = self.b.rpol_(fr)
1356         return aval, bval
1357 
1358     def rpol(self):
1359         if not self.checkrec():
1360             return None
1361         aval = self.a.rpol()
1362         bval = self.b.rpol()
1363         return aval, bval
1364 
1365     def rw(self):
1366         if not self.checkrec():
1367             return None
1368         aval = self.a.rw()
1369         bval = self.b.rw()
1370         return aval, bval
1371 
1372 
1373     @classmethod
1374     def rrandhist(cls):
1375         bn = np.linspace(-4,4,200)
1376         av = np.random.standard_normal(8000)
1377         bv = np.random.standard_normal(8000)
1378         la = ["A rand", "B rand"]
1379 
1380         ctx = {}
1381         ctx["det"] = "dummy"
1382         ctx["tag"] = "1"
1383         ctx["seq"] = "dummy"
1384         ctx["qwn"] = "U"
1385         ctx["irec"] = "0"   
1386 
1387         cfh = CFH(ctx)
1388         cfh(bn,av,bv,la,c2cut=cls.C2CUT)
1389         return cfh
1390  
1391     def _set_qwn(self, qwn):
1392         self._qwn = qwn 
1393     def _get_qwn(self):
1394         return self._qwn
1395     qwn = property(_get_qwn, _set_qwn)
1396 
1397     def _get_h(self):
1398         hh = self.rhist( self.qwn, self.irec)
1399         assert len(hh) == 1
1400         return hh[0]        
1401     h = property(_get_h)
1402 
1403     def _get_hh(self):
1404         hh = self.rhist()
1405         return hh
1406     hh = property(_get_hh)
1407 
1408 
1409     def rhist_(self, ctxs, rehist=False, c2shape=False):
1410         """
1411         :param ctxs: list of Ctx 
1412         :param rehist: bool, when True recreate histograms, otherwise pre-persisted histos are loaded
1413 
1414         Collects unique seq0 and then interactes 
1415 
1416         """
1417         seq0s = list(set(map(lambda ctx:ctx.seq0,ctxs)))
1418         hh = []
1419         for seq0 in seq0s:
1420             sel = seq0.replace("_", " ")
1421             log.debug("AB.rhist_ setting sel to \"%s\" rehist %d  " % (sel, rehist) )
1422 
1423             self.sel = sel   # adjust selection 
1424 
1425             for ctx in filter(lambda ctx:ctx.seq0 == seq0, ctxs):
1426                 hs = self.rhist(qwn=ctx["qwn"], irec=int(ctx["irec"]), rehist=rehist, c2shape=c2shape)
1427                 assert len(hs) == 1
1428                 hh.append(hs[0])
1429             pass
1430         pass
1431         return hh
1432 
1433     def rhist(self, qwn=None, irec=None, rehist=False, log_=False, c2shape=True ): 
1434         """
1435         :param qwn: eg "XYZT"
1436         :param irec: int 
1437         :param rehist:
1438         :param log_:
1439         :param c2shape:
1440         :return hh: list of CFH persisted comparison histograms
1441         """
1442         if qwn is None:
1443             qwn = self.ok.qwns
1444         assert type(qwn) is str
1445 
1446         if irec is None:
1447             irec = self.irec
1448         assert type(irec) is int
1449 
1450         hh = []
1451 
1452         srec = CFH.srec_(irec)   # (ctx.py) srec:single char hexint 0123456789abcdef
1453 
1454         log.debug("AB.rhist qwn %s irec %s srec %s " % (qwn, irec, srec))
1455 
1456         for ir in CFH.irec_(srec):
1457 
1458             log.debug(" ir %s ir 0x%x " % (ir,ir))
1459 
1460             self.irec = ir
1461 
1462             for q in str(qwn):
1463 
1464                 self.qwn = q 
1465 
1466                 ctx=self.ctx
1467 
1468                 h = CFH(ctx)
1469 
1470                 h.log = log_
1471 
1472                 if h.exists() and not rehist:
1473                     h.load()
1474                 else:
1475                     bn, av, bv, la = self.rqwn(q, ir)
1476 
1477                     h(bn,av,bv,la, c2cut=self.C2CUT, c2shape=c2shape )
1478 
1479                     h.save()
1480                 pass
1481                 hh.append(h)
1482             pass
1483         pass
1484         return hh
1485  
1486 
1487 
1488 
1489 
1490     def rqwn(self, qwn, irec): 
1491         """
1492         :param qwn: X,Y,Z,W,T,A,B,C or R  
1493         :param irec: step index 0,1,...
1494         :return binx, aval, bval, labels
1495 
1496         ::
1497 
1498             bi,a,b,l = cf.rqwn("T",4)
1499 
1500         """
1501         a = self.a
1502         b = self.b
1503         lval = "%s[%s]" % (qwn.lower(), irec)
1504         labels = ["Op : %s" % lval, "G4 : %s" % lval]
1505  
1506         if qwn == "R":
1507             apost = a.rpost_(irec)
1508             bpost = b.rpost_(irec)
1509             aval = vnorm(apost[:,:2])
1510             bval = vnorm(bpost[:,:2])
1511             cbins = a.pbins()
1512         elif qwn == "W":
1513             aval = a.recwavelength(irec)
1514             bval = b.recwavelength(irec)
1515             cbins = a.wbins()
1516         elif qwn in Evt.RPOST:
1517             q = Evt.RPOST[qwn]
1518             aval = a.rpost_(irec)[:,q]
1519             bval = b.rpost_(irec)[:,q]
1520             if qwn == "T":
1521                 cbins = a.tbins()
1522             else:
1523                 cbins = a.pbins()
1524             pass
1525         elif qwn in Evt.RPOL:
1526             q = Evt.RPOL[qwn]
1527             aval = a.rpol_(irec)[:,q]
1528             bval = b.rpol_(irec)[:,q]
1529             cbins = a.rpol_bins()
1530         else:
1531             assert 0, "qwn %s unknown " % qwn 
1532         pass
1533 
1534         binscale = Evt.RQWN_BINSCALE[qwn]
1535         bins = cbins[::binscale]   
1536 
1537         # hmm should arrange scaling to retain extreme bins to avoid clipping perhaps ??
1538         # formerly tried clever binning, but certainty of simple binning turns out easier to interpret
1539         #    bins = decompression_bins(cbins, [aval, bval], label=lval, binscale=binscale )
1540         #  
1541 
1542         if len(bins) == 0:
1543             raise Exception("no bins")
1544 
1545         return bins, aval, bval, labels
1546 
1547     def seqhis_counts(self, slot=1, labs="AB RE SC BT"):
1548         ab = self
1549         a = self.a
1550         b = self.b
1551         ac = a.seqhis_counts(slot, labs) 
1552         bc = b.seqhis_counts(slot, labs) 
1553         assert ac.shape == bc.shape
1554         abc = np.zeros( (2,)+ac.shape , dtype=np.int32 )
1555         abc[0] = ac 
1556         abc[1] = bc 
1557         return abc
1558 
1559     def seqhis_splits(self, slot=1):
1560 
1561         ab = self
1562         a = self.a
1563         b = self.b
1564 
1565         dump = False
1566         a_uss = a.seqhis_splits(slot, dump) 
1567         b_uss = b.seqhis_splits(slot, dump) 
1568 
1569         codes = list(set( a_uss[0] )|set( b_uss[0] ))    
1570 
1571         a_total = a_uss[1].sum()
1572         b_total = b_uss[1].sum()
1573         assert a_total == b_total 
1574         total = a_total 
1575 
1576         cols = ["cod", "la","a", "af", "b", "bf", "a-b", "(a-b)^2/(a+b)" ] 
1577 
1578         cols_fmt = " %3s %2s  %6s  %10s     %6s  %10s  %6s   %13s " 
1579         line_fmt = " 0x%x %2s  %6d  %10.3f      %6d  %10.3f  %6d   %13.3f "
1580 
1581         cols_hdr = cols_fmt % tuple(cols) 
1582         print( cols_hdr + " slot %d  (seqhis_splits)  a.itag %d b.itag %d " % (slot, a.itag, b.itag) )
1583 
1584 
1585         for i in range(len(codes)):
1586             code = codes[i]
1587             label = self.histype.label(code) 
1588 
1589             a_codes = list(a_uss[0])
1590             b_codes = list(b_uss[0])
1591 
1592             a_index = a_codes.index(code) if code in a_codes else -1 
1593             b_index = b_codes.index(code) if code in b_codes else -1 
1594 
1595             a_count = a_uss[1][a_index] if a_index > -1 else 0  
1596             b_count = b_uss[1][b_index] if b_index > -1 else 0  
1597 
1598             ab_count = a_count - b_count 
1599             c2 = chi2one(a_count, b_count)
1600 
1601             a_frac = float(a_count)/float(total)
1602             b_frac = float(b_count)/float(total)
1603 
1604             print( line_fmt  % ( code, label, a_count, a_frac, b_count, b_frac, ab_count, c2 ))  
1605         pass
1606 
1607 
1608 
1609 
1610 
1611 if __name__ == '__main__':
1612     from opticks.ana.main import opticks_main
1613     ok = opticks_main()
1614     ab = AB(ok)  
1615 
1616     a = ab.a
1617     b = ab.b
1618     print("a.valid:%s" % a.valid)
1619     print("b.valid:%s" % b.valid)
1620     print("ab.valid:%s" % ab.valid)
1621 
1622     if ok.compare:
1623         ab.dump()
1624     pass
1625  
1626     als = ab.a.make_seqhis_ls() if ab.a.valid else None
1627     bls = ab.b.make_seqhis_ls() if ab.b.valid else None
1628 
1629     qq={}
1630     qq["a"]="als[:10]"
1631     qq["b"]="bls[:10]"
1632     for x in ["a","b"]:
1633         e = getattr(ab,x)
1634         if e.valid:
1635             for q in qq[x].split():
1636                 print(q)
1637                 print(eval(q))
1638             pass
1639         pass 
1640     pass
1641 
1642 
1643 
1644 
1645