GeneralBrokenLines V03-01-02
gblpy
gblfit.py
Go to the documentation of this file.
1'''
2Track fit with general broken lines.
3
4Created on Jul 27, 2011
5
6@author: kleinwrt
7'''
8
9
29
30import numpy as np
31import math
32from gblnum import BorderedBandMatrix
33from mille import MilleRecord
34
35
36
44class GblMeasurement(object):
45
46
55 def __init__(self, aMeasurement, minPrecision=0.):
56
57 self.__measurement = aMeasurement
58
59 self.__measDim = aMeasurement[1].shape[0]
60
61 self.__measMinPrec = minPrecision
62
64
66
67 self.__globalLabels = None
68
70
71 # full precision matrix?
72 if (aMeasurement[2].ndim == 2):
73 # need to diagonalize
74 eigenVal, eigenVec = np.linalg.eigh(aMeasurement[2])
75 self.__measTransformation = eigenVec.T
76 # transform measurement
77 if (aMeasurement[0] is None):
79 else:
80 self.__measurement[0] = np.dot(self.__measTransformation, aMeasurement[0])
81 self.__measurement[1] = np.dot(self.__measTransformation, aMeasurement[1])
82 self.__measurement[2] = eigenVal
83
84
88 def hasMeasurement(self):
89
90 return (self.__measurement is not None)
91
92
96 def getMeasurement(self):
97 return self.__measurement
98
99
103 def getMeasDim(self):
104 return self.__measDim
105
106
110 def getMeasMinPrec(self):
111 return self.__measMinPrec
112
113
117 def addLocals(self, derivatives):
118 if (self.__measDim > 0):
119 if (self.__measTransformation is None):
120 self.__localDerivatives = derivatives
121 else:
122 self.__localDerivatives = np.dot(self.__measTransformation, derivatives)
123
124
129 def addGlobals(self, labels, derivatives):
130 if (self.__measDim > 0):
131 self.__globalLabels = labels
132 if (self.__measTransformation is None):
133 self.__globalDerivatives = derivatives
134 else:
135 self.__globalDerivatives = np.dot(self.__measTransformation, derivatives)
136
137
141 def getNumLocals(self):
142 if (self.__localDerivatives is not None):
143 return self.__localDerivatives.shape[1]
144 else:
145 return 0
146
147
152 return self.__localDerivatives
153
154
158 return self.__globalLabels
159
160
165 return self.__globalDerivatives
166
167
169 print " measurement ", self.__measDim, len(self.__localDerivatives), len(self.__globalDerivatives)
170
171#------------------------------------------------------------------------------
172
173
174
182class GblPoint(object):
183
184
188 def __init__(self, aJacobian):
189
190 self.__label = 0
191
192 self.__offset = 0
193
194 self.__type = 0
195
196 self.__p2pJacobian = aJacobian
197
198 self.__jacobians = [ [], [] ]
199
201
202 self.__scatDim = 0
203
204 self.__scatterer = None
205
206# for extension to retrieval of residuals, pulls
207# self.__dataMeas = [0, 0]
208# for extension to retrieval of residuals, pulls
209# self.__dataScat = [0, 0]
210
211
220 def addMeasurement(self, aMeasurement, minPrecision=0.):
221 self.__measurements.append(GblMeasurement(aMeasurement, minPrecision))
222
223
227 def hasMeasurement(self):
228 return (self.__measurements <> [])
229
230
235 return self.__measurements
236
237
284 def addScatterer(self, aScatterer):
285 self.__scatDim = aScatterer[0].shape[0] # 2 (thin) or 4 (thick)
286 self.__scatterer = [ None ] + aScatterer
287 if (aScatterer[1].ndim == 2): # full precision matrix, need to diagonalize
288 eigenVal, eigenVec = np.linalg.eigh(aScatterer[1])
289 scatTransformation = eigenVec.T
290# transform measurement
291 self.__scatterer[0] = scatTransformation
292 self.__scatterer[1] = np.dot(scatTransformation, aScatterer[0])
293 self.__scatterer[2] = eigenVal
294
295
299 def getScatDim(self):
300 return self.__scatDim
301
302
306 def getScatterer(self):
307 return self.__scatterer
308
309
316 # get scatterer input back from diagonalization
317 scatRes = np.dot(self.__scatterer[0].T, self.__scatterer[1])
318 # positional covariance from diagonalization
319 posCov = np.dot(np.dot(self.__scatterer[0][:, 2:].T, np.diag(1. / self.__scatterer[2])), self.__scatterer[0][:, 2:])
320
321 scatPrec = np.zeros((4, 4))
322 scatPrec[2:, 2:] = np.linalg.inv(posCov)
323 # diagonalize again
324 eigenVal, eigenVec = np.linalg.eigh(scatPrec)
325 scatTransformation = eigenVec.T
326 return [scatTransformation, np.dot(scatTransformation, scatRes), eigenVal]
327
328
332 def addLocals(self, derivatives):
333 if self.__measurements <> []:
334 self.__measurements[-1].addLocals(derivatives)
335
336
341 def addGlobals(self, labels, derivatives):
342 if self.__measurements <> []:
343 self.__measurements[-1].addGlobals(labels, derivatives)
344
345
349 def setLabel(self, aLabel):
350 self.__label = aLabel
351
352
356 def getLabel(self):
357 return self.__label
358
359
363 def setOffset(self, anOffset):
364 self.__offset = anOffset
365
366
370 def getOffset(self):
371 return self.__offset
372
373
377 def setType(self, aType):
378 self.__type = aType
379
380
384 def isFirst(self):
385 return (self.__type < 0)
386
387
391 def isLast(self):
392 return (self.__type > 0)
393
394# def setDataMeas(self, aIndex, aData):
395# for extension to retrieval of residuals, pulls
396# self.__dataMeas[aIndex] = aData
397
398# def setDataScat(self, aIndex, aData):
399# for extension to retrieval of residuals, pulls
400# self.__dataScat[aIndex] = aData
401
402
406 def getP2pJacobian(self):
407 return self.__p2pJacobian
408
409
413 def addPrevJacobian(self, aJacobian):
414 self.__jacobians[0] = np.linalg.inv(aJacobian)
415
416
420 def addNextJacobian(self, aJacobian):
421 self.__jacobians[1] = aJacobian
422
423
428 def getDerivatives(self, index):
429 aJacobian = self.__jacobians[index]
430 matJ = aJacobian[3:5, 3:5] # J
431 matS = aJacobian[3:5, 1:3] # S
432 vecD = aJacobian[3:5, 0:1] # d
433 if (index < 1):
434 matS = -matS
435 matW = np.linalg.inv(matS) # W = +/- S^-1
436 return matW, np.dot(matW, matJ), np.dot(matW, vecD) # W, W*J, W*d
437
438
439 def printPoint(self):
440 print " point ", self.__label, self.__offset, self.__scatDim, len(self.__measurements)
441
442#------------------------------------------------------------------------------
443
444
445
449class GblData(object):
450
451
459 def __init__(self, aLabel=0, aType=0, aValue=0., aPrec=0., aMeas=0):
460
461 self.__label = aLabel
462
463 self.__type = aType
464
465 self.__index = aMeas
466
467 self.__value = aValue
468
469 self.__precision = aPrec
470
471 self.__dwMethod = 0
472
473 self.__downWeight = 1.
474
475 self.__prediction = 0.
476
477 self.__parameters = []
478
480
482
484
485# self.__predictionVar = 0.
486
487
496 def addDerivatives(self, iRow, labDer, matDer, \
497 derLocal=None, labGlobal=None, derGlobal=None):
498 if (derLocal is not None):
499 for i in range(derLocal.shape[1]): # local derivatives
500 if (derLocal[iRow, i] != 0.):
501 self.__derivatives.append(derLocal[iRow, i])
502 self.__parameters.append(i + 1)
503
504 for i in range(len(labDer)): # curvature, offset derivatives
505 if (labDer[i] != 0 and matDer[iRow, i] != 0.):
506 self.__derivatives.append(matDer[iRow , i])
507 self.__parameters.append(labDer[i])
508
509 if (derGlobal is not None):
510 for i in range(derGlobal.shape[1]): # global derivatives
511 if (derGlobal[iRow, i] != 0.):
512 self.__globalLabels.append(labGlobal[iRow, i])
513 self.__globalDerivatives.append(derGlobal[iRow, i])
514
515
520 def addExtDerivatives(self, indexExt, derExt):
521 for i in range(len(derExt)): # external derivatives
522 if (derExt[i] != 0.):
523 self.__derivatives.append(derExt[i])
524 self.__parameters.append(indexExt[i])
525
526
530 def getMatrices(self):
531 aVector = np.array([ self.__derivatives ])
532 aMatrix = np.dot(aVector.T, aVector)
533 aValue = self.__value
534 aWeight = self.__precision * self.__downWeight
535 return self.__parameters, aValue * aVector * aWeight, aMatrix * aWeight
536
537
541 def setPrediction(self, aVector):
542 self.__prediction = 0.
543 for i in range(len(self.__parameters)):
544 self.__prediction += self.__derivatives[i] * aVector[ self.__parameters[i] - 1 ]
545
546# def setPredictionVariance(self, aMatrix):
547# '''Calculate variance of prediction for data from fit.'''
548# var(residual) = 1./precision**2 - var(prediction)
549# aBlockMatrix = aMatrix.getBlockMatrix(self.__parameters)
550# self.__predictionVar = np.dot(self.__derivatives.T, \
551# np.dot(aBlockMatrix, self.__derivatives))
552
553
557 return self.__globalLabels
558
559
564 def setDownWeighting(self, aMethod):
565 self.__dwMethod = aMethod
566 scaledResidual = abs(self.__value - self.__prediction) * math.sqrt(self.__precision)
567 if (aMethod == 1): # Tukey
568 if (scaledResidual < 4.6851):
569 aWeight = (1.0 - (scaledResidual / 4.6851) ** 2) ** 2
570 else:
571 aWeight = 0.
572 elif (aMethod == 2): # Huber
573 if (scaledResidual < 1.345):
574 aWeight = 1.
575 else:
576 aWeight = 1.345 / scaledResidual
577 elif (aMethod == 3): # Cauchy
578 aWeight = 1.0 / (1.0 + (scaledResidual / 2.3849) ** 2)
579 self.__downWeight = aWeight
580 return aWeight
581
582
588 def getChi2(self):
589 scaledResidual = abs(self.__value - self.__prediction) * math.sqrt(self.__precision)
590 Chi2 = scaledResidual ** 2
591 if (self.__dwMethod == 1): # Tukey
592 if (scaledResidual < 4.6851):
593 Chi2 = 4.6851 ** 2 / 3. * (1. - (1. - (scaledResidual / 4.6851) ** 2) ** 3)
594 else:
595 Chi2 = 4.6851 ** 2 / 3.
596 elif (self.__dwMethod == 2): # Huber
597 if (scaledResidual > 1.345):
598 Chi2 = 1.345 * (2.*scaledResidual - 1.345)
599 elif (self.__dwMethod == 3): # Cauchy
600 Chi2 = math.log(1. + (scaledResidual / 2.3849) ** 2) * 2.3849 ** 2
601 return Chi2
602
603
607 def getLabel(self):
608 return self.__label
609
610
614 def getType(self):
615 return self.__type
616
617
621 def getIndex(self):
622 return self.__index
623
624
628 def getResidual(self):
629 return self.__value - self.__prediction, 1.0 / self.__precision, self.__downWeight, self.__parameters, self.__derivatives
630
631
635 def toRecord(self):
636 return self.__value, self.__precision, self.__parameters, self.__derivatives, \
638
639
643 def fromRecord(self, dataList):
644 self.__value, self.__precision, self.__parameters, self.__derivatives, \
645 self.__globalLabels, self.__globalDerivatives = dataList
646
647
653 def analyzeData(self, maxBand):
654 maxPar = self.__parameters[-1]
655 maxBor = 0
656 for i in self.__parameters:
657 if (i < maxPar - maxBand):
658 maxBor = i
659 return maxPar, maxBor
660
661
662 def printData(self):
663 print " measurement at label ", self.__label, " with type ", self.__type, " : ", self.__value, self.__precision
664 print " param ", self.__parameters
665 print " deriv ", self.__derivatives
666 print " global labels ", self.__globalLabels
667 print " global deriv ", self.__globalDerivatives
668
669#------------------------------------------------------------------------------
670
671
764
765
766
768class GblTrajectory(object):
769
770
775 def __init__(self, hasCurv=True, aDim=[0, 1]):
776
777 self.__numPoints = 0
778
780
781 self.__numCurvature = (1 if hasCurv else 0)
782
784
785 self.__numLocals = 0
786
788
789 self.__dimensions = aDim
790
791 self.__points = []
792
793 self.__data = []
794
795 self.__externalSeed = None
796
798
800
802
803
808 def addPoint(self, point):
809 self.__numPoints += 1
810 label = self.__numPoints
811 point.setLabel(label)
812 self.__points.append(point)
813 for m in point.getMeasurements():
814 self.__numLocals = max(self.__numLocals, m.getNumLocals())
815 return label
816
817
821 def getNumPoints(self):
822 return self.__numPoints
823
824
828 def getNumbers(self):
829 return self.__numLocals, self.__numCurvature, self.__numParameters
830
831
836 def addExternalSeed(self, aLabel, aSeed):
837 self.__externalPoint = aLabel
838 self.__externalSeed = aSeed
839
840
841 def printPoints(self):
842 print "GblPoints"
843 for p in self.__points:
844 p.printPoint()
845
846
847 def printData(self):
848 print "GblData blocks"
849 for d in self.__data:
850 d.printData()
851
852
856 def getData(self):
857 return self.__data
858
859
864 def milleOut(self, aFile, doublePrec=False):
865 rec = MilleRecord(doublePrec)
866# data measurements and kinks
867 for aData in self.__data:
868 rec.addData(aData.toRecord())
869
870 rec.writeRecord(aFile)
871
872
876 def milleIn(self, aFile):
877 rec = MilleRecord()
878 rec.readRecord(aFile)
879 mPar = 0
880 mBor = 0
881 mBand = 4 * len(self.__dimensions) - 1 # max band width
882 while (rec.moreData()):
883 aTag = rec.specialDataTag()
884 if (aTag < 0):
885# get data
886 aData = GblData()
887 aData.fromRecord(rec.getData())
888 self.__data.append(aData)
889 nPar, nBor = aData.analyzeData(mBand)
890 mPar = max(mPar, nPar)
891 mBor = max(mBor, nBor)
892
893 self.__numParameters = mPar
894 self.__numLocals = mBor - self.__numCurvature
895
896
901 def __getJacobian(self, aLabel):
902 aDim = self.__dimensions
903 nDim = len(aDim)
904 anIndex = abs(aLabel) - 1
905# check consistency of (index, direction)
906 if (aLabel > 0):
907 nJacobian = 1
908 if (anIndex >= self.__numPoints - 1):
909 anIndex = self.__numPoints - 1
910 nJacobian = 0
911 else:
912 nJacobian = 0
913 if (anIndex <= 0):
914 anIndex = 0
915 nJacobian = 1
916# Jacobian broken lines (q/p,..,u_i,u_i+1..) to local (q/p,u',u) parameters
917 nCurv = self.__numCurvature
918 nLocals = self.__numLocals
919 nBorder = nCurv + nLocals
920 nParBRL = nBorder + 2 * nDim
921 nParLoc = nLocals + 5
922 aJacobian = np.zeros((nParLoc, nParBRL))
923 aPoint = self.__points[anIndex]
924 anIndex = []
925 labDer, matDer = self.__getFitToLocalJacobian(aPoint, 5, nJacobian)
926# from local parameters
927 for i in range(nLocals):
928 aJacobian[i + 5, i] = 1.0;
929 anIndex.append(i + 1);
930
931# from trajectory parameters
932 iCol = nLocals;
933 for i in range(5):
934 if (labDer[i] > 0):
935 anIndex.append(labDer[i]);
936 for j in range(5):
937 aJacobian[j, iCol] = matDer[j, i];
938 iCol += 1
939
940 return anIndex, aJacobian
941
942
952 def __getFitToLocalJacobian(self, aPoint, measDim, nJacobian=1):
953 aDim = self.__dimensions
954 nDim = len(aDim)
955 nCurv = self.__numCurvature
956 nLocals = self.__numLocals
957 nOffset = aPoint.getOffset()
958 scatDim = aPoint.getScatDim()
959 anIndex = [0, 0, 0, 0, 0]
960 aJacobian = np.zeros((measDim, 5))
961 labOffset = measDim - 2
962 labSlope = measDim - 4
963 labCurv = measDim - 5
964
965 if (nOffset < 0): # need interpolation
966 prevW, prevWJ, prevWd = aPoint.getDerivatives(0) # W-, W- * J-, W- * d-
967 nextW, nextWJ, nextWd = aPoint.getDerivatives(1) # W+, W+ * J+, W+ * d+
968 sumWJ = prevWJ + nextWJ
969 matN = np.linalg.inv(sumWJ) # N = (W- * J- + W+ * J+)^-1
970# local offset
971 if (labOffset >= 0):
972# derivatives for u_int
973 prevNW = np.dot(matN, prevW) # N * W-
974 nextNW = np.dot(matN, nextW) # N * W+
975 prevNd = np.dot(matN, prevWd) # N * W- * d-
976 nextNd = np.dot(matN, nextWd) # N * W+ * d+
977 iOff = nDim * (-nOffset - 1) + nLocals + nCurv + 1 # first offset ('i' in u_i)
978 if (nCurv > 0):
979 aJacobian[labOffset:measDim, 0:1] = -prevNd - nextNd # from curvature
980 anIndex[0] = nLocals + 1
981 aJacobian[labOffset:measDim, 1:3] = prevNW
982 aJacobian[labOffset:measDim, 3:5] = nextNW
983 for i in range(nDim):
984 anIndex[1 + aDim[i]] = iOff + i
985 anIndex[3 + aDim[i]] = iOff + nDim + i
986# local slope
987 if (labSlope >= 0):
988# derivatives for u'_int
989 prevWPN = np.dot(nextWJ, prevNW) # W+ * J+ * N * W-
990 nextWPN = np.dot(prevWJ, nextNW) # W- * J- * N * W+
991 prevWNd = np.dot(nextWJ, prevNd) # W+ * J+ * N * W- * d-
992 nextWNd = np.dot(prevWJ, nextNd) # W- * J- * N * W+ * d+
993 if (nCurv > 0):
994 aJacobian[labSlope:labOffset, 0:1] = prevWNd - nextWNd # from curvature
995 aJacobian[labSlope:labOffset, 1:3] = -prevWPN
996 aJacobian[labSlope:labOffset, 3:5] = nextWPN
997
998 else: # at point
999# anIndex must be sorted
1000# forward : iOff2 = iOff1 + nDim, index1 = 1, index2 = 3
1001# backward: iOff2 = iOff1 - nDim, index1 = 3, index2 = 1
1002 # adjust for thick scatterer
1003 if (scatDim == 4):
1004 nOffset += 1
1005 iOff1 = nDim * nOffset + nCurv + nLocals + 1 # first offset ('i' in u_i)
1006 index1 = 3 - 2 * nJacobian # index of first offset
1007 iOff2 = iOff1 + nDim * (nJacobian * 2 - 1) # second offset ('i' in u_i)
1008 index2 = 1 + 2 * nJacobian # index of second offset
1009# local offset
1010 if (labOffset >= 0):
1011 aJacobian[labOffset, index1] = 1.0 # from 1st Offset
1012 aJacobian[labOffset + 1, index1 + 1] = 1.0
1013 for i in range(nDim):
1014 anIndex[index1 + aDim[i]] = iOff1 + i
1015# local slope and curvature
1016 if (labSlope >= 0):
1017 matW, matWJ, vecWd = aPoint.getDerivatives(nJacobian) # W, W * J, W * d
1018 sign = 2 * nJacobian - 1
1019 if (nCurv > 0):
1020 aJacobian[labSlope:labOffset, 0:1] = -sign * vecWd # from curvature
1021 anIndex[0] = nLocals + 1
1022 aJacobian[labSlope:labOffset, index1:index1 + 2] = -sign * matWJ
1023 aJacobian[labSlope:labOffset, index2:index2 + 2] = sign * matW
1024 for i in range(nDim):
1025 anIndex[index2 + aDim[i]] = iOff2 + i
1026
1027# local curvature
1028 if (labCurv >= 0):
1029 if (nCurv > 0):
1030 aJacobian[labCurv, labCurv] = 1.0
1031
1032 return anIndex, aJacobian
1033
1034
1042 def __getFitToKinkJacobian(self, aPoint):
1043 aDim = self.__dimensions
1044 nDim = len(aDim)
1045 nCurv = self.__numCurvature
1046 nLocals = self.__numLocals
1047 nOffset = aPoint.getOffset()
1048 anIndex = [0, 0, 0, 0, 0, 0, 0]
1049 aJacobian = np.zeros((2, 7))
1050
1051 prevW, prevWJ, prevWd = aPoint.getDerivatives(0) # W-, W- * J-, W- * d-
1052 nextW, nextWJ, nextWd = aPoint.getDerivatives(1) # W+, W+ * J+, W+ * d+
1053 sumWJ = prevWJ + nextWJ # W- * J- + W+ * J+
1054 sumWd = prevWd + nextWd # W+ * d+ + W- * d-
1055 iOff = (nOffset - 1) * nDim + nCurv + nLocals + 1 # first offset ('i' in u_i)
1056
1057# local offset
1058 if (nCurv > 0):
1059 aJacobian[:, 0:1] = -sumWd # from curvature
1060 anIndex[0] = nLocals + 1
1061 aJacobian[:, 1:3] = prevW # from 1st Offset
1062 aJacobian[:, 3:5] = -sumWJ # from 2nd Offset
1063 aJacobian[:, 5:7] = nextW # from 3rd Offset
1064 for i in range(nDim):
1065 anIndex[1 + aDim[i]] = iOff + i
1066 anIndex[3 + aDim[i]] = iOff + nDim + i
1067 anIndex[5 + aDim[i]] = iOff + nDim * 2 + i
1068
1069 return anIndex, aJacobian
1070
1071
1081 def __getFitToStepJacobian(self, aPoint):
1082 aDim = self.__dimensions
1083 nDim = len(aDim)
1084 nCurv = self.__numCurvature
1085 nLocals = self.__numLocals
1086 nOffset = aPoint.getOffset()
1087 anIndex = [0, 0, 0, 0]
1088 aJacobian = np.zeros((4, 4))
1089
1090 iOff = (nOffset - 1) * nDim + nCurv + nLocals + 1 # first offset ('i' in u_i)
1091
1092# step
1093 aJacobian[2:,:2] = -np.eye(2) # from 2nd Offset
1094 aJacobian[2:, 2:] = np.eye(2) # from 3rd Offset
1095
1096 for i in range(nDim):
1097 anIndex[aDim[i]] = iOff + nDim + i
1098 anIndex[2 + aDim[i]] = iOff + nDim * 2 + i
1099
1100 return anIndex, aJacobian
1101
1102
1113 aDim = self.__dimensions
1114 nDim = len(aDim)
1115 nCurv = self.__numCurvature
1116 nLocals = self.__numLocals
1117 nOffset = aPoint.getOffset()
1118 anIndex = [0, 0, 0, 0, 0, 0, 0, 0, 0]
1119 aJacobian = np.zeros((4, 9))
1120
1121 prevW, prevWJ, prevWd = aPoint.getDerivatives(0) # W-, W- * J-, W- * d-
1122 nextW, nextWJ, nextWd = aPoint.getDerivatives(1) # W+, W+ * J+, W+ * d+
1123 iOff = (nOffset - 1) * nDim + nCurv + nLocals + 1 # first offset ('i' in u_i)
1124
1125# kink
1126 if (nCurv > 0):
1127 aJacobian[:2, 0:1] = -(prevWd + nextWd) # from curvature
1128 anIndex[0] = nLocals + 1
1129 aJacobian[:2, 1:3] = prevW # from 1st Offset
1130 aJacobian[:2, 3:5] = -prevWJ # from 2nd Offset
1131 aJacobian[:2, 5:7] = -nextWJ # from 3rd Offset
1132 aJacobian[:2, 7:9] = nextW # from 4th Offset
1133# step
1134 aJacobian[2:, 3:5] = -np.eye(2) # from 2nd Offset
1135 aJacobian[2:, 5:7] = np.eye(2) # from 3rd Offset
1136
1137 for i in range(nDim):
1138 anIndex[1 + aDim[i]] = iOff + i
1139 anIndex[3 + aDim[i]] = iOff + nDim + i
1140 anIndex[5 + aDim[i]] = iOff + nDim * 2 + i
1141 anIndex[7 + aDim[i]] = iOff + nDim * 3 + i
1142
1143 return anIndex, aJacobian
1144
1145 def __getResAndErr(self, aData, used=True):
1146 aResidual, aMeasVar, aDownWeight, indLocal, derLocal = self.__data[aData].getResidual()
1147 aVec = np.array(derLocal) # compressed vector
1148 aMat = self.__matrix.getBlockMatrix(indLocal) # compressed matrix
1149 aFitVar = np.dot(aVec, np.dot(aMat, aVec.T)) # variance from track fit
1150 aFitVar *= aDownWeight # account for down-weighting (of measurement in fit)
1151 aMeasError = math.sqrt(aMeasVar) # error of measurement
1152 if used:
1153 aResError = math.sqrt(aMeasVar - aFitVar) if aFitVar < aMeasVar else 0. # error of biased residual
1154 else:
1155 aResError = math.sqrt(aMeasVar + aFitVar) # error of unbiased residual
1156 return aResidual, aMeasError, aResError, aDownWeight
1157
1158
1167 def getResults(self, aLabel):
1168 anIndex, aJacobian = self.__getJacobian(aLabel)
1169 nParBRL = len(anIndex)
1170 aVec = np.empty(nParBRL)
1171 for i in range(nParBRL):
1172 aVec[i] = self.__vector[anIndex[i] - 1] # compressed vector
1173 aMat = self.__matrix.getBlockMatrix(anIndex) # compressed matrix
1174 locPar = np.dot(aJacobian, aVec)
1175 locCov = np.dot(aJacobian, np.dot(aMat, aJacobian.T))
1176 return locPar, locCov
1177
1178
1185 def getMeasResults(self, aLabel):
1186 firstData = self.__measDataIndex[aLabel - 1] # first data block with measurement
1187 numData = self.__measDataIndex[aLabel] - firstData # number of data blocks
1188 if numData <= 0:
1189 return 0, None, None, None, None
1190 #
1191 aResiduals = np.empty(numData)
1192 aMeasErr = np.empty(numData)
1193 aResErr = np.empty(numData)
1194 aDownWeight = np.empty(numData)
1195 for i in range(numData):
1196 aResiduals[i], aMeasErr[i], aResErr[i], aDownWeight[i] = self.__getResAndErr(firstData + i, (aLabel <> self.__skippedMeasLabel))
1197 return numData, aResiduals, aMeasErr, aResErr, aDownWeight
1198
1199
1206 def getScatResults(self, aLabel):
1207 firstData = self.__scatDataIndex[aLabel - 1] # first data block with measurement
1208 numData = self.__scatDataIndex[aLabel] - firstData # number of data blocks
1209 if numData <= 0:
1210 return 0, None, None, None, None
1211 #
1212 aResiduals = np.empty(numData)
1213 aMeasErr = np.empty(numData)
1214 aResErr = np.empty(numData)
1215 aDownWeight = np.empty(numData)
1216 for i in range(numData):
1217 aResiduals[i], aMeasErr[i], aResErr[i], aDownWeight[i] = self.__getResAndErr(firstData + i)
1218 return numData, aResiduals, aMeasErr, aResErr, aDownWeight
1219
1220
1224 def getResidual(self, iData):
1225 return self.__getResAndErr(iData)
1226
1227
1233 def fit(self, optionList="", aLabel=0):
1234
1235
1236 def defineOffsets():
1237# set labels for previous/next offsets
1238# first point is offset
1239 self.__points[0].setOffset(0)
1240 self.__points[0].setType(-1)
1241 nOffsets = 1
1242# intermediate scatterers are offsets
1243 for aPoint in self.__points[1:-1]:
1244 scatDim = aPoint.getScatDim()
1245 if (scatDim > 0):
1246 aPoint.setOffset(nOffsets)
1247 nOffsets += 1
1248 # thick scatterer ?
1249 if (scatDim == 4):
1250 nOffsets += 1
1251 else:
1252 aPoint.setOffset(-nOffsets)
1253# last point is offset
1254 self.__points[-1].setOffset(nOffsets)
1255 self.__points[-1].setType(1)
1256 if (self.__points[-1].getScatDim() == 4):
1257 # thick scatterer
1258 nOffsets += 1
1259 self.__numOffsets = nOffsets + 1
1260 self.__numParameters = self.__numOffsets * len(self.__dimensions) \
1261 +self.__numCurvature + self.__numLocals
1262
1263
1264 def calcJacobians():
1265 scatJacobian = np.empty((5, 5))
1266# forward propagation (all)
1267 lastPoint = 0;
1268 numStep = 0;
1269 for iPoint in range(1, self.__numPoints):
1270 if (numStep == 0):
1271 scatJacobian = self.__points[iPoint].getP2pJacobian()
1272 else:
1273 scatJacobian = np.dot(self.__points[iPoint].getP2pJacobian(), scatJacobian)
1274 numStep += 1
1275 self.__points[iPoint].addPrevJacobian(scatJacobian) # aPoint -> previous scatterer
1276 if (self.__points[iPoint].getOffset() >= 0):
1277 self.__points[lastPoint].addNextJacobian(scatJacobian) # lastPoint -> next scatterer
1278 numStep = 0;
1279 lastPoint = iPoint
1280# backward propagation (without scatterers)
1281 for iPoint in range(self.__numPoints - 1, 0, -1):
1282 if (self.__points[iPoint].getOffset() >= 0):
1283 scatJacobian = self.__points[iPoint].getP2pJacobian()
1284 continue # skip offsets
1285 self.__points[iPoint].addNextJacobian(scatJacobian) # iPoint -> next scatterer
1286 scatJacobian = np.dot(scatJacobian, self.__points[iPoint].getP2pJacobian())
1287
1288
1289 def prepare():
1290 aDim = self.__dimensions
1291# measurements
1292 self.__measDataIndex.append(len(self.__data)) # offset
1293 for aPoint in self.__points:
1294 if (aPoint.hasMeasurement()):
1295 nLabel = aPoint.getLabel()
1296 measIndex = 0
1297 for m in aPoint.getMeasurements():
1298 measIndex += 1
1299 measDim = m.getMeasDim()
1300 measPrecision = m.getMeasMinPrec()
1301 localDer = m.getLocalDerivatives()
1302 globalLab = m.getGlobalLabels()
1303 globalDer = m.getGlobalDerivatives()
1304 matP, aMeas, aPrec = m.getMeasurement()
1305 nJacobian = 1 if aPoint.getOffset() < self.__numOffsets - 1 else 0 # last point needs backward propagation
1306 labDer, matDer = self.__getFitToLocalJacobian(aPoint, measDim, nJacobian)
1307 matPDer = matDer if matP is None else np.dot(matP, matDer)
1308 for i in range(measDim):
1309 if (aPrec[i] > measPrecision):
1310 aData = GblData(nLabel, 1, aMeas[i], aPrec[i], measIndex)
1311 aData.addDerivatives(i, labDer, matPDer, localDer, \
1312 globalLab, globalDer)
1313 self.__data.append(aData)
1314 self.__measDataIndex.append(len(self.__data))
1315# aPoint.setDataMeas(i, len(self.__data))
1316# pseudo measurements from kinks
1317 self.__scatDataIndex.append(len(self.__data)) # offset
1318 self.__scatDataIndex.append(len(self.__data)) # first point
1319 for aPoint in self.__points[1:]:
1320 scatDim = aPoint.getScatDim()
1321 if (scatDim > 0):
1322 nLabel = aPoint.getLabel()
1323 if (scatDim == 4):
1324 # thick scatterer, last point?
1325 if (aPoint.isLast()):
1326 # steps (only)
1327 matT, aMeas, aPrec = aPoint.getReducedScatterer()
1328 labDer, matDer = self.__getFitToStepJacobian(aPoint)
1329 else:
1330 # kinks+steps
1331 matT, aMeas, aPrec = aPoint.getScatterer()
1332 labDer, matDer = self.__getFitToKinkAndStepJacobian(aPoint)
1333 else:
1334 # thin scatterer, last point?
1335 if (aPoint.isLast()):
1336 continue
1337 else:
1338 # kinks
1339 matT, aMeas, aPrec = aPoint.getScatterer()
1340 labDer, matDer = self.__getFitToKinkJacobian(aPoint)
1341 matTDer = matDer if matT is None else np.dot(matT, matDer)
1342 for i in aDim:
1343 if (aPrec[i] > 0.):
1344 aData = GblData(nLabel, 2, aMeas[i], aPrec[i])
1345 aData.addDerivatives(i, labDer, matTDer)
1346 self.__data.append(aData)
1347 # thick scatterer?
1348 if (scatDim == 4):
1349 for i in aDim:
1350 if (aPrec[i + 2] > 0.):
1351 aData = GblData(nLabel, 2, aMeas[i + 2], aPrec[i + 2])
1352 aData.addDerivatives(i + 2, labDer, matTDer)
1353 self.__data.append(aData)
1354 self.__scatDataIndex.append(len(self.__data))
1355# aPoint.setDataScat(i, len(self.__data))
1356 self.__scatDataIndex.append(len(self.__data)) # last point
1357# external seed
1358 if (self.__externalPoint != 0):
1359 externalIndex, aJacobian = self.__getJacobian(self.__externalPoint)
1360 eigenVal, eigenVec = np.linalg.eigh(self.__externalSeed)
1361 aMatrix = np.dot(eigenVec.T, aJacobian)
1362 for i in range(len(eigenVec)):
1363 if (eigenVal[i] > 0.):
1364 externalDerivatives = []
1365 for j in range(len(externalIndex)):
1366 externalDerivatives.append(aMatrix[i, j])
1367 aData = GblData(self.__externalPoint, 3, 0., eigenVal[i])
1368 aData.addExtDerivatives(externalIndex, externalDerivatives)
1369 self.__data.append(aData)
1370 self.__measDataIndex.append(len(self.__data))
1371
1372
1373 def buildLinearEquationSystem():
1374 nBorder = self.__numCurvature + self.__numLocals
1376 self.__vector = np.zeros(self.__numParameters)
1377 for aData in self.__data:
1378 # skipped (internal) measurement?
1379 if aData.getLabel() == self.__skippedMeasLabel and aData.getType() == 1:
1380 continue
1381 index, aVector, aMatrix = aData.getMatrices()
1382 for i in range(len(index)):
1383 self.__vector[ index[i] - 1 ] += aVector[0, i] # update vector
1384 self.__matrix.addBlockMatrix(index, aMatrix) # update matrix
1385
1386
1391 def downWeight(aMethod):
1392 aLoss = 0.
1393 for aData in self.__data:
1394 aLoss += (1. - aData.setDownWeighting(aMethod))
1395 return aLoss
1396
1397
1398 def predict():
1399 for aData in self.__data:
1400 aData.setPrediction(self.__vector)
1401
1402 if (self.__data == []): # generate data from points
1403 defineOffsets()
1404 calcJacobians()
1405 prepare()
1406
1407 # skip measurements from point?
1408 self.__skippedMeasLabel = aLabel
1409
1410 buildLinearEquationSystem() # create linear equations system from data
1411 #
1412 try:
1413 aMethod = 0
1414 lostWeight = 0.
1415 self.__vector = self.__matrix.solveAndInvertBorderedBand(self.__vector)
1416 predict()
1417
1418 for o in optionList: # down weighting iterations
1419 try:
1420 aMethod = "THC".index(o.upper()) + 1
1421 lostWeight = downWeight(aMethod)
1422 buildLinearEquationSystem()
1423 self.__vector = self.__matrix.solveAndInvertBorderedBand(self.__vector)
1424 predict()
1425 except ValueError:
1426 pass
1427
1428 Ndf = -self.__numParameters
1429 Chi2 = 0.
1430 for aData in self.__data:
1431 # skipped (internal) measurement?
1432 if aData.getLabel() == self.__skippedMeasLabel and aData.getType() == 1:
1433 continue
1434 Chi2 += aData.getChi2()
1435 Ndf += 1
1436 Chi2 /= [1.0, 0.8737, 0.9326, 0.8228 ][aMethod]
1437 return Chi2, Ndf, lostWeight #, self.__matrix.getBandCond()
1438
1439 except (ZeroDivisionError, np.linalg.linalg.LinAlgError):
1440 return 0., -1, 0. #, 0.
1441
Data (block) containing value, precision and derivatives for measurements, kinks and external seed.
Definition: gblfit.py:449
def toRecord(self)
Get data components (for copying to MP binaty record)
Definition: gblfit.py:635
__downWeight
down weighting factor (M-estimators); float
Definition: gblfit.py:473
def fromRecord(self, dataList)
Set data components (from MP binaty record)
Definition: gblfit.py:643
__dwMethod
down weighting method; int
Definition: gblfit.py:471
def printData(self)
Print data.
Definition: gblfit.py:662
def setDownWeighting(self, aMethod)
Outlier down weighting with M-estimators.
Definition: gblfit.py:564
__value
value (residual or kink); float
Definition: gblfit.py:467
__prediction
prediction (for value from fit); float
Definition: gblfit.py:475
def getMatrices(self)
Calculate compressed matrix and right hand side from data.
Definition: gblfit.py:530
def getResidual(self)
Get data for residual (and errors).
Definition: gblfit.py:628
__label
label of corresponding point; int
Definition: gblfit.py:461
__derivatives
derivatives (prediction vs fit parameters); list(float)
Definition: gblfit.py:479
def addExtDerivatives(self, indexExt, derExt)
Add derivatives to data (block) from external seed.
Definition: gblfit.py:520
def __init__(self, aLabel=0, aType=0, aValue=0., aPrec=0., aMeas=0)
Create new data.
Definition: gblfit.py:459
def addDerivatives(self, iRow, labDer, matDer, derLocal=None, labGlobal=None, derGlobal=None)
Add derivatives to data (block) from measurements and kinks.
Definition: gblfit.py:497
__parameters
labels of fit parameters (with non zero derivative); list(int)
Definition: gblfit.py:477
def setPrediction(self, aVector)
Calculate prediction for data from fit.
Definition: gblfit.py:541
def getIndex(self)
Get index.
Definition: gblfit.py:621
def analyzeData(self, maxBand)
Analyze labels of fit parameters to determine number of parameters and border size with given maximal...
Definition: gblfit.py:653
__type
type of data (0: none, 1: internal measurement, 2: internal kink, 3: external seed,...
Definition: gblfit.py:463
__precision
precision (diagonal element of inverse covariance matrix); float
Definition: gblfit.py:469
__globalLabels
labels of global parameters; list(int)
Definition: gblfit.py:481
def getType(self)
Get type.
Definition: gblfit.py:614
def getChi2(self)
Calculate Chi2 (contribution) from data.
Definition: gblfit.py:588
__index
index for internal measurement
Definition: gblfit.py:465
def getGlobalLabels(self)
Get global labels.
Definition: gblfit.py:556
__globalDerivatives
derivatives (prediction vs global parameters); list(float)
Definition: gblfit.py:483
def getLabel(self)
Get Label.
Definition: gblfit.py:607
User supplied measurement at point on (initial) trajectory.
Definition: gblfit.py:44
def getLocalDerivatives(self)
Get local derivatives.
Definition: gblfit.py:151
def printMeasurement(self)
Print Measurement.
Definition: gblfit.py:168
__measDim
dimension of measurement (2D, 4D or 5D); int
Definition: gblfit.py:59
def getNumLocals(self)
Get number of local derivatives.
Definition: gblfit.py:141
def getMeasurement(self)
Retrieve measurement of a point.
Definition: gblfit.py:96
__globalDerivatives
global derivatives; matrix(float)
Definition: gblfit.py:69
__measurement
measurement at point: projection (dm/du), residuals (to initial trajectory), precision; list(matrix(f...
Definition: gblfit.py:57
__localDerivatives
local derivatives; matrix(float)
Definition: gblfit.py:65
def getGlobalLabels(self)
Get global labels.
Definition: gblfit.py:157
def getGlobalDerivatives(self)
Get global derivatives.
Definition: gblfit.py:164
def addGlobals(self, labels, derivatives)
Add global derivatives.
Definition: gblfit.py:129
def addLocals(self, derivatives)
Add local derivatives.
Definition: gblfit.py:117
def hasMeasurement(self)
Check point for a measurement.
Definition: gblfit.py:88
def getMeasDim(self)
Retrieve dimension of measurement of a point.
Definition: gblfit.py:103
def __init__(self, aMeasurement, minPrecision=0.)
Create new measurement.
Definition: gblfit.py:55
__measMinPrec
minimal precision to accept measurement <
Definition: gblfit.py:61
__globalLabels
global labels; matrix(int)
Definition: gblfit.py:67
def getMeasMinPrec(self)
Retrieve minimal precision to accept measurement.
Definition: gblfit.py:110
__measTransformation
transformation (to eigen-vectors of precision matrix); matrix(float)
Definition: gblfit.py:63
User supplied point on (initial) trajectory.
Definition: gblfit.py:182
__jacobians
jacobians for propagation to previous or next point with offsets; pair(matrix(float))
Definition: gblfit.py:198
__p2pJacobian
Point-to-point jacobian from previous point; matrix(float)
Definition: gblfit.py:196
def getScatDim(self)
Get scatterer dimension.
Definition: gblfit.py:299
def isFirst(self)
Is first point?
Definition: gblfit.py:384
def getMeasurements(self)
Retrieve measurements of a point.
Definition: gblfit.py:234
def addPrevJacobian(self, aJacobian)
Add jacobian to previous offset.
Definition: gblfit.py:413
def isLast(self)
Is last point?
Definition: gblfit.py:391
__scatterer
scatterer at point: (transformation or None,) initial kinks, precision (inverse covariance matrix); l...
Definition: gblfit.py:204
def getScatterer(self)
Retrieve scatterer of a point.
Definition: gblfit.py:306
def addGlobals(self, labels, derivatives)
Add global derivatives (to last measurement).
Definition: gblfit.py:341
def printPoint(self)
Print point.
Definition: gblfit.py:439
def setLabel(self, aLabel)
Define label of a point.
Definition: gblfit.py:349
def getLabel(self)
Retrieve label of a point.
Definition: gblfit.py:356
def hasMeasurement(self)
Check point for a measurement.
Definition: gblfit.py:227
__type
type (-1: first, 0: inner, 1: last)
Definition: gblfit.py:194
def getReducedScatterer(self)
Retrieve (reduced) scatterer of a point.
Definition: gblfit.py:315
def __init__(self, aJacobian)
Create new point.
Definition: gblfit.py:188
def addLocals(self, derivatives)
Add local derivatives (to last measurement).
Definition: gblfit.py:332
def getOffset(self)
Get offset of a point.
Definition: gblfit.py:370
def setOffset(self, anOffset)
Define offset of a point and references to previous and next point with offset.
Definition: gblfit.py:363
def addNextJacobian(self, aJacobian)
Add jacobian to next offset.
Definition: gblfit.py:420
def addScatterer(self, aScatterer)
Add a (thin or thick) scatterer to a point.
Definition: gblfit.py:284
def getP2pJacobian(self)
Retrieve jacobian of a point.
Definition: gblfit.py:406
__scatDim
scatterer dimension (valid 2(thin) or 4(thick))
Definition: gblfit.py:202
def getDerivatives(self, index)
Get derivatives for locally linearized track model (backward or forward propagation).
Definition: gblfit.py:428
def addMeasurement(self, aMeasurement, minPrecision=0.)
Add a mesurement to a point.
Definition: gblfit.py:220
__measurements
measurements at point; list(GblMeasurement)
Definition: gblfit.py:200
__label
label for referencing point (0,1,..,number(points)-1); int
Definition: gblfit.py:190
def setType(self, aType)
Define type of a point.
Definition: gblfit.py:377
__offset
>=0: offset number at point, <0: offset number at next point with offset; int
Definition: gblfit.py:192
General Broken Lines Trajectory.
Definition: gblfit.py:768
__externalPoint
label of point with external seed; int
Definition: gblfit.py:787
def getResidual(self, iData)
Get residuals from data of trajectory.
Definition: gblfit.py:1224
def getNumPoints(self)
Get number of points on trajectory.
Definition: gblfit.py:821
__numCurvature
'curvature' is fit parameter (=1); int
Definition: gblfit.py:781
def milleOut(self, aFile, doublePrec=False)
Write (data blocks of) trajectory to MP (binary) file (as float or double values).
Definition: gblfit.py:864
__skippedMeasLabel
label of point with measurements skipped in fit (for unbiased residuals)
Definition: gblfit.py:801
def getNumbers(self)
Get (parameter) number of trajectory.
Definition: gblfit.py:828
__dimensions
active components of offsets (both ([0,1]) or single ([0] or [1]); list(int)
Definition: gblfit.py:789
def milleIn(self, aFile)
Read (data blocks of) trajectory from MP (binary) file.
Definition: gblfit.py:876
def addPoint(self, point)
Add point to trajectory.
Definition: gblfit.py:808
__measDataIndex
mapping points to data blocks from measurements; list(int)
Definition: gblfit.py:797
def __getFitToStepJacobian(self, aPoint)
Get jacobian for transformation from (trajectory) fit to step parameters at point.
Definition: gblfit.py:1081
__numParameters
number fit parameters; int
Definition: gblfit.py:783
def __init__(self, hasCurv=True, aDim=[0, 1])
Create new trajectory.
Definition: gblfit.py:775
__numLocals
number of local parameters; int
Definition: gblfit.py:785
__numPoints
number of points on trajectory; int
Definition: gblfit.py:777
def printData(self)
Print data of trajectory.
Definition: gblfit.py:847
__points
points on trajectory; list(GblPoint)
Definition: gblfit.py:791
def getScatResults(self, aLabel)
Get residuals at point from scatterer.
Definition: gblfit.py:1206
def __getFitToKinkJacobian(self, aPoint)
Get jacobian for transformation from (trajectory) fit to kink parameters at point.
Definition: gblfit.py:1042
__externalSeed
external seed (for local, fit parameters); matrix(float)
Definition: gblfit.py:795
def getData(self)
Get data of trajectory.
Definition: gblfit.py:856
def __getJacobian(self, aLabel)
Get jacobian for transformation from fit to track parameters at point.
Definition: gblfit.py:901
__matrix
Calculate Jacobians to previous/next scatterer from point to point ones.
Definition: gblfit.py:1375
def fit(self, optionList="", aLabel=0)
Perform fit of trajectory.
Definition: gblfit.py:1233
__numOffsets
number of (points with) offsets on trajectory; int
Definition: gblfit.py:779
def printPoints(self)
Print points of trajectory.
Definition: gblfit.py:841
def addExternalSeed(self, aLabel, aSeed)
Add external seed to trajectory.
Definition: gblfit.py:836
__data
data (blocks) of trajectory; list(GblData)
Definition: gblfit.py:793
def __getFitToKinkAndStepJacobian(self, aPoint)
Get jacobian for transformation from (trajectory) fit to kink and step parameters at point.
Definition: gblfit.py:1112
def __getResAndErr(self, aData, used=True)
Definition: gblfit.py:1145
__scatDataIndex
mapping points to data blocks from scatterers; list(int)
Definition: gblfit.py:799
def getMeasResults(self, aLabel)
Get residuals at point from measurement.
Definition: gblfit.py:1185
def __getFitToLocalJacobian(self, aPoint, measDim, nJacobian=1)
Get (part of) jacobian for transformation from (trajectory) fit to track parameters at point.
Definition: gblfit.py:952
def getResults(self, aLabel)
Get results (corrections, covarinace matrix) at point in forward or backward direction.
Definition: gblfit.py:1167
(Symmetric) Bordered Band Matrix.
Definition: gblnum.py:65
Millepede-II (binary) record.
Definition: mille.py:76