@@ -705,9 +705,6 @@ def chisqfunc_compact(d):
705
705
706
706
def _combined_fit (x , y , func , silent = False , ** kwargs ):
707
707
708
- if kwargs .get ('correlated_fit' ) is True :
709
- raise Exception ("Correlated fit has not been implemented yet" )
710
-
711
708
output = Fit_result ()
712
709
output .fit_function = func
713
710
@@ -735,6 +732,9 @@ def _combined_fit(x, y, func, silent=False, **kwargs):
735
732
if len (x_all .shape ) > 2 :
736
733
raise Exception ('Unknown format for x values' )
737
734
735
+ if np .any (np .asarray (dy_f ) <= 0.0 ):
736
+ raise Exception ('No y errors available, run the gamma method first.' )
737
+
738
738
# number of fit parameters
739
739
n_parms_ls = []
740
740
for key in key_ls :
@@ -766,6 +766,22 @@ def _combined_fit(x, y, func, silent=False, **kwargs):
766
766
else :
767
767
x0 = [0.1 ] * n_parms
768
768
769
+ if kwargs .get ('correlated_fit' ) is True :
770
+ corr = covariance (y_all , correlation = True , ** kwargs )
771
+ covdiag = np .diag (1 / np .asarray (dy_f ))
772
+ condn = np .linalg .cond (corr )
773
+ if condn > 0.1 / np .finfo (float ).eps :
774
+ raise Exception (f"Cannot invert correlation matrix as its condition number exceeds machine precision ({ condn :1.2e} )" )
775
+ if condn > 1e13 :
776
+ warnings .warn ("Correlation matrix may be ill-conditioned, condition number: {%1.2e}" % (condn ), RuntimeWarning )
777
+ chol = np .linalg .cholesky (corr )
778
+ chol_inv = scipy .linalg .solve_triangular (chol , covdiag , lower = True )
779
+
780
+ def chisqfunc_corr (p ):
781
+ model = np .concatenate ([np .array (func [key ](p , np .asarray (x [key ]))) for key in key_ls ])
782
+ chisq = anp .sum (anp .dot (chol_inv , (y_f - model )) ** 2 )
783
+ return chisq
784
+
769
785
def chisqfunc (p ):
770
786
func_list = np .concatenate ([[func [k ]] * len (x [k ]) for k in key_ls ])
771
787
model = anp .array ([func_list [i ](p , x_all [i ]) for i in range (len (x_all ))])
@@ -782,30 +798,46 @@ def chisqfunc(p):
782
798
if 'tol' in kwargs :
783
799
tolerance = kwargs .get ('tol' )
784
800
fit_result = iminuit .minimize (chisqfunc , x0 , tol = tolerance ) # Stopping criterion 0.002 * tol * errordef
801
+ if kwargs .get ('correlated_fit' ) is True :
802
+ fit_result = iminuit .minimize (chisqfunc_corr , fit_result .x , tol = tolerance )
785
803
output .iterations = fit_result .nfev
786
804
else :
787
805
tolerance = 1e-12
788
806
if 'tol' in kwargs :
789
807
tolerance = kwargs .get ('tol' )
790
808
fit_result = scipy .optimize .minimize (chisqfunc , x0 , method = kwargs .get ('method' ), tol = tolerance )
809
+ if kwargs .get ('correlated_fit' ) is True :
810
+ fit_result = scipy .optimize .minimize (chisqfunc_corr , fit_result .x , method = kwargs .get ('method' ), tol = tolerance )
791
811
output .iterations = fit_result .nit
792
812
793
813
chisquare = fit_result .fun
794
814
795
815
else :
816
+ if kwargs .get ('correlated_fit' ) is True :
817
+ def chisqfunc_residuals_corr (p ):
818
+ model = np .concatenate ([np .array (func [key ](p , np .asarray (x [key ]))) for key in key_ls ])
819
+ chisq = anp .dot (chol_inv , (y_f - model ))
820
+ return chisq
821
+
796
822
def chisqfunc_residuals (p ):
797
823
model = np .concatenate ([np .array (func [key ](p , np .asarray (x [key ]))) for key in key_ls ])
798
824
chisq = ((y_f - model ) / dy_f )
799
825
return chisq
826
+
800
827
if 'tol' in kwargs :
801
828
print ('tol cannot be set for Levenberg-Marquardt' )
829
+
802
830
fit_result = scipy .optimize .least_squares (chisqfunc_residuals , x0 , method = 'lm' , ftol = 1e-15 , gtol = 1e-15 , xtol = 1e-15 )
831
+ if kwargs .get ('correlated_fit' ) is True :
832
+ fit_result = scipy .optimize .least_squares (chisqfunc_residuals_corr , fit_result .x , method = 'lm' , ftol = 1e-15 , gtol = 1e-15 , xtol = 1e-15 )
803
833
804
834
chisquare = np .sum (fit_result .fun ** 2 )
805
- assert np .isclose (chisquare , chisqfunc (fit_result .x ), atol = 1e-14 )
806
- output .iterations = fit_result .nfev
835
+ if kwargs .get ('correlated_fit' ) is True :
836
+ assert np .isclose (chisquare , chisqfunc_corr (fit_result .x ), atol = 1e-14 )
837
+ else :
838
+ assert np .isclose (chisquare , chisqfunc (fit_result .x ), atol = 1e-14 )
807
839
808
- output .message = fit_result .message
840
+ output .iterations = fit_result .nfev
809
841
810
842
if not fit_result .success :
811
843
raise Exception ('The minimization procedure did not converge.' )
@@ -818,17 +850,12 @@ def chisqfunc_residuals(p):
818
850
else :
819
851
output .chisquare_by_dof = float ('nan' )
820
852
853
+ output .message = fit_result .message
821
854
if not silent :
822
855
print (fit_result .message )
823
856
print ('chisquare/d.o.f.:' , output .chisquare_by_dof )
824
857
print ('fit parameters' , fit_result .x )
825
858
826
- def chisqfunc_compact (d ):
827
- func_list = np .concatenate ([[func [k ]] * len (x [k ]) for k in key_ls ])
828
- model = anp .array ([func_list [i ](d [:n_parms ], x_all [i ]) for i in range (len (x_all ))])
829
- chisq = anp .sum (((d [n_parms :] - model ) / dy_f ) ** 2 )
830
- return chisq
831
-
832
859
def prepare_hat_matrix ():
833
860
hat_vector = []
834
861
for key in key_ls :
@@ -838,16 +865,43 @@ def prepare_hat_matrix():
838
865
hat_vector = [item for sublist in hat_vector for item in sublist ]
839
866
return hat_vector
840
867
841
- fitp = fit_result .x
868
+ if kwargs .get ('expected_chisquare' ) is True :
869
+ if kwargs .get ('correlated_fit' ) is not True :
870
+ W = np .diag (1 / np .asarray (dy_f ))
871
+ cov = covariance (y_all )
872
+ hat_vector = prepare_hat_matrix ()
873
+ A = W @ hat_vector # hat_vector = 'jacobian(func)(fit_result.x, x)'
874
+ P_phi = A @ np .linalg .pinv (A .T @ A ) @ A .T
875
+ expected_chisquare = np .trace ((np .identity (x_all .shape [- 1 ]) - P_phi ) @ W @ cov @ W )
876
+ output .chisquare_by_expected_chisquare = output .chisquare / expected_chisquare
877
+ if not silent :
878
+ print ('chisquare/expected_chisquare:' , output .chisquare_by_expected_chisquare )
842
879
880
+ fitp = fit_result .x
843
881
if np .any (np .asarray (dy_f ) <= 0.0 ):
844
882
raise Exception ('No y errors available, run the gamma method first.' )
845
883
846
884
try :
847
- hess = hessian (chisqfunc )(fitp )
885
+ if kwargs .get ('correlated_fit' ) is True :
886
+ hess = hessian (chisqfunc_corr )(fitp )
887
+ else :
888
+ hess = hessian (chisqfunc )(fitp )
848
889
except TypeError :
849
890
raise Exception ("It is required to use autograd.numpy instead of numpy within fit functions, see the documentation for details." ) from None
850
891
892
+ if kwargs .get ('correlated_fit' ) is True :
893
+ def chisqfunc_compact (d ):
894
+ func_list = np .concatenate ([[func [k ]] * len (x [k ]) for k in key_ls ])
895
+ model = anp .array ([func_list [i ](d [:n_parms ], x_all [i ]) for i in range (len (x_all ))])
896
+ chisq = anp .sum (anp .dot (chol_inv , (d [n_parms :] - model )) ** 2 )
897
+ return chisq
898
+ else :
899
+ def chisqfunc_compact (d ):
900
+ func_list = np .concatenate ([[func [k ]] * len (x [k ]) for k in key_ls ])
901
+ model = anp .array ([func_list [i ](d [:n_parms ], x_all [i ]) for i in range (len (x_all ))])
902
+ chisq = anp .sum (((d [n_parms :] - model ) / dy_f ) ** 2 )
903
+ return chisq
904
+
851
905
jac_jac_y = hessian (chisqfunc_compact )(np .concatenate ((fitp , y_f )))
852
906
853
907
# Compute hess^{-1} @ jac_jac_y[:n_parms + m, n_parms + m:] using LAPACK dgesv
@@ -856,24 +910,17 @@ def prepare_hat_matrix():
856
910
except np .linalg .LinAlgError :
857
911
raise Exception ("Cannot invert hessian matrix." )
858
912
859
- if kwargs .get ('expected_chisquare' ) is True :
860
- if kwargs .get ('correlated_fit' ) is not True :
861
- W = np .diag (1 / np .asarray (dy_f ))
862
- cov = covariance (y_all )
863
- hat_vector = prepare_hat_matrix ()
864
- A = W @ hat_vector # hat_vector = 'jacobian(func)(fit_result.x, x)'
865
- P_phi = A @ np .linalg .pinv (A .T @ A ) @ A .T
866
- expected_chisquare = np .trace ((np .identity (x_all .shape [- 1 ]) - P_phi ) @ W @ cov @ W )
867
- output .chisquare_by_expected_chisquare = output .chisquare / expected_chisquare
868
- if not silent :
869
- print ('chisquare/expected_chisquare:' , output .chisquare_by_expected_chisquare )
870
-
871
913
result = []
872
914
for i in range (n_parms ):
873
915
result .append (derived_observable (lambda x_all , ** kwargs : (x_all [0 ] + np .finfo (np .float64 ).eps ) / (y_all [0 ].value + np .finfo (np .float64 ).eps ) * fitp [i ], list (y_all ), man_grad = list (deriv_y [i ])))
874
916
875
917
output .fit_parameters = result
876
918
919
+ if kwargs .get ('correlated_fit' ) is True :
920
+ n_cov = np .min (np .vectorize (lambda x_all : x_all .N )(y_all ))
921
+ output .t2_p_value = 1 - scipy .stats .f .cdf ((n_cov - output .dof ) / (output .dof * (n_cov - 1 )) * output .chisquare ,
922
+ output .dof , n_cov - output .dof )
923
+
877
924
return output
878
925
879
926
0 commit comments