File indexing completed on 2026-04-09 07:48:50
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 import os, datetime, logging, re, signal, sys
0022 log = logging.getLogger(__name__)
0023 import numpy as np
0024 lfilter = lambda *args:list(filter(*args))
0025
0026 try:
0027 reduce
0028 except NameError:
0029 from functools import reduce
0030 pass
0031
0032
0033 """
0034 About using IPython debugger
0035 ------------------------------
0036
0037 Plant an ipython debugger breakpoint inside some python module by
0038 duplicating the below code block into the head and entering
0039 the below at the critical code point::
0040
0041 ipdb.set_trace()
0042
0043 1. previously thought this was not working in py3, but it seems OK now
0044 2. attempting to import code doing the below doesnt work, have to include the code
0045 3. breakpoint actually lands on the statement immediately after "ipdb.set_trace()"
0046 4. when stopped at the breakpoint an >ipdb prompt should appear
0047
0048 Commands available from the ipdb prompt::
0049
0050 1. "bt" : show the stack backtrace
0051 2. "c" : continue from the breakpoint
0052
0053 Observed that even when using python this can magically
0054 jump into ipython when a breakpoint is reached. However
0055 see notes/issues/ipython-ipdb-issue.rst which made
0056 me add the check for an ipython invokation to prevent
0057 such fragile magic.
0058 """
0059
0060 if True:
0061 try:
0062 from IPython.core.debugger import Pdb as MyPdb
0063 except ImportError:
0064 class MyPdb(object):
0065 def set_trace(self):
0066 log.error("IPython is required for ipdb.set_trace() " )
0067 pass
0068 pass
0069 pass
0070 ipdb = MyPdb()
0071 else:
0072 ipdb = None
0073 pass
0074
0075
0076 from opticks.ana.base import ihex_
0077 from opticks.ana.nbase import chi2, chi2_pvalue, ratio, count_unique_sorted
0078 from opticks.ana.nload import A
0079
0080 nibble_ = lambda n:0xf << (4*n)
0081 firstlast_ = lambda name:name[0] + name[-1]
0082
0083 class BaseType(object):
0084 hexstr = re.compile("^[0-9a-f]+$")
0085 def __init__(self, flags, abbrev, delim=" "):
0086 """
0087 When no abbreviation available, use first and last letter of name eg::
0088
0089 MACHINERY -> MY
0090 FABRICATED -> FD
0091 G4GUN -> GN
0092
0093 """
0094
0095 log.debug("flags.names %s " % repr(flags.names) )
0096 log.debug("abbrev.name2abbr %s " % abbrev.name2abbr )
0097 log.debug("abbrev %s " % repr(map(lambda _:abbrev.name2abbr.get(_,None),flags.names)))
0098
0099 abbrs = list(map(lambda name:abbrev.name2abbr.get(name,firstlast_(name)), flags.names ))
0100
0101 abbr2code = dict(zip(abbrs, flags.codes))
0102 code2abbr = dict(zip(flags.codes, abbrs))
0103 aflags = np.array( [' '] + list(map(lambda _:"%s " % _ ,abbrs )), dtype="|S3" )
0104
0105 self.abbr2code = abbr2code
0106 self.code2abbr = code2abbr
0107 self.flags = flags
0108 self.abbrev = abbrev
0109 self.delim = delim
0110 self.aflags = aflags
0111
0112 def __call__(self, args):
0113 for a in args:
0114 return self.code(a)
0115
0116 def check(self, s):
0117 f = self.abbr2code
0118 bad = 0
0119 for n in s.strip().split(self.delim):
0120 if f.get(n,0) == 0:
0121
0122 bad += 1
0123
0124
0125
0126 return bad
0127
0128
0129 def seq2msk_procedural(isq):
0130 ifl = 0
0131 for n in range(16):
0132 msk = 0xf << (4*n)
0133 nib = ( isq & msk ) >> (4*n)
0134 if nib == 0:continue
0135 flg = 1 << (nib - 1)
0136 ifl |= flg
0137 pass
0138 return ifl
0139
0140 def seq2msk(isq):
0141 """
0142 Convert seqhis into mskhis
0143
0144 OpticksPhoton.h uses a mask but seq use the index for bit-bevity::
0145
0146 3 enum
0147 4 {
0148 5 CERENKOV = 0x1 << 0,
0149 6 SCINTILLATION = 0x1 << 1,
0150 7 MISS = 0x1 << 2,
0151 8 BULK_ABSORB = 0x1 << 3,
0152 9 BULK_REEMIT = 0x1 << 4,
0153
0154
0155 """
0156 ifl = np.zeros_like(isq)
0157 for n in range(16):
0158 msk = 0xf << (4*n)
0159 nib = ( isq & msk ) >> (4*n)
0160 flg = 1 << ( nib[nib>0] - 1 )
0161 ifl[nib>0] |= flg
0162 pass
0163 return ifl
0164
0165
0166 class MaskType(BaseType):
0167 def __init__(self, flags, abbrev):
0168 BaseType.__init__(self, flags, abbrev, delim="|")
0169 log.debug("abbr2code %s " % repr(self.abbr2code))
0170 log.debug("code2abbr %s " % repr(self.code2abbr))
0171 log.debug("flags.codes %s " % repr(self.flags.codes))
0172
0173 def code(self, s):
0174 """
0175 :param s: abbreviation string eg "TO|BT|SD" or hexstring 8ccccd (without 0x prefix)
0176 :return: integer bitmask
0177 """
0178
0179
0180 if self.hexstr.match(s):
0181 c = int(s,16)
0182 cs = "%x" % c
0183 log.debug("converted hexstr %s to hexint %x and back %s " % (s,c,cs))
0184 assert s == cs
0185 else:
0186 f = self.abbr2code
0187 bad = self.check(s)
0188 c = reduce(lambda a,b:a|b,list(map(lambda n:f.get(n,0), s.split(self.delim))))
0189 pass
0190 return c
0191
0192
0193 def label(self, arg):
0194 """
0195 :param i: integer bitmask
0196 :return: abbreviation mask string
0197 """
0198 if type(arg) is list or issubclass( arg.__class__, np.ndarray ):
0199 return list(map(lambda _:self.label(_), arg))
0200 pass
0201
0202 if type(arg) is int:
0203 i = arg
0204 elif type(arg) in (np.uint64,np.int64,np.uint32,np.int32):
0205 i = arg
0206 elif type(arg) is str:
0207 if self.hexstr.match(arg):
0208 i = int(arg, 16)
0209 else:
0210 return arg
0211 pass
0212 else:
0213 log.fatal("unexpected argtype %s %s " % (arg, repr(type(arg))))
0214 assert 0
0215 pass
0216
0217
0218 codes = filter(lambda c:int(i) & c, self.flags.codes)
0219 codes = sorted(codes,reverse=True)
0220 d = self.code2abbr
0221 return self.delim.join(map(lambda _:d.get(_,'?%s?' % _ ), codes ))
0222
0223
0224 class SeqType(BaseType):
0225 def __init__(self, flags, abbrev):
0226
0227 BaseType.__init__(self, flags, abbrev, delim=" ")
0228
0229 def code(self, s):
0230 """
0231 :param s: abbreviation sequence string eg "TO BT BR BR BR BT SA"
0232 :return: integer code eg 0x8cbbbcd
0233 """
0234 if type(s) is list or issubclass( s.__class__, np.ndarray ):
0235 return list(map(lambda _:self.code(_), s))
0236 pass
0237
0238
0239 if self.hexstr.match(s):
0240 c = int(s,16)
0241 cs = "%x" % c
0242 log.debug("converted hexstr %s to hexint %x and back %s " % (s,c,cs))
0243 assert s == cs
0244 else:
0245 f = self.abbr2code
0246 bad = self.check(s)
0247
0248 if bad>0:
0249
0250 log.warn("SeqType.code check [%s] bad %d " % (s, bad))
0251
0252 c = reduce(lambda a,b:a|b,map(lambda ib:ib[1] << 4*ib[0],enumerate(map(lambda n:f.get(n,0), s.split(self.delim)))))
0253 pass
0254 return c
0255
0256
0257 def label(self, arg):
0258 """
0259 :param i: integer code
0260 :return: abbreviation sequence string
0261
0262 ::
0263
0264 In [6]: from opticks.ana.histype import HisType
0265 In [7]: af = HisType()
0266
0267 In [4]: af.label(0xccd) # hexint
0268 Out[4]: 'TO BT BT'
0269
0270 In [5]: af.label("TO BT BT") # already a label
0271 Out[5]: 'TO BT BT'
0272
0273 In [6]: af.label("ccd") # hexstring (NB without 0x)
0274 Out[6]: 'TO BT BT'
0275
0276 In [7]: af.label(".ccd") # hexstring with wildcard continuation char "."
0277 Out[7]: 'TO BT BT ..'
0278
0279 """
0280 if type(arg) is list or issubclass( arg.__class__, np.ndarray ):
0281 return list(map(lambda _:self.label(_), arg))
0282 pass
0283
0284 i = None
0285 wildcard = type(arg) == str and arg[0] == "."
0286 if wildcard:
0287 arg = arg[1:]
0288
0289 if type(arg) is int:
0290 i = arg
0291 elif type(arg) is np.uint64:
0292 i = arg
0293 elif type(arg) is str:
0294 if self.hexstr.match(arg):
0295 i = int(arg, 16)
0296 else:
0297 return arg
0298 pass
0299 else:
0300 log.fatal("unexpected argtype %s %s " % (arg, repr(type(arg))))
0301 assert 0
0302 pass
0303
0304 xs = ihex_(i)[::-1]
0305 seq = list(map(lambda _:int(_,16), xs ))
0306
0307 d = self.code2abbr
0308 elem = list(map(lambda _:d.get(_,'?%s?' % _ ), seq ))
0309 if wildcard:
0310 elem += [".."]
0311
0312 return self.delim.join(elem)
0313
0314
0315
0316 class SeqList(object):
0317 def __init__(self, ls, af, sli ):
0318 """
0319 :param ls: seqhis or seqmat array of integers
0320 :param af: histype or mattype able to decode integers into labels
0321 """
0322 self.ls = ls
0323 self.afl = af.label
0324 self.sli = sli
0325
0326 def __repr__(self):
0327 return "\n".join(map(lambda _:self.afl(_), self.ls[self.sli]))
0328
0329 def __getitem__(self, sli):
0330 self.sli = sli
0331 return self
0332
0333
0334
0335
0336 class SeqTable(object):
0337 """
0338 Based on count_unique_sorted applied to a photon length array of sequence history codes
0339
0340 """
0341 ptn_ = r"^(?P<idx>\d{4})\s*(?P<code>[0-9a-f]+)\s*(?P<a>\d+)\s*(?P<b>\d+)\s*(?P<cf>\S*).*$"
0342 ptn = re.compile(ptn_)
0343
0344 @classmethod
0345 def FromTxt(cls, txt, af, **kwa):
0346 """
0347 Hmm this assumes a comparison table with cu(count-unique) array of shape (n,3)
0348 """
0349 dd = []
0350 for line in txt.split("\n"):
0351 m = cls.ptn.match(line)
0352 if not m: continue
0353 dd.append(m.groupdict())
0354 pass
0355 cu = np.zeros( (len(dd),3), dtype=np.uint64 )
0356 for i,d in enumerate(dd):
0357 cu[i] = ( int("0x%s"%d["code"],16), int(d["a"]), int(d["b"]) )
0358 pass
0359 return cls(cu, af, **kwa)
0360
0361
0362 def __init__(self, cu, af, cnames=[], dbgseq=0, dbgmsk=0, dbgzero=False, cmx=0, c2cut=30, shortname="noshortname"):
0363 """
0364 :param cu: count unique array, typically shaped (n, 2) or (n,3) for comparisons
0365 :param af: instance of SeqType subclass such as HisType
0366 :param cnames: column names
0367
0368 """
0369 log.debug("cnames %s " % repr(cnames))
0370
0371 assert len(cu.shape) == 2 and cu.shape[1] >= 2
0372 ncol = cu.shape[1] - 1
0373
0374 log.debug("SeqTable.__init__ dbgseq %x" % dbgseq)
0375 log.debug("shortname %s cu.shape %s ncol: %s" % (shortname,repr(cu.shape), ncol))
0376 assert shortname != "noshortname"
0377
0378 if sys.version_info.major in (2,3):
0379 pass
0380
0381 pass
0382
0383
0384
0385 self.dirty = False
0386 self.cu = cu
0387
0388 self.ncol = ncol
0389 self.dbgseq = dbgseq
0390 self.dbgmsk = dbgmsk
0391 self.dbgzero = dbgzero
0392 self.cmx = cmx
0393 self.shortname = shortname
0394
0395 seqs = cu[:,0]
0396 msks = seq2msk(seqs)
0397
0398 tots = [cu[:,n].sum() for n in range(1,ncol+1)]
0399
0400 if ncol == 2:
0401 a = cu[:,1].astype(np.float64)
0402 b = cu[:,2].astype(np.float64)
0403
0404 ia = cu[:,1].astype(np.int64)
0405 ib = cu[:,2].astype(np.int64)
0406 idif = ia-ib
0407
0408 c2, c2n, c2c = chi2(a, b, cut=c2cut)
0409
0410
0411
0412
0413 ndf = c2n - 1
0414
0415 c2sum = c2.sum()
0416 c2p = c2sum/max(1,ndf)
0417
0418 c2_pval = chi2_pvalue( c2sum , ndf )
0419
0420
0421 log.debug(" c2sum %10.4f ndf %d c2p %10.4f c2_pval %10.4f " % (c2sum,ndf,c2p, c2_pval ))
0422
0423
0424
0425
0426 stats = "%.2f/%d = %5.2f pvalue:P[C2>]:%0.3f 1-pvalue:P[C2<]:%0.3f " % (c2sum,ndf,c2p,c2_pval,1-c2_pval)
0427
0428 cfcount = cu[:,1:]
0429
0430 ab, ba = ratio(a, b)
0431
0432
0433
0434 else:
0435 c2 = None
0436
0437 c2p = None
0438 cfcount = None
0439 ab = None
0440 ba = None
0441 ia = None
0442 ib = None
0443 idif = None
0444 stats = None
0445 c2sum = None
0446 pass
0447 self.ia = ia
0448 self.ib = ib
0449 self.idif = idif
0450
0451
0452 if len(tots) == 1:
0453 total = tots[0]
0454 tots += ["%10.2f" % 1.0 ]
0455 else:
0456 total = None
0457 pass
0458
0459 self.total = total
0460 self.c2 = c2
0461
0462 self.c2p = c2p
0463 self.ab = ab
0464 self.ba = ba
0465
0466 self.stats = stats
0467 self.seqs = seqs
0468 self.msks = msks
0469
0470 codes = cu[:,0]
0471 counts = cu[:,1]
0472
0473
0474
0475
0476 labels = list(map(lambda i:af.label(i), codes ))
0477 nstep = list(map(lambda l:len(l.split(af.delim)),labels))
0478
0479 self.label2nstep = dict(zip(labels, nstep))
0480 self.labels = labels
0481 self.codes = codes
0482 self.counts = counts
0483
0484 k_line = self.line(0, key=True, smry=False) if len(cu) > 0 else ""
0485 k_sine = self.line(-1, key=True, smry=True) if len(cu) > 0 else ""
0486
0487 lines = list(filter(None, list(map(lambda n:self.line(n,smry=False), range(len(cu))))))
0488 sines = list(filter(None, list(map(lambda n:self.line(n,smry=True ), range(len(cu))))))
0489
0490 self.lines = [k_line] + lines + [k_line]
0491 self.sines = [k_sine] + sines + [k_sine]
0492
0493 self.label2count = dict(zip(labels, counts))
0494 self.label2line = dict(zip(labels, lines))
0495 self.label2sine = dict(zip(labels, sines))
0496 self.label2code = dict(zip(labels, seqs))
0497
0498 if cfcount is not None:
0499 self.label2cfcount = dict(zip(labels, cfcount))
0500 pass
0501
0502 self.cnames = cnames
0503 self.tots = tots
0504 self.c2sum = c2sum
0505 log.debug(" tots %s " % repr(tots))
0506
0507 self.af = af
0508 self.sli = slice(None)
0509
0510
0511
0512
0513 def cfo_line(self, n):
0514 if self.ncol == 2:
0515 cfo_key = getattr(self, 'cfordering_key', [] )
0516 if len(cfo_key) > 0:
0517 cfo_debug = " %10.5f " % self.cfordering_key[n]
0518 else:
0519
0520 cfo_debug = " no cfo_key"
0521 pass
0522 else:
0523 cfo_debug = ""
0524 pass
0525 return cfo_debug
0526
0527
0528 k_xn_dot = ".%4s"
0529 k_xn_fmt = "%4s "
0530 xn_fmt = "%0.4d "
0531
0532 k_xs_fmt = "%16s "
0533 k_xv_fmt = " %7s "
0534
0535 idif_fmt = " %4d "
0536 k_idif_fmt = " %4s "
0537
0538 sc2_fmt = " %10.2f "
0539 k_sc2_fmt = "%14s"
0540 div1 = " "
0541 div2 = " "
0542 div3 = " "
0543
0544 k_label_fmt = "%s"
0545
0546
0547 def line(self, n, key=False, smry=False):
0548 """
0549 :param n: 0-based line number
0550 :param key: when True returns column labels, ignoring n
0551 :param smry: when True presents less columns
0552 :return str: formatted line of sequence table
0553 """
0554 iseq = int(self.cu[n,0])
0555 imsk = int(self.msks[n])
0556
0557 if self.dbgseq > 0 and ( self.dbgseq & iseq ) != self.dbgseq:
0558 return None
0559 pass
0560
0561 if self.dbgmsk > 0:
0562 pick = (self.dbgmsk & imsk) == self.dbgmsk
0563 if not pick:
0564 return None
0565 pass
0566 pass
0567
0568 if smry == False:
0569 xn = self.xn_fmt % (n)
0570 k_xn = self.k_xn_fmt % ("n")
0571 else:
0572 xn = self.xn_fmt % (n)
0573 k_xn = self.k_xn_fmt % ("n")
0574 pass
0575
0576 if smry == False:
0577 xs = self.k_xs_fmt % (ihex_(iseq))
0578 k_xs = self.k_xs_fmt % ("iseq")
0579 else:
0580 xs = self.k_xs_fmt % (ihex_(iseq))
0581 k_xs = self.k_xs_fmt % ("iseq")
0582 pass
0583
0584
0585 cfo_debug = ""
0586 k_cfo_debug = ""
0587
0588 vals = list(map(lambda _:self.k_xv_fmt % _, self.cu[n,1:] ))
0589 keys = "abcdefghijklmnopqrstuvwxyz"
0590 k_vals = list(map(lambda _:self.k_xv_fmt % keys[_], range(len(self.cu[n,1:])) ))
0591
0592 idif = self.idif[n] if len(vals) == 2 else None
0593 idif = self.idif_fmt % idif if idif is not None else " "
0594 k_idif = self.k_idif_fmt % "a-b" if idif is not None else " "
0595
0596 label = self.k_label_fmt % self.labels[n]
0597 k_label = self.k_label_fmt % "label"
0598
0599 if smry == False:
0600 nstep = "[%-2d]" % self.label2nstep[label]
0601 k_nstep = "[ns]"
0602 else:
0603 nstep = ""
0604 k_nstep = ""
0605 pass
0606
0607
0608 if self.c2 is not None:
0609 if self.cmx > 0 and self.c2[n] < self.cmx:
0610 return None
0611 pass
0612 pass
0613
0614
0615 if self.c2 is not None:
0616 if self.dbgzero and self.cu[n,1] > 0 and self.cu[n,2] > 0:
0617 return None
0618 pass
0619 pass
0620
0621 if self.c2 is not None:
0622 sc2 = self.sc2_fmt % (self.c2[n])
0623 k_sc2 = self.k_sc2_fmt % "(a-b)^2/(a+b)"
0624 else:
0625 sc2 = ""
0626 k_sc2 = ""
0627 pass
0628
0629 if self.ab is not None and smry == False:
0630 sab = " %10.3f +- %5.3f " % ( self.ab[n,0], self.ab[n,1] )
0631 k_sab = " %10s %5s " % ( "a/b", "" )
0632 else:
0633 sab = ""
0634 k_sab = ""
0635 pass
0636
0637 if self.ba is not None and smry == False:
0638 sba = " %10.3f +- %5.3f " % ( self.ba[n,0], self.ba[n,1] )
0639 k_sba = " %10s %5s " % ( "b/a", "" )
0640 else:
0641 sba = ""
0642 k_sba = ""
0643 pass
0644
0645 if self.total is not None:
0646 frac = float(self.cu[n,1])/float(self.total)
0647 frac = " %10.3f " % frac
0648 k_frac = " %10s " % "frac"
0649 else:
0650 frac = ""
0651 k_frac = ""
0652 pass
0653
0654 cols = [ cfo_debug, xn, xs, frac] + vals + [self.div1, idif , self.div2, sc2, sab, sba, nstep, self.div3, label]
0655 k_cols = [k_cfo_debug, k_xn,k_xs, k_frac] + k_vals + [self.div1, k_idif , self.div2, k_sc2, k_sab, k_sba, k_nstep, self.div3, k_label]
0656
0657 u_cols = k_cols if key==True else cols
0658 return "".join(filter(lambda _:_ != "", u_cols))
0659
0660 def present(self, smry=False):
0661 """
0662 title is externally set from evt.present_table
0663 """
0664 spacer_ = lambda _:"%1s%3s %22s " % (".","",_)
0665 space = spacer_("")
0666
0667 lhs_fmt = self.k_xn_dot + self.k_xs_fmt
0668 lhs_key = lhs_fmt % ("","TOTALS:")
0669
0670 rhs_fmt = self.div1 + self.k_idif_fmt + self.div2 + self.k_sc2_fmt + self.div3 + self.k_label_fmt
0671
0672 if self.c2sum is None or self.stats is None:
0673 rhs_key = ""
0674 else:
0675 rhs_key = rhs_fmt % ( "", self.sc2_fmt % self.c2sum, self.stats )
0676 pass
0677
0678 title = spacer_(getattr(self,'title',"")+" cfo:"+getattr(self,'cfordering',"-"))
0679 body_ = lambda _:" %7s " % _
0680
0681 head = title + " ".join(map(body_, self.cnames ))
0682
0683
0684 tots = lhs_key + "".join(map(body_, self.tots)) + rhs_key
0685
0686 if smry:
0687 return "\n".join([tots] + lfilter(None,self.sines[self.sli]) + [tots])
0688 else:
0689 return "\n".join([self.shortname,head,tots]+ lfilter(None,self.lines[self.sli]) + [tots])
0690 pass
0691
0692 def __repr__(self):
0693 return self.present(smry=False)
0694
0695 def __str__(self):
0696 return self.present(smry=True)
0697
0698 def __call__(self, labels):
0699 ll = sorted(list(labels), key=lambda _:self.label2count.get(_, None))
0700 return "\n".join(map(lambda _:self.label2line.get(_,None), ll ))
0701
0702 def _get_ll(self):
0703 return np.array(self.lines[1:-1])
0704 ll = property(_get_ll)
0705
0706 def _get_ss(self):
0707 return np.array(self.sines[1:-1])
0708 ss = property(_get_ss)
0709
0710 def __getitem__(self, sli):
0711 self.sli = sli
0712 return self
0713
0714 def compare(self, other, ordering="self", shortname="noshortname?"):
0715 """
0716 :param other: SeqTable instance
0717 :param ordering: string "self", "other", "max" control descending count row ordering
0718 """
0719 log.debug("SeqTable.compare START")
0720 l = set(self.labels)
0721 o = set(other.labels)
0722 lo = list(l | o)
0723
0724
0725 if ordering == "max":
0726 ordering_ = lambda _:max(self.label2count.get(_,0),other.label2count.get(_,0))
0727 elif ordering == "sum":
0728 ordering_ = lambda _:self.label2count.get(_,0)+other.label2count.get(_,0)
0729 elif ordering == "sum_code":
0730 ordering_ = lambda _:1e9*float(self.label2count.get(_,0)+other.label2count.get(_,0))+float(self.label2code.get(_,0)+other.label2code.get(_,0))
0731 elif ordering == "code":
0732 ordering_ = lambda _:self.label2code.get(_,0) + other.label2code.get(_,0)
0733 elif ordering == "self":
0734 ordering_ = lambda _:self.label2count.get(_,0)
0735 elif ordering == "other":
0736 ordering_ = lambda _:other.label2count.get(_,0)
0737 else:
0738 assert 0, "ordering_ must be one of max/sum/self/other "
0739 pass
0740
0741 u = sorted( lo, key=ordering_, reverse=True)
0742
0743
0744 cf = np.zeros( (len(u),3), dtype=np.uint64 )
0745 cf[:,0] = list(map(lambda _:self.af.code(_), u ))
0746 cf[:,1] = list(map(lambda _:self.label2count.get(_,0), u ))
0747 cf[:,2] = list(map(lambda _:other.label2count.get(_,0), u ))
0748
0749
0750 cnames = self.cnames + other.cnames
0751
0752 log.debug("compare dbgseq %x dbgmsk %x " % (self.dbgseq, self.dbgmsk))
0753
0754 cftab = SeqTable(cf, self.af, cnames=cnames, dbgseq=self.dbgseq, dbgmsk=self.dbgmsk, dbgzero=self.dbgzero, cmx=self.cmx, shortname=shortname)
0755 cftab.cfordering = ordering
0756
0757 cfordering_key = list(map(ordering_, u))
0758 log.debug("cfordering_key for %s" % shortname)
0759
0760
0761 cftab.cfordering_key = cfordering_key
0762 log.debug("SeqTable.compare DONE")
0763 return cftab
0764
0765
0766 class SeqAna(object):
0767 """
0768 Canonical usage is from evt with::
0769
0770 self.seqhis_ana = SeqAna(self.seqhis, self.histype)
0771 self.seqmat_ana = SeqAna(self.seqmat, self.mattype)
0772
0773 In addition to holding the SeqTable instance SeqAna provides
0774 methods to make boolean array selections using the aseq and
0775 form labels.
0776
0777 SeqAna and its contained SeqTable exist within a particular selection,
0778 ie changing selection entails recreation of SeqAna and its contained SeqTable
0779
0780 Hmm: when searching for nibbles (eg RE) it would be convenient to view seqhis as an
0781 np.int4/np.uint4 dtype, but there is no such thing.
0782 """
0783 @classmethod
0784 def for_evt(cls, af, tag="1", src="torch", det="dayabay", pfx="source", offset=0):
0785 ph = A.load_("ph",src,tag,det, pfx=pfx)
0786 aseq = ph[:,0,offset]
0787 return cls(aseq, af, cnames=[tag])
0788
0789 def __init__(self, aseq, af, cnames=["noname"], dbgseq=0, dbgmsk=0, dbgzero=False, cmx=0, table_shortname="no_table_shortname"):
0790 """
0791 :param aseq: photon length sequence array, eg a.seqhis or a.seqmat
0792 :param af: instance of SeqType subclass, which knows what the nibble codes mean
0793
0794 ::
0795
0796 In [10]: sa.aseq
0797 A([ 9227469, 9227469, 147639405, ..., 9227469, 9227469, 19661], dtype=uint64)
0798
0799 In [11]: sa.aseq.shape
0800 Out[11]: (1000000,)
0801
0802 """
0803 cu = count_unique_sorted(aseq)
0804 self.af = af
0805 self.dbgseq = dbgseq
0806 self.dbgmsk = dbgmsk
0807 self.dbgzero = dbgzero
0808 self.cmx = cmx
0809
0810 self.table = SeqTable(cu, af, cnames=cnames, dbgseq=self.dbgseq, dbgmsk=self.dbgmsk, dbgzero=self.dbgzero, cmx=self.cmx, shortname=table_shortname)
0811
0812 self.aseq = aseq
0813 self.cu = cu
0814
0815 def labels(self, prefix=None):
0816 """
0817 :param prefix: string sequence label eg "TO BT BT SC"
0818 :return labels: list of string labels that start with the prefix
0819 """
0820 codes = self.cu[:,0]
0821 if not prefix is None:
0822 pfx = self.af.code(prefix)
0823 codes = codes[np.where( codes & pfx == pfx )]
0824 pass
0825 labels = map( lambda _:self.af.label(_), codes )
0826 return labels
0827
0828 def seq_or(self, sseq):
0829 """
0830 :param sseq: list of sequence labels including source, eg "TO BR SA" "TO BR AB"
0831 :return psel: selection boolean array of photon length
0832
0833 Selection of photons with any of the sequence arguments
0834 """
0835
0836 af = self.af
0837 bseq = list(map(lambda _:self.aseq == af.code(_), sseq))
0838 psel = np.logical_or.reduce(bseq)
0839 return psel
0840
0841 def seq_or_count(self, sseq):
0842 psel = self.seq_or(sseq)
0843 return np.count_nonzero(psel)
0844
0845 def seq_startswith(self, prefix):
0846 """
0847 :param prefix: sequence string stub eg "TO BT BT SC"
0848 :return psel: selection boolean array of photon length
0849
0850 Selection of all photons starting with prefix sequence
0851 """
0852 af = self.table.af
0853 pfx = af.code(prefix)
0854 psel = self.aseq & pfx == pfx
0855 return psel
0856
0857 def seq_startswith_count(self, prefix):
0858 psel = self.seq_startswith(prefix)
0859 return np.count_nonzero(psel)
0860
0861 def seq_any_(self, co="RE"):
0862 code = self.af.code(co)
0863 aseq = self.aseq
0864 wk = np.zeros( (len(aseq), 16), dtype=np.bool )
0865 for n in range(16): wk[:, n] = ( aseq & ( 0xf << (n*4) ) == ( code << (n*4) ))
0866 return wk
0867
0868 def seq_any(self, co="RE" ):
0869 """
0870 :param co: string label for seqhis nibble
0871 :return psel: selection boolean array of photon length
0872
0873 Selects photons with the co nibble in any of the 16 slots
0874 """
0875 wk = self.seq_any_(co)
0876 psel = np.any(wk, axis=1)
0877 return psel
0878
0879 def seq_any_count(self, co="RE" ):
0880 """
0881 :param co: string label for seqhis nibble
0882 :return count: count_nonzero of psel result of seq_any
0883
0884 The count is of photons with the co in any slot (this is not the count of nibbles)
0885 """
0886 psel = self.seq_any(co)
0887 return np.count_nonzero(psel)
0888
0889 def seq_any_count_nibble(self, co="RE"):
0890 """
0891 Like seq_any_count but instead of counting photons count nibbles
0892 """
0893 wk = self.seq_any_(co)
0894 return np.count_nonzero(wk.ravel())
0895
0896
0897 def test_simple_table():
0898 log.info("test_simple_table")
0899 from opticks.ana.histype import HisType
0900 af = HisType()
0901 cu = np.zeros( (5,2), dtype=np.uint64 )
0902 cu[0] = (af.code("TO BT AB"), 100)
0903 cu[1] = (af.code("TO BT AB SD"),200)
0904 cu[2] = (af.code("TO BT BR BT AB"),300)
0905 cu[3] = (af.code("TO BT BT BT AB SD"),400)
0906 cu[4] = (af.code("TO BT AB MI"),500)
0907
0908 table = SeqTable(cu, af)
0909 print(table)
0910
0911 def test_comparison_table():
0912 log.info("test_comparison_table")
0913 from opticks.ana.histype import HisType
0914 af = HisType()
0915 cu = np.zeros( (8,3), dtype=np.uint64 )
0916
0917 a = 0
0918 b = 0
0919
0920 cu[0] = (af.code("TO"), a,b)
0921 cu[1] = (af.code("TO BT"), a,b)
0922 cu[2] = (af.code("TO BT BT"),a,b)
0923 cu[3] = (af.code("TO BT BT BT"),a,b)
0924 cu[4] = (af.code("TO BT BT BT BT"),a,b)
0925 cu[5] = (af.code("TO BT BT BT BT BT"),a,b)
0926 cu[6] = (af.code("TO BT BT BT BT BT BT"),a,b)
0927 cu[7] = (af.code("TO BT BT BT BT BT BT BT"),a,b)
0928
0929 table = SeqTable(cu, af)
0930 print(table)
0931
0932
0933 def test_comparison_table_2():
0934 txt = r"""
0935 ab.ahis
0936 . all_seqhis_ana 1:tboolean-box:tboolean-box -1:tboolean-box:tboolean-box c2 ab ba
0937 . 10000 10000 2285.00/5 = 457.00 (pval:1.000 prob:0.000)
0938 0000 8ccd 8805 8807 -2 0.00 1.000 +- 0.011 1.000 +- 0.011 [4 ] TO BT BT SA
0939 0001 3bd 580 0 580 580.00 0.000 +- 0.000 0.000 +- 0.000 [3 ] TO BR MI
0940 0002 3cbcd 563 0 563 563.00 0.000 +- 0.000 0.000 +- 0.000 [5 ] TO BT BR BT MI
0941 0003 8cbbcd 29 29 0 0.00 1.000 +- 0.186 1.000 +- 0.186 [6 ] TO BT BR BR BT SA
0942 0004 3cbbbcd 6 0 6 0.00 0.000 +- 0.000 0.000 +- 0.000 [7 ] TO BT BR BR BR BT MI
0943 0005 36d 5 0 5 0.00 0.000 +- 0.000 0.000 +- 0.000 [3 ] TO SC MI
0944 0006 4d 3 0 3 0.00 0.000 +- 0.000 0.000 +- 0.000 [2 ] TO AB
0945 0007 86ccd 2 2 0 0.00 1.000 +- 0.707 1.000 +- 0.707 [5 ] TO BT BT SC SA
0946 0008 8c6cd 1 1 0 0.00 1.000 +- 1.000 1.000 +- 1.000 [5 ] TO BT SC BT SA
0947 0009 3b6bd 1 0 1 0.00 0.000 +- 0.000 0.000 +- 0.000 [5 ] TO BR SC BR MI
0948 0010 8b6ccd 1 0 1 0.00 0.000 +- 0.000 0.000 +- 0.000 [6 ] TO BT BT SC BR SA
0949 0011 3cc6d 1 0 1 0.00 0.000 +- 0.000 0.000 +- 0.000 [5 ] TO SC BT BT MI
0950 0012 3cc6ccd 1 0 1 0.00 0.000 +- 0.000 0.000 +- 0.000 [7 ] TO BT BT SC BT BT MI
0951 0013 4ccd 1 1 0 0.00 1.000 +- 1.000 1.000 +- 1.000 [4 ] TO BT BT AB
0952 0014 3c6cd 1 0 1 0.00 0.000 +- 0.000 0.000 +- 0.000 [5 ] TO BT SC BT MI
0953 0015 8bd 0 580 -580 580.00 0.000 +- 0.000 0.000 +- 0.000 [3 ] TO BR SA
0954 0016 8cbc6d 0 1 -1 0.00 0.000 +- 0.000 0.000 +- 0.000 [6 ] TO SC BT BR BT SA
0955 0017 86d 0 5 -5 0.00 0.000 +- 0.000 0.000 +- 0.000 [3 ] TO SC SA
0956 0018 8cbbc6ccd 0 1 -1 0.00 0.000 +- 0.000 0.000 +- 0.000 [9 ] TO BT BT SC BT BR BR BT SA
0957 0019 8cbbbb6cd 0 1 -1 0.00 0.000 +- 0.000 0.000 +- 0.000 [9 ] TO BT SC BR BR BR BR BT SA
0958 . 10000 10000 2285.00/5 = 457.00 (pval:1.000 prob:0.000)
0959 """
0960 log.info("test_comparison_table_2")
0961 from opticks.ana.histype import HisType
0962 af = HisType()
0963 table = SeqTable.FromTxt(txt, af)
0964 print(table)
0965
0966
0967 if __name__ == '__main__':
0968 pass
0969
0970 logging.basicConfig(level=logging.INFO)
0971
0972
0973 test_comparison_table_2()
0974
0975
0976