3232 'NameDuplicates' , ## allow/disallow name duplicates
3333 'SETPARS' , ## context manager to keep/preserve parameters
3434 ###
35- 'TailN' , ## helper mixing to define N-parameter for tail
36- 'TailNL' , ## helper mixing to define N-parameter for tail
37- 'TailNR' , ## helper mixing to define N-parameter for tail
35+ 'TailN' , ## helper mixin to define N-parameter for tail
36+ 'TailNL' , ## helper mixin to define N-parameter for tail
37+ 'TailNR' , ## helper mixin to define N-parameter for tail
3838 ##
39- 'Tail' , ## helper mixing to define power-law tail
40- 'LeftTail' , ## helper mixing to define power-law (left) tail
41- 'RightTail' , ## helper mixing to define power-law (right) tail
39+ 'Tail' , ## helper mixin to define power-law tail
40+ 'LeftTail' , ## helper mixin to define power-law (left) tail
41+ 'RightTail' , ## helper mixin to define power-law (right) tail
4242 ##
43- 'SigmaLR' , ## helper mixing to treat L/R sigmas
44- 'TwoSigmas' , ## helper mixing to treat two sigmas
43+ 'SigmaLR' , ## helper mixin to treat L/R sigmas
44+ 'TwoSigmas' , ## helper mixin to treat two sigmas
4545)
4646# =============================================================================
4747from ostap .core .meta_info import root_info
@@ -650,6 +650,62 @@ def set_value ( var , value , ok = lambda a , b : True ) :
650650 # =========================================================================
651651 ## Create some expressions with variables
652652 # =========================================================================
653+
654+ # =============================================================================
655+ ## construct (on-flight) variable for the sum of many variables \f$ sum_i v_i \f$
656+ # @code
657+ # var1 = ...
658+ # var2 = ...
659+ # var2 = ...
660+ # var4 = xxx.add_variables ( var1 , var2 , var3 )
661+ # @endcode
662+ def add_variables ( self , * variables , name = '' , title = '' ) :
663+ """ Construct (on-flight) variable for var1*c1+var2*c2
664+ >>> var1 = ...
665+ >>> var2 = ...
666+ >>> var3 =
667+ >>> var4 = xxx.add_variables ( var1 , var2 , var3 )
668+ """
669+ ## the most trivial case
670+ if not variables : return ROOT .RooFit .RooConst ( 0 )
671+
672+ ## collect variables
673+ vvars = []
674+ vsum = 0.0
675+ for i , v in enumerate ( variables ) :
676+ if isinstance ( v , num_types ) : vsum += float ( v )
677+ elif isinstance ( v , ROOT .RooConstVar ) : vsum += float ( v )
678+ elif isinstance ( v , ROOT .RooAbsReal ) : vvars .append ( v )
679+ else :
680+ raise TypeError ("add_variables: invalid variable[#%d] type %s" % ( i , typename ( v ) ) )
681+
682+ ## no variables?
683+ if not vvars : return ROOT .RooFit .RooConst ( vsum )
684+
685+ ## only one variable ?
686+ if 1 == len ( vvars ) : return self .vars_add ( vvars [ 0 ] , vsum , name = name , title = title )
687+
688+ ## at least two variables
689+ vlst = ROOT .RooArgList ()
690+ for v in vvars : vlst .add ( v )
691+
692+ ## if constant is not zero - add it!
693+ if vsum :
694+ vsum = ROOT .RooFit .RooConst ( vsum )
695+ vlst .add ( vsum )
696+
697+ self .aux_keep .append ( vlst )
698+
699+ name = name if name else self .roo_name ( "_add_" .join ( v .name for v in vlst ) )
700+ title = title if title else ( "+" .join ( v .name for v in vlst ) )
701+
702+ ## add variables
703+ result = Ostap .MoreRooFit .Addition ( name , title , vlst )
704+ self .aux_keep .append ( result )
705+
706+ return result
707+
708+ sum_variables = add_variables
653709
654710 # =========================================================================
655711 ## construct (on-flight) variable for the product of
@@ -670,7 +726,9 @@ def vars_multiply ( self , var1 , var2 , name = '' , title = '' ) :
670726 >>> var4 = xxx.vars_multiply ( var1 , 2 , 'sigma2' , title = 'Scaled sigma' )
671727 >>> var3 = xxx.vars_product ( var1 , var2 )
672728 >>> var4 = xxx.vars_product ( var1 , 2 , 'sigma2' , title = 'Scaled sigma' )
673- """
729+ """
730+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
731+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
674732
675733 f1 = isinstance ( var1 , num_types )
676734 f2 = isinstance ( var2 , num_types )
@@ -704,7 +762,6 @@ def vars_multiply ( self , var1 , var2 , name = '' , title = '' ) :
704762
705763 return result
706764
707-
708765 # =============================================================================
709766 ## construct (on-flight) variable for the sum of
710767 # <code>var1</code> and <code>var2</code> \f$ v\equiv c_1v_1 + c_2v_2\f$
@@ -726,36 +783,34 @@ def vars_add ( self , var1 , var2 , c1 = 1 , c2 = 1 , name = '' , title = '' ) :
726783 >>> var6 = xxx.vars_sum ( var1 , 2 , 'sigma2' , title = 'Scaled sigma' )
727784 """
728785
786+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
787+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
788+
729789 assert isinstance ( c1 , num_types ) and isinstance ( c2 , num_types ),\
730790 "vars_add: c1 and c2 must be numeric types!"
731791
732792 c1 = float ( c1 )
733793 c2 = float ( c2 )
734794
735- if 0 == c1 and 0 == c2 :
736- return ROOT .RooFit .RooConst ( 0 ) ## shortcut
737- elif 0 == c1 :
738- return self .vars_multiply ( var2 , c2 , name = name , title = title )
739- elif 0 == c2 :
740- return self .vars_multiply ( var1 , c1 , name = name , title = title )
795+ if 0 == c1 and 0 == c2 : return ROOT .RooFit .RooConst ( 0 ) ## shortcut
796+ elif 0 == c1 : return self .vars_multiply ( var2 , c2 , name = name , title = title )
797+ elif 0 == c2 : return self .vars_multiply ( var1 , c1 , name = name , title = title )
741798
742799 f1 = isinstance ( var1 , num_types )
743800 f2 = isinstance ( var2 , num_types )
744801
745802 if f1 and f2 :
746803 res = float ( var1 ) * float ( c1 ) + float ( var2 ) * float ( c2 )
747- return ROOT .RooFit .RooConst ( res )
804+ return ROOT .RooFit .RooConst ( res )
748805 elif f1 :
749806 ## shortcut
750- if 0 == var1 :
751- return self .var_multiply ( var2 , c2 , name = name , title = title ) ## SHORTCUT
807+ if 0 == var1 : return self .var_multiply ( var2 , c2 , name = name , title = title ) ## SHORTCUT
752808 #
753809 var1 = ROOT .RooFit .RooConst ( float ( var1 ) * float ( c1 ) )
754810 return self .vars_add ( var1 , var2 , name = name , title = title )
755811 elif f2 :
756812 ## shortcut
757- if 0 == var2 :
758- return self .var_multiply ( var1 , c1 , name = name , title = title ) ## SHORTCUT
813+ if 0 == var2 : return self .var_multiply ( var1 , c1 , name = name , title = title ) ## SHORTCUT
759814 #
760815 var2 = ROOT .RooFit .RooConst ( float ( var2 ) * float ( c2 ) )
761816 return self .vars_add ( var1 , var2 , name = name , title = title )
@@ -766,10 +821,8 @@ def vars_add ( self , var1 , var2 , c1 = 1 , c2 = 1 , name = '' , title = '' ) :
766821 name = name if name else self .roo_name ( "add_%s_%s" % ( var1 .name , var2 .name ) )
767822 title = title if title else "(%s)+(%s)" % ( var1 .name , var2 .name )
768823
769- if c1 == 1 and c2 == 1 :
770- result = Ostap .MoreRooFit .Addition ( var1 , var2 , name , title )
771- else :
772- result = Ostap .MoreRooFit .Addition2 ( name , title , var1 , var2 , c1 , c2 )
824+ if c1 == 1 and c2 == 1 : result = Ostap .MoreRooFit .Addition ( var1 , var2 , name , title )
825+ else : result = Ostap .MoreRooFit .Addition2 ( name , title , var1 , var2 , c1 , c2 )
773826
774827 self .aux_keep .append ( result )
775828
@@ -796,6 +849,9 @@ def vars_subtract ( self , var1 , var2 , name = '' , title = '' ) :
796849 >>> var6 = xxx.vars_difference ( var1 , 2 , 'sigma2' , title = 'Scaled sigma' )
797850 """
798851
852+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
853+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
854+
799855 f1 = isinstance ( var1 , num_types )
800856 f2 = isinstance ( var2 , num_types )
801857
@@ -846,6 +902,9 @@ def vars_divide ( self , var1 , var2 , name = '' , title = '' ) :
846902 >>> var6 = xxx.vars_ratio ( var1 , 2 , 'sigma2' , title = 'Scaled sigma' )
847903 """
848904
905+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
906+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
907+
849908 f1 = isinstance ( var1 , num_types )
850909 f2 = isinstance ( var2 , num_types )
851910
@@ -886,6 +945,9 @@ def vars_fraction ( self , var1 , var2 , name = '' , title = '' ) :
886945 >>> var4 = xxx.vars_fraction ( var1 , 2 , 'sigma2' , title = 'exression' )
887946 """
888947
948+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
949+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
950+
889951 f1 = isinstance ( var1 , num_types )
890952 f2 = isinstance ( var2 , num_types )
891953
@@ -935,6 +997,9 @@ def vars_asymmetry ( self , var1 , var2 , scale = 1 , name = '' , title = '' ) :
935997 >>> var4 = xxx.vars_reldifference ( var1 , 2 , 'sigma2' , title = 'exression' )
936998 """
937999
1000+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
1001+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
1002+
9381003 f1 = isinstance ( var1 , num_types )
9391004 f2 = isinstance ( var2 , num_types )
9401005
@@ -981,6 +1046,10 @@ def vars_power ( self , var1 , var2 , name = '' , title = '' ) :
9811046 >>> var4 = xxx.vars_power ( var1 , 2.0 )
9821047 >>> var4 = xxx.vars_power ( 2.0 , var2 )
9831048 """
1049+
1050+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
1051+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
1052+
9841053 f1 = isinstance ( var1 , num_types )
9851054 f2 = isinstance ( var2 , num_types )
9861055
@@ -1009,7 +1078,7 @@ def vars_power ( self , var1 , var2 , name = '' , title = '' ) :
10091078
10101079 return result
10111080
1012- # =============================================================================
1081+ # =============================================================================
10131082 ## construct (on-flight) variable for \f$ exp(ab) \f$
10141083 # <code>var1</code> and <code>var2</code>:
10151084 # @code
@@ -1027,6 +1096,10 @@ def vars_exp ( self , var1 , var2 = 1 , name = '' , title = '' ) :
10271096 >>> var4 = xxx.vars_exp ( var1 , 2.0 )
10281097 >>> var4 = xxx.vars_exp ( 2.0 , var2 )
10291098 """
1099+
1100+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
1101+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
1102+
10301103 f1 = isinstance ( var1 , num_types )
10311104 f2 = isinstance ( var2 , num_types )
10321105
@@ -1073,6 +1146,10 @@ def vars_abs ( self , var1 , var2 = 1 , name = '' , title = '' ) :
10731146 >>> var4 = xxx.vars_abs ( var1 , 2.0 )
10741147 >>> var4 = xxx.vars_abs ( 2.0 , var2 )
10751148 """
1149+
1150+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
1151+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
1152+
10761153 f1 = isinstance ( var1 , num_types )
10771154 f2 = isinstance ( var2 , num_types )
10781155
@@ -1109,7 +1186,6 @@ def vars_abs ( self , var1 , var2 = 1 , name = '' , title = '' ) :
11091186 vars_pow = vars_power
11101187 vars_expo = vars_exp
11111188
1112-
11131189 # =========================================================================
11141190 ## helper function to create <code>RooFormulaVar</code>
11151191 # @code
@@ -1171,7 +1247,6 @@ def vars_formula ( self , formula , vars , name = '' , title = '' ) :
11711247
11721248 return rfv
11731249
1174-
11751250 # =========================================================================
11761251 ## make very specific combination of variables: alpha*var1*(bets+gamma*var2)
11771252 # \f$ r = \alpha v_1 ( \beta + \gamma * v_2 ) \
@@ -1187,6 +1262,9 @@ def vars_combination ( self ,
11871262 r = alpha * v_1 ( beta + gamma * v_2 )
11881263 """
11891264
1265+ if isinstance ( var1 , ROOT .RooConstVar ) : var1 = float ( var1 )
1266+ if isinstance ( var2 , ROOT .RooConstVar ) : var2 = float ( var2 )
1267+
11901268 f1 = isinstance ( var1 , num_types )
11911269 f2 = isinstance ( var2 , num_types )
11921270
0 commit comments