Skip to content

Commit

Permalink
1. add pos_error/neg_error/errors properteis to `Ostap::Math::Valu…
Browse files Browse the repository at this point in the history
…eWithError` object `

   1. add column `@limit?` for printput of `RooFitResult` object to shwo the distance to the limits (if any). Distanc eof <3sigma, an d<5 sigma ar ecolored. Distances > 10sigma are omitted.
  • Loading branch information
VanyaBelyaev committed Aug 27, 2024
1 parent 6afbde0 commit 00d3532
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 19 deletions.
3 changes: 3 additions & 0 deletions ReleaseNotes/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
1. add `pretty_array` function for nice print of arrays/sequences
1. add some auxillary methods for matrices: `(min/max/minabs/maxabs)_element` and `(min/max)_diagonal` and `(min/max/minabs/maxabs)_element_index`
1. improve printout of `SVectorwithError`
1. add `pos_error/neg_error/errors` properteis to `Ostap::Math::ValueWithError` object `
1. add column `@limit?` for printput of `RooFitResult` object to shwo the distance to the limits (if any). Distanc eof <3sigma, an d<5 sigma ar ecolored. Distances > 10sigma are omitted.

`
## Backward incompatible

## Bug fixes
Expand Down
73 changes: 63 additions & 10 deletions ostap/fitting/roofitresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
from ostap.core.meta_info import root_info
from ostap.core.core import Ostap, VE, valid_pointer, iszero, isequal
from ostap.core.ostap_types import string_types , integer_types
from ostap.utils.valerrors import ValWithErrors, AsymErrors
import ostap.fitting.variables
import ostap.fitting.printable
import ostap.math.linalg as LA
from ostap.logger.colorized import allright, attention
from ostap.logger.colorized import allright, attention, attstr
from ostap.logger.pretty import pretty_float, pretty_ve, pretty_err2
import ROOT, math, sys, ctypes
# =============================================================================
Expand Down Expand Up @@ -593,7 +594,7 @@ def _rfr_evaluate_ ( self , func , args , partial = () ) :
# result.table()
# @endcode
def _rfr_table_ ( rr , title = '' , prefix = '' , more_vars = {} ) :
""" print RooFitResult as a table
""" Print RooFitResult as a table
>>> result = ...
>>> result.table()
"""
Expand Down Expand Up @@ -653,18 +654,63 @@ def _rfr_table_ ( rr , title = '' , prefix = '' , more_vars = {} ) :
rows.append ( ( 'Invalid FCN/NLL evaluations' , '' , ' %d' % nbadnll , '' ) )


with_globcorr = not ( (6,24) <= root_info < (6,28) )
with_globcorr = True or not ( (6,24) <= root_info < (6,28) )
with_globcorr = not ( (6,24) <= root_info < (6,26) )
with_globcorr = not ( ( 6 , 24 ) <= root_info < ( 6 , 28 ) )
with_globcorr = True or not ( ( 6 , 24 ) <= root_info < ( 6 , 28 ) )
with_globcorr = not ( ( 6 , 24 ) <= root_info < ( 6 , 26 ) )

with_globcorr = True

if with_globcorr : rows = [ ( '', 'Unit', 'Value' , 'Global/max correlation [%]') ] + rows
else : rows = [ ( '', 'Unit', 'Value' , 'Max correlation [%]') ] + rows

pars_all = r.params ( float_only = False )
pars_float = r.params ( float_only = True )

#
## parameter close to the limits?
#
limits = {}
threshold = 10
for p in pars_float :

v , a = pars_float [ p ]

has_min = hasattr ( a , 'hasMin' ) and a.hasMin()
has_max = hasattr ( a , 'hasMax' ) and a.hasMax()

if has_min or has_max :

value = v.value()

if a.hasAsymError () :
v = ValWithErrors ( a.getVal() , AsymErrors ( negative = a.getAsymErrorLo() , positive = a.getAsymErrorHi() ) )

dmin , dmax = -1, -1
if has_min and hasattr ( a , 'getMin' ) :
vmin = a.getMin()
verr = abs ( v.neg_error )
if isequal ( vmin , value ) : dmin = 0
elif vmin < value and 0 < verr : dmin = abs ( value - vmin ) / verr

if has_max and hasattr ( a , 'getMax' ) :
vmax = a.getMax()
verr = abs ( v.pos_error )
if isequal ( vmax , value ) : dmax = 0
elif value < vmax and 0 < verr : dmax = abs ( value - vmax ) / verr

dmn = ( 0 <= dmin < threshold )
dmx = ( 0 <= dmax < threshold )

if dmn and dmx : limits [ a.name ] = min ( dmin , dmax )
elif dmn : limits [ a.name ] = dmin
elif dmx : limits [ a.name ] = dmax


if with_globcorr : header = ( '', 'Unit', 'Value' , 'Global/max correlation [%]')
else : header = ( '', 'Unit', 'Value' , 'Max correlation [%]')

if limits : header += ( '@limit?', )

rows = [ header ] + rows


## constant/fix parameters
crows = []
for p in pars_all :
Expand All @@ -686,7 +732,7 @@ def _rfr_table_ ( rr , title = '' , prefix = '' , more_vars = {} ) :

v , a = pars_float [ p ]

if not a.hasAsymError() :
if not a.hasAsymError () :
s , n = pretty_ve ( v )
else :
s , n = pretty_err2 ( a.getVal() , a.getAsymErrorHi() , a.getAsymErrorLo() )
Expand Down Expand Up @@ -720,6 +766,13 @@ def _rfr_table_ ( rr , title = '' , prefix = '' , more_vars = {} ) :
else :

row = p , n , s

dist = limits.get ( a.name , None )
if not dist is None :
item = '%.1fs' % dist
if dist <= 3 : item = attention ( item )
elif dist <= 5 : item = attstr ( item )
row += ( item , )

frows.append ( row )

Expand Down Expand Up @@ -747,7 +800,7 @@ def _rfr_table_ ( rr , title = '' , prefix = '' , more_vars = {} ) :

import ostap.logger.table as T

return T.table ( all , title = title if title else r.GetTitle() , prefix = prefix , alignment = 'llll' )
return T.table ( all , title = title if title else r.GetTitle() , prefix = prefix , alignment = 'llllc' )

# =============================================================================
## 'easy' print of RooFitResult
Expand Down
10 changes: 8 additions & 2 deletions ostap/fitting/tests/test_fitting_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ def test_gauss() :

models.add ( model )
results.append ( result )

with rooSilent() :
result, frame = model. fitTo ( dataset0 , silent = True , minos = ( signal.mean.name, ) )

make_print ( model , result , 'Simple Gaussian model' , logger )

# =============================================================================
## CrystalBall PDF
Expand Down Expand Up @@ -2000,10 +2005,11 @@ def dump_models () :
## simple Gaussian PDF + background
with timing ('test_gauss' , logger ) :
test_gauss ()

## Crystal Ball + background
with timing ('test_crystalball' , logger ) :
test_crystalball ()


## right-side Crystal Ball + background
with timing ('test_crystalball_RS' , logger ) :
Expand Down Expand Up @@ -2184,7 +2190,7 @@ def dump_models () :
## Hypatia + background
with timing ('test_hypatia' , logger ) :
test_hypatia ()

## check finally that everything is serializeable:
with timing ('test_db' , logger ) :
test_db ()
Expand Down
49 changes: 47 additions & 2 deletions ostap/math/ve.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ def _ve_sum_ ( s ) :
"""
return Ostap.Math.sum ( s )


# =============================================================================
## Sum the contents of the vector
def _ve_asum_ ( s ) :
Expand Down Expand Up @@ -269,7 +268,7 @@ def _ve_ne_ ( self , other ) :
# print ve.minmax(2)
# @endcode
def _ve_minmax_ ( s , n = 1 ) :
"""Get an easy and coherent way to access ``min/max'' for
""" Get an easy and coherent way to access `min/max' for
the value with error object: (value-n*error,value+n*error)
>>> ve = VE(2,2)
>>> print ve.minmax()
Expand Down Expand Up @@ -300,6 +299,52 @@ def _ve_hash_ ( v ) :

VE.__hash__ = _ve_hash_

# =============================================================================
## Get the "positive" error
# @code
# ve = ...
# pos_error = ve.pos_error
# @endcode
def _ve_pos_error_ ( v ) :
""" Get the "positive" error
>>> ve = ...
>>> pos_error = ve.pos_error
"""
err = v.error()
return err if 0 <= err else 0

# =============================================================================
## Get the "negative" error
# @code
# ve = ...
# neg_error = ve.neg_error
# @endcode
def _ve_neg_error_ ( v ) :
""" Get the "negative" error
>>> ve = ...
>>> neg_error = ve.neg_error
"""
err = v.error()
return -err if 0 <= err else 0

# =============================================================================
## Get "negative&positive" errors
# @code
# ve = ...
# neg, pos = ve.errors ()
# @endcode
def _ve_errors_ ( v ) :
""" Get `negative&positive' errors
>>> ve = ...
>>> neg, pos = ve.errors ()
"""
err = v.error()
return (-err,err) if 0 <= err else (0,0)

VE.pos_error = property ( _ve_pos_error_ , None , None , _ve_pos_error_ .__doc__ )
VE.neg_error = property ( _ve_neg_error_ , None , None , _ve_neg_error_ .__doc__ )
VE.errors = property ( _ve_errors_ , None , None , _ve_errors_ .__doc__ )

# =============================================================================
from random import gauss as _gauss
# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion ostap/utils/valerrors.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def __setstate__ ( self , state ) :
# ==========================================================================
## conversion to string
def toString ( self , format = '( %+.5g -/%.5g +/%-.5g ) ' ) :
"""Conversion to string
""" Conversion to string
"""
return format % ( self.value , abs ( self.neg_error ) , self.pos_error )

Expand Down
9 changes: 5 additions & 4 deletions source/include/Ostap/ValueWithError.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ namespace Ostap
public:
// ======================================================================
/// constructor from the value and covariance
ValueWithError ( const double value = 0 ,
const double covariance = 0 ) ;
ValueWithError
( const double value = 0 ,
const double covariance = 0 ) ;
// ======================================================================
/** constructor from the (value,error)-pair
* - first element is "value"
Expand Down Expand Up @@ -331,9 +332,9 @@ namespace Ostap
private:
// ======================================================================
/// the actual value
double m_value ; // the actual value
double m_value { 0 } ; // the actual value
/// the associated covariance
double m_cov2 ; // the associated covariance
double m_cov2 { 0 } ; // the associated covariance
// ======================================================================
} ;
// ========================================================================
Expand Down

0 comments on commit 00d3532

Please sign in to comment.