def interpolation(table, degree=3, bc_type=None): """Create interpolation spline using scipy machinery >>> table = ... ## interpolation table >>> spline = intepolation ( table , degree = 3 ) - see `scipy.interpolation.make_innterp_spline` - see `Ostap.Math.Interpolaiton.Table` """ assert isinstance ( table , Ostap.Math.Interpolation.Table ), \ "Inavalid interpolation type " assert isinstance ( degree , integer_types ) \ and 0 <= degree < len ( table ) \ and ( 0 == degree or 1 == degree %2 ) , \ "Invalid ``degree'' parameter %s" % degree N = len(table) K = degree spl = scipy.interpolate.make_interp_spline( [table.x(i) for i in range(N)], table.values(), k=K, bc_type=bc_type) if 0 < K: knots = doubles(spl.t[K:-K]) else: knots = doubles(spl.t) pars = doubles(spl.c) return Ostap.Math.BSpline(knots, pars)
def interpolate(func, abscissas, spline, *args): """Construct the interpolation B-spline func : the ``function'' abscissas : abscissas, if None/Empty, Greville's abscissas from spline will be used spline : th epsline qwill be constructed forem ``spline'' and ``args'' :Example: >> b1 = interpolate ( lambda x : x*x , [0,0.5,1,2] , 3 ) >> b2 = interpolate ( { 0:0 , 0.5:0.25 , 1:1 } , None , 1 ) >> b3 = interpolate ( [0,0.25,1,4] , [ 0,0.5, 1,2] , 2 ) >> b4 = interpolate ( lambda x : x*x , None , 3 ) """ bs = Ostap.Math.BSpline(spline, *args) if not abscissas: abscissas = bs.greville_abscissas() from types import GeneratorType as GT from collections import Iterable as IT from collections import Mapping as MT if isinstance(abscissas, GT): abscissas = [x for x in abscissas] if callable(func) and abscissas: func = [func(x) for x in abscissas] ## callable elif isinstance(func, dict) and not abscissas: ## mapping keys = func.keys() keys.sort() abscissas = [x for x in keys] func = [func[x] for x in keys] elif isinstance(func, GT): func = [f for f in func] ## generator elif isinstance(func, IT): pass ## iterable else: raise TypeError("Can't treat ``func''=%s" % func) ## from ostap.math.base import doubles _x = doubles(abscissas) _y = doubles(func) bs = Ostap.Math.BSpline(spline, *args) if len(bs) != len(_x): raise TypeError( "Can't interpolate: different number of parameters %s/%s" % (len(_x), len(bs))) sc = Ostap.Math.Interpolation.bspline(_x, _y, bs) if sc.isFailure(): raise TypeError("Ostap.Math.Bspline: Can't iterpolate!%s" % sc) return bs
def sp_factory(klass, knots, pars, *args): """Factory for deserisalization of splines - see Ostap.Math.BSPline - see Ostap.Math.PositiveSpline - see Ostap.Math.MonotonicSpline - see Ostap.Math.ConvexSpline - see Ostap.Math.ConvexOnlySpline """ return klass(doubles(knots), doubles(pars), *args)
def deboor(x, order, knots, points): """Evaluate the spline given by the set of knots, control points and the order using de Boor's algorithm - see https://en.wikipedia.org/wiki/De_Boor%27s_algorithm """ VD = cpp.std.vector('double') ## convert knots if needed if not isinstance(knots, VD): knots = doubles(knots) ## convert knots if needed if not isinstance(points, VD): points = doubles(points) ## return Ostap.Math.deboor(x, order, knots, points)
def interpolate(func, abscissas, xmin=0, xmax=1): """Construct the interpolation Bernstein polynomial It relies on Newton-Bernstein algorithm - see http://arxiv.org/abs/1510.09197 - see Mark Ainsworth and Manuel A. Sanches, ``Computing of Bezier control points of Largangian interpolant in arbitrary dimension'', arXiv:1510.09197 [math.NA] - see http://adsabs.harvard.edu/abs/2015arXiv151009197A func : the ``function'' abscissas : absciccas xmin : minimal x-value xmax : maximal x-value :Example: >> b1 = interpolate ( lambda x : x*x , [0,0.5,1,2] , 0 , 4 ) >> b2 = interpolate ( { 0:0 , 0.5:0.25 , 1:1 } , None , 0 , 4 ) >> b3 = interpolate ( [0,0.25,1,4] , [ 0,0.5, 1,2] , 0 , 4 ) """ if xmin > xmax: xmin, xmax = xmax, xmin from types import GeneratorType as GT from collections import Iterable as IT from collections import Mapping as MT if isinstance(abscissas, GT): abscissas = [x for x in abscissas] if callable(func): func = [func(x) for x in abscissas] ## callable elif isinstance(func, GT): func = [f for f in func] ## generator elif isinstance(func, dict) and not abscissas: ## mapping keys = func.keys() keys.sort() abscissas = [x for x in keys] func = [func[x] for x in keys] elif isinstance(func, IT): pass ## iterable else: raise TypeError("Can't treat ``func''=%s" % func) ## from ostap.math.base import doubles _x = doubles(abscissas) _y = doubles(func) ## return Ostap.Math.Interpolation.bernstein(_x, _y, xmin, xmax)
def data_quantiles(data, quantiles, expression, cuts='', *args): """Get the quantiles >>> data = ... >>> print data_quantiles ( data , 0.1 , 'mass' , 'pt>1' ) ## quantile >>> print data_quantiles ( data , (0.1,0.5) , 'mass' , 'pt>1' ) >>> print data_quantiles ( data , 10 , 'mass' , 'pt>1' ) ## deciles! >>> print data.quantiles ( 0.1 , 'mass' , 'pt>1' ) ## quantile >>> print data.quantiles ( (0.1,0.5) , 'mass' , 'pt>1' ) >>> print data.quantiles ( 10 , 'mass' , 'pt>1' ) ## deciles! - see Ostap::StatVar::quantile """ if isinstance(quantiles, float) and 0 < quantiles < 1: quantiles = [quantiles] elif isinstance(quantiles, int) and 1 < quantiles: N = quantiles quantiles = (float(i) / N for i in xrange(1, N)) qq = [] for q in quantiles: assert isinstance(q, float) and 0 < q < 1, 'Invalid quantile:%s' % q qq.append(q) qq.sort() from ostap.math.base import doubles rr = StatVar.quantiles(data, doubles(qq), expression, cuts, *args) rr = [r for r in rr] return tuple(rr)
def _new_init_ ( t , *args ) : """(Redefine standard constructor to allow usage of python lists&tuples) Lists and tuples are converted on flight to : - std::vector<double> - or std::vector<std::complex<double>> """ from ostap.math.base import doubles , complexes from ostap.core.ostap_types import string_types , listlike_types largs = list ( args ) for i , arg in enumerate ( largs ) : if not isinstance ( arg , ( list , tuple ) ) : continue try: _arg = doubles ( arg ) largs [ i ] = _arg continue except TypeError : pass try: _arg = complexes ( arg ) largs [ i ] = _arg continue except TypeError : pass targs = tuple ( largs ) ## use old constructor return t._old_init_ ( *targs )
def _new_init_ ( t , *args ) : """(Redefine standard constructor to allow usage of python lists&tuples) Lists and tuples are converted on flight to : - std::vector<double> - or std::vector<std::complex<double>> """ from ostap.math.base import doubles, complexes largs = list ( args ) alen = len ( largs ) for i in range(alen) : arg = largs[i] if not isinstance ( arg , ( list , tuple ) ) : continue try: _arg = doubles ( *arg ) largs[i] = _arg continue except TypeError : pass try: _arg = complexes ( *arg ) largs[i] = _arg continue except TypeError : pass targs = tuple(largs) ## use old constructor t._old_init_ ( *targs )
def abs_factory(arg, *args): """Factory for deserialisation of interpolation abscissas """ if isinstance(arg, sequence_types): vals = doubles(arg) return Ostap.Math.Interpolation.Abscissas(vals, *args) return Ostap.Math.Interpolation.Abscissas(arg, *args)
def knots_from_abscissas(abscissas, order, convert=True): """Reconstruct knot vector from greville abscissas and spline degree abscissas : vector of abscissas order : the order/degree of spline convert : convert to tuple ? >>> data = [ 0,2,3,4,5,10,11] >>> knots = knots_from_abscissas ( data , 3 ) """ ## VD = cpp.std.vector('double') ## ## convert knots if needed ## if not isinstance ( abscissas , VD ) : abscissas = doubles ( abscissas ) ## knots = Ostap.Math.knots_from_abscissas ( abscissas , order ) ## if convert : knots = tuple ( knots ) ## return knots if isinstance(abscissas, Ostap.Math.Interpolation.Table): data = abscissas.data() abscissas = [i.first for i in data] elif isinstance(anscissas, Ostap.Math.Interpolation.TABLE): data = abscissas abscissas = [i.first for i in data] elif isinstance(abscissas, list): abscissas.sort() else: abscissas = list(abscissas) abscissas.sort() if len(abscissas) < order + 1: raise AttributeError("Vector of abscissas is too short") degree = order af = abscissas[0] al = abscissas[-1] ## knots = [ af ] + abscissas + [ al ] ## ## if 1 == order : return knots ## while len(knots) < len(abscissas) + order + 1 : ## knots = [ af ] + knots + [ al ] ## ## return knots knots = (degree + 1) * [af] N = len(abscissas) for i in range(1, N): st = sum(knots[-(degree - 1):]) ti = abscissas[i] * (degree) - st knots.append(ti) while len(knots) < N + order + 1: knots.append(al) abscissas = doubles(abscissas) knots2 = Ostap.Math.knots_from_abscissas(abscissas, order) return knots2
def poly_factory(klass, params, *args): """Factory for deserisalization of polynomials with parameters - see Ostap.Math.Polynomial - see Ostap.Math.ChebyshevSum - see Ostap.Math.LegendreSum - see Ostap.Math.HermiteSum - see Ostap.Math.Bernstein - see Ostap.Math.BernsteinEven """ return klass(doubles(params), *args)
def _a_new_init_(a, arg1, *args): """ create abscissas """ if isinstance(arg1, Ostap.Math.Interpolation.Abscissas): return a._old_init_(arg1) if isinstance(arg1, sequence_types): if not args: return a._old_init_(doubles(arg1)) elif 1 == len(args) and isinstance(args[0], bool): return a._old_init_(doubles(arg1), args[0]) if isinstance(arg1, integer_types) and 0 <= arg1: if 2 == len(args): return a._old_init_(arg1, args[0], args[1]) elif 3 == len(args) and isinstance(args[2], integer_types) and 0 <= args[2]: return a._old_init_(arg1, *args) return a._old_init_(doubles(arg1, *args))
def data_quantiles ( data , quantiles , expression , cuts = '' , exact = QEXACT , *args ) : """Get the quantiles >>> data = ... >>> print data_quantiles ( data , 0.1 , 'mass' , 'pt>1' ) ## quantile >>> print data_quantiles ( data , (0.1,0.5) , 'mass' , 'pt>1' ) >>> print data_quantiles ( data , 10 , 'mass' , 'pt>1' ) ## deciles! >>> print data.quantiles ( 0.1 , 'mass' , 'pt>1' ) ## quantile >>> print data.quantiles ( (0.1,0.5) , 'mass' , 'pt>1' ) >>> print data.quantiles ( 10 , 'mass' , 'pt>1' ) ## deciles! - see Ostap::StatVar::quantile """ if isinstance ( quantiles , float ) and 0 < quantiles < 1 : quantiles = [ quantiles ] elif isinstance ( quantiles , int ) and 1 < quantiles : N = quantiles quantiles = ( float ( i ) / N for i in range ( 1 , N ) ) qq = [] for q in quantiles : assert isinstance ( q , float ) and 0 < q < 1 , 'Invalid quantile:%s' % q qq.append ( q ) qq.sort () from ostap.math.base import doubles qqq = doubles ( qq ) if exact is True : ## exact slow algorithm qn = StatVar. quantiles ( data , qqq , expression , cuts , *args ) elif exact is False : ## approximate fast formula qn = StatVar.p2quantiles ( data , qqq , expression , cuts , *args ) elif isinstance ( exact , int ) and len ( data ) <= exact : ## exact slow algorithm qn = StatVar. quantiles ( data , qqq , expression , cuts , *args ) else : ## approximate fast algorithm qn = StatVar.p2quantiles ( data , qqq , expression , cuts , *args ) return qn
def _new_init_(t, *args): """(Redefine standard constructor to allow usage of python lists&tuples) Lists and tuples are converted on flight to : - std::vector<double> - or std::vector<std::complex<double>> """ from ostap.math.base import doubles, complexes, VCT_TYPES from ostap.core.ostap_types import Generator, Sequence, list_types largs = list(args) for i, arg in enumerate(largs): if isinstance(arg, VCT_TYPES): continue if isinstance(arg, Generator): pass elif isinstance(arg, Sequence): pass elif isinstance(arg, list_types): pass else: continue try: _arg = doubles(arg) largs[i] = _arg continue except TypeError: pass try: _arg = complexes(arg) largs[i] = _arg continue except TypeError: pass targs = tuple(largs) ## use old constructor return t._old_init_(*targs)
def tab_factory(abscissas, values): """The factory for serialisation of the interpolation table """ return Ostap.Math.Interpolation.Table(abscissas, doubles(values))
def points ( func , abscissas = None ) : """Construct collection of interpolation points func : the ``function'' abscissas : abscissas :Example: >> b1 = points ( lambda x : x*x , [0,0.5,1,2] ) >> b2 = points ( { 0:0 , 0.5:0.25 , 1:1 } ) >> b3 = points ( [0,0.25,1,4] , [ 0,0.5, 1,2] ) >> b4 = points ( lambda x : x * x , Abscissas ( 4 , -2 , 2 , 1 ) ) """ from types import GeneratorType as GT from collections import Iterable as IT from collections import Mapping as MT ## switch on abscissas: _A = isinstance ( abscissas , Ostap.Math.Interpolation.Abscissas ) if isinstance ( abscissas , GT ) : abscissas = [ x for x in abscissas ] elif abscissas is None : if isinstance ( func , MT ) : _new_abscissas = [] _new_func = [] _keys = func.keys() _keys.sort() for _k in _keys : _new_abscissas.append ( _k ) _new_func .append ( func[_k] ) abscissas = _new_abscissas func = _new_func elif isinstance ( func , Ostap.Math.Interpolation.Table ) : return func elif hasattr ( func , 'iteritems' ) : _new_abscissas = [] _new_func = [] for _a,_f in func.iteritems() : _new_abscissas.append ( _a ) _new_func .append ( _f ) abscissas = _new_abscissas func = _new_func elif isinstance ( func , IT ) : try : _new_abscissas = [] _new_func = [] for _a , _f in func : _new_abscissas.append ( _a ) _new_func .append ( _f ) abscissas = _new_abscissas func = _new_func except : raise TypeError ( "Invalid iterable ``func'' for ``None''-abscissas!" ) else : raise TypeError ( "For ``None''-abscissas ``func'' must be Table or Mapping!" ) ## switch on func if callable ( func ) : nfunc = func func = ( nfunc ( x ) for x in abscissas ) elif isinstance ( func , GT ) : pass ## generator elif isinstance ( func , IT ) : pass ## iterable else : raise TypeError("Can't treat ``func''=%s" % func ) ## from ostap.math.base import doubles ## if _A : return Ostap.Math.Interpolation.Table ( abscissas , doubles ( func ) ) return Ostap.Math.Interpolation.Table ( doubles ( abscissas ) , doubles ( func ) )
def lagrange ( func , abscissas = None ) : """Construct very efficient Barycentric Lagrange interpolant - it takes O(n) flops for initialization with Chebyshev/Lobatto or uniform abscissas - it takes O(n^2) flops for initialization with arbitrary interpolation abscissas - it takes O(n) flops for evaluation! It is very fast! - see Jean-Paul Berrut and Lloyd N. Trefethen, ... Barycentric Lagrange Interpolation, SIAM Rev., 46(3), 501–517. ... ISSN (print): 0036-1445 ... ISSN (online): 1095-7200 - see https://doi.org/10.1137/S0036144502417715 - see https://en.wikipedia.org/wiki/Lagrange_polynomial - see https://people.maths.ox.ac.uk/trefethen/barycentric.pdf func : the ``function'' abscissas : abscissas :Example: >>> b1 = interpolate ( lambda x : x*x , [0,0.5,1,2] ) >>> b2 = interpolate ( { 0:0 , 0.5:0.25 , 1:1 } ) >>> b3 = interpolate ( [0,0.25,1,4] , [ 0,0.5, 1,2] ) >>> b4 = interpolate ( lambda x : x * x , Abscissas( 4 , -2 , 2 , 1 ) ) >>> b1 = lagrange ( lambda x : x*x , [0,0.5,1,2] ) >>> b2 = lagrange ( { 0:0 , 0.5:0.25 , 1:1 } ) >>> b3 = lagrange ( [0,0.25,1,4] , [ 0,0.5, 1,2] ) >>> b4 = lagrange ( lambda x : x * x , Abscissas( 4 , -2 , 2 , 1 ) ) >>> b1 = barycentric ( lambda x : x*x , [0,0.5,1,2] ) >>> b2 = barycentric ( { 0:0 , 0.5:0.25 , 1:1 } ) >>> b3 = barycentric ( [0,0.25,1,4] , [ 0,0.5, 1,2] ) >>> b4 = barycentric ( lambda x : x * x , Abscissas( 4 , -2 , 2 , 1 ) ) """ from types import GeneratorType as GT from collections import Iterable as IT from collections import Mapping as MT ## switch on abscissas: _W = isinstance ( abscissas , Ostap.Math.Interpolation.Weights ) _A = isinstance ( abscissas , Ostap.Math.Interpolation.Abscissas ) if isinstance ( abscissas , GT ) : abscissas = [ x for x in abscissas ] elif abscissas is None : if isinstance ( func , MT ) : _new_abscissas = [] _new_func = [] _keys = func.keys() _keys.sort() for _k in _keys : _new_abscissas.append ( _k ) _new_func .append ( func[_k] ) abscissas = _new_abscissas func = _new_func elif isinstance ( func , Ostap.Math.Interpolation.Table ) : return Ostap.Math.Barycentric ( func ) ## ready else : raise TypeError ( "For ``None''-abscissas ``func'' must be Table or Mapping!" ) ## switch on func if callable ( func ) : nfunc = func if _W or _A : func = ( nfunc ( x ) for x in abscissas.x() ) else : func = ( nfunc ( x ) for x in abscissas ) elif isinstance ( func , GT ) : pass ## generator elif isinstance ( func , IT ) : pass ## iterable else : raise TypeError("Can't treat ``func''=%s" % func ) ## from ostap.math.base import doubles if _W or _A : return Ostap.Math.Barycentric ( abscissas , doubles ( func ) ) ## return Ostap.Math.Barycentric ( doubles ( abscissas ) , doubles ( func ) )
def run_func_interpolation(fun, N, low, high, scale=1.e-5, logger=logger, name='Interpolation'): """Interpolate the function""" Abscissas = Ostap.Math.Interpolation.Abscissas abscissas = (('Uniform', Abscissas(N, low, high, 0)), ('Chebyshev', Abscissas(N, low, high, 1)), ('Lobatto', Abscissas(N, low, high, 2)), ('Random', Abscissas(doubles([x for x in more_uniform(low, high, N, N) ])))) tables = [(a[0], points(fun, a[1])) for a in abscissas] interpolants = [] for i, t in enumerate(tables): item = ('Bernstein', t[0]), interpolate_bernstein(t[1], None, low, high) interpolants.append(item) item = ('Neville', t[0]), Ostap.Math.Neville(t[1]) interpolants.append(item) item = ('Lagrange', t[0]), Ostap.Math.Lagrange(t[1]) interpolants.append(item) item = ('Newton', t[0]), Ostap.Math.Newton(t[1]) interpolants.append(item) item = ('Berrut1st', t[0]), Ostap.Math.Berrut1st(t[1]) interpolants.append(item) item = ('Berrut2nd', t[0]), Ostap.Math.Berrut2nd(t[1]) interpolants.append(item) item = ('Barycentric', t[0]), Ostap.Math.Barycentric(t[1]) interpolants.append(item) item = ('Thiele', t[0]), Ostap.Math.Thiele(t[1]) interpolants.append(item) for d in range(0, 9): item = ('FloaterHormann%d' % d, t[0]), Ostap.Math.FloaterHormann(t[1], d) interpolants.append(item) for d in range(1, 6): item = ('BSpline%d' % d, t[0]), interpolate_bspline(t[1], None, d) interpolants.append(item) if bspline_interpolate: for d in (1, 3, 5, 7): item = ('BSpline%dSP' % d, t[0]), bspline_interpolate(t[1], d) interpolants.append(item) for n, t in interpolants: functions.add((n, t)) graphs = [] with wait(3), use_canvas(name): ff = lambda x: fun(x) f1_draw(ff, xmin=low, xmax=high, linecolor=2, linewidth=2) for i, item in enumerate(interpolants): p, f = item n1, n2 = p color = i + 3 f.draw('same', linecolor=color, xmin=low, xmax=high) if hasattr(f, 'graph'): g = f.graph() g.draw('p', markercolor=color, markersize=2) graphs.append(g) if 1 == color: color = 'Black' elif 2 == color: color = 'Red' elif 3 == color: color = 'Green' elif 4 == color: color = 'Blue' elif 5 == color: color = 'Yellow' elif 6 == color: color = 'Magenta' elif 7 == color: color = 'Cyan' elif 8 == color: color = 'DarkGreen' logger.info('Color %10s for %s:%s' % (color, n1, n2)) graphs = [t[1].graph() for t in tables] for i, g in enumerate(graphs, start=3): g.draw('p', markercolor=i) xx = [] NP = 50000 for i in range(NP): xx.append(random.uniform(low, high)) xx.sort() from collections import defaultdict counters = defaultdict(SE) cpu = {} ## loop over all interpolants for n, fi in interpolants: n1, n2 = n cnt = counters[(n1, n2, fi)] with timing('', logger=None) as t: for x in xx: v = fun(x) vi = fi(x) cnt += abs(vi - v) / scale cpu[(n1, n2)] = t.delta rows = [('Interpolant', 'Grid', 'mean+/-rms', 'max', 'distance')] for item in counters: n1, n2, ff = item c = counters[item] d = distance(ff, fun, low, high) / scale vmax = c.max() if 1.e+6 < vmax: vmax = '+inf' else: vmax = '%-9.1f' % vmax row = n1, n2, '%9.2f +/- %-09.1f' % (c.mean().value(), c.rms()), vmax, '%.3g' % d rows.append(row) title = 'Interpolation precision (%d random points)[x%s]' % (NP, scale) table = T.table(rows, title=title, prefix='# ', alignment='lllll') logger.info('%s:\n%s' % (title, table)) lst = [] for k in cpu: item = cpu[k], k[0], k[1] lst.append(item) lst.sort() rows = [('Interpolant', 'Grid', 'CPU [s]')] for t, k0, k1 in lst: row = k0, k1, '%.4g' % cpu[(k0, k1)] rows.append(row) title = 'CPU: %d points' % NP table = T.table(rows, title=title, prefix='# ', alignment='ll') logger.info('%s:\n%s' % (title, table))
def int_factory(klass, abscissas, values, *args): """The factory for serialisation of the interpolation table """ the_table = Ostap.Math.Interpolation.Table(abscissas, doubles(values)) return klass(the_table, *args)