def simpson(y, x, dx=1, axis=-1): nd = len(y.shape) N = y.shape[axis] start = 0 stop = N - 2 step = 2 slice_all = (slice(None), ) * nd slice0 = tupleset(slice_all, axis, slice(start, stop, step)) slice1 = tupleset(slice_all, axis, slice(start + 1, stop + 1, step)) slice2 = tupleset(slice_all, axis, slice(start + 2, stop + 2, step)) if x is None: result = np.sum(dx / 3.0 * (y[slice0] + 4 * y[slice1] + y[slice2]), axis=axis) else: h = np.diff(x, axis=axis) sl0 = tupleset(slice_all, axis, slice(start, stop, step)) sl1 = tupleset(slice_all, axis, slice(start + 1, stop + 1, step)) h0 = h[sl0] h1 = h[sl1] hsum = h0 + h1 hprod = h0 * h1 h0divh1 = h0 / h1 tmp = hsum / 6.0 * (y[slice0] * (2 - 1.0 / h0divh1) + y[slice1] * hsum * hsum / hprod + y[slice2] * (2 - h0divh1)) result = np.sum(tmp, axis=axis) return result
def _simps_error2(dy, start, stop, x, dx, axis): """Squared error on Simpson's rule integration. :Arguments: *dy* errors at function values *start*, *stop* first and last index at which a Simpson 3-point interval starts *x* abscissa values (provide if spacing not equal) *dx* constant spacing (is overridden by *dx*) *axis* axis in *dy* along which the data lie """ nd = len(dy.shape) if start is None: start = 0 step = 2 all = (slice(None), ) * nd # check that stop is appropriate !!! slice2k = tupleset(all, axis, slice(start, stop, step)) # does NOT include last point stop+1 slice2k1 = tupleset(all, axis, slice(start + 1, stop + 1, step)) slice0 = tupleset(all, axis, slice(start, start + 1)) # first point sliceN = tupleset(all, axis, slice(stop + 1, stop + 2)) # last point Df2k = dy[slice2k] # 2k Df2k1 = dy[slice2k1] # 2k+1 Df0 = dy[slice0] # first = 0 DfN = dy[sliceN] # last = N if x is None: # Even spaced Simpson's rule. # Simpson error propagation for I = (h/3)*(f_1 + 4f_2 + 2f_3 + 4f_4 + ... + 4f_{N-1} + f_N) # error**2 = (h/3)**2 * [sum_k=0^(N-1)/2 ((2*Df_2k)**2 + (4*Df_2k+1)**2) - 3*(Df_0**2 + Df_N**2)] # = (h/3)**2 * [sum_k=0^(N-3)/2 ((2*Df_2k)**2 + (4*Df_2k+1)**2) - 3*Df_0**2 + Df_N**2] # with the last term being the correction for the end points # # In order to work with nd-arrays I need to get the axis of '- 3*Df0**2 + DfN**2' # but my numpy-foo is weak and I don't know how to extract this axis nicely: Hence # sum the single term with the axis argument :-( result = (dx/3.0)**2 * (numpy.add.reduce(((2*Df2k)**2 + (4*Df2k1)**2), axis) \ + numpy.add.reduce(-3*Df0**2 + DfN**2, axis)) else: logger.warning( "Approximating Simpson integration statistical error with the average spacing." ) dx = numpy.diff(x).mean() result = _simps_error2(dy, start, stop, None, dx, axis) return result
def _simps_error2(dy,start,stop,x,dx,axis): """Squared error on Simpson's rule integration. :Arguments: *dy* errors at function values *start*, *stop* first and last index at which a Simpson 3-point interval starts *x* abscissa values (provide if spacing not equal) *dx* constant spacing (is overridden by *dx*) *axis* axis in *dy* along which the data lie """ nd = len(dy.shape) if start is None: start = 0 step = 2 all = (slice(None),)*nd # check that stop is appropriate !!! slice2k = tupleset(all, axis, slice(start, stop, step)) # does NOT include last point stop+1 slice2k1 = tupleset(all, axis, slice(start+1, stop+1, step)) slice0 = tupleset(all, axis, slice(start, start+1)) # first point sliceN = tupleset(all, axis, slice(stop+1, stop+2)) # last point Df2k = dy[slice2k] # 2k Df2k1 = dy[slice2k1] # 2k+1 Df0 = dy[slice0] # first = 0 DfN = dy[sliceN] # last = N if x is None: # Even spaced Simpson's rule. # Simpson error propagation for I = (h/3)*(f_1 + 4f_2 + 2f_3 + 4f_4 + ... + 4f_{N-1} + f_N) # error**2 = (h/3)**2 * [sum_k=0^(N-1)/2 ((2*Df_2k)**2 + (4*Df_2k+1)**2) - 3*(Df_0**2 + Df_N**2)] # = (h/3)**2 * [sum_k=0^(N-3)/2 ((2*Df_2k)**2 + (4*Df_2k+1)**2) - 3*Df_0**2 + Df_N**2] # with the last term being the correction for the end points # # In order to work with nd-arrays I need to get the axis of '- 3*Df0**2 + DfN**2' # but my numpy-foo is weak and I don't know how to extract this axis nicely: Hence # sum the single term with the axis argument :-( result = (dx/3.0)**2 * (numpy.add.reduce(((2*Df2k)**2 + (4*Df2k1)**2), axis) \ + numpy.add.reduce(-3*Df0**2 + DfN**2, axis)) else: logger.warning("Approximating Simpson integration statistical error with the average spacing.") dx = numpy.diff(x).mean() result = _simps_error2(dy,start,stop,None,dx,axis) return result
def RombergMethod(y, dx, show=False): axis = -1 y = asarray(y) nd = len(y.shape) Nsamps = y.shape[axis] Ninterv = Nsamps - 1 n = 1 k = 0 while n < Ninterv: n <<= 1 k += 1 R = {} all = (slice(None), ) * nd slice0 = tupleset(all, axis, 0) slicem1 = tupleset(all, axis, -1) h = Ninterv * asarray(dx) * 1.0 R[(1, 1)] = (y[slice0] + y[slicem1]) / 2.0 * h slice_R = all start = stop = step = Ninterv for i in range(2, k + 1): start >>= 1 slice_R = tupleset(slice_R, axis, slice(start, stop, step)) step >>= 1 R[(i, 1)] = 0.5 * (R[(i - 1, 1)] + h * add.reduce(y[slice_R], axis)) for j in range(2, i + 1): R[(i,j)] = R[(i,j-1)] + \ (R[(i,j-1)]-R[(i-1,j-1)]) / ((1 << (2*(j-1)))-1) h = h / 2.0 if show: precis = 5 width = 8 formstr = "%" + str(width) + '.' + str(precis) + 'f' print('\nMétodo de Romberg') print('----------------------------------') for i in range(1, k + 1): for j in range(1, i + 1): print(formstr % R[(i, j)], end=' ') print() print('----------------------------------') return R[(k, k)]
def RombergMethod(y, dx, show=False): axis=-1 y = asarray(y) nd = len(y.shape) Nsamps = y.shape[axis] Ninterv = Nsamps-1 n = 1 k = 0 while n < Ninterv: n <<= 1 k += 1 R = {} all = (slice(None),) * nd slice0 = tupleset(all, axis, 0) slicem1 = tupleset(all, axis, -1) h = Ninterv*asarray(dx)*1.0 R[(1,1)] = (y[slice0] + y[slicem1])/2.0*h slice_R = all start = stop = step = Ninterv for i in range(2,k+1): start >>= 1 slice_R = tupleset(slice_R, axis, slice(start,stop,step)) step >>= 1 R[(i,1)] = 0.5*(R[(i-1,1)] + h*add.reduce(y[slice_R],axis)) for j in range(2,i+1): R[(i,j)] = R[(i,j-1)] + \ (R[(i,j-1)]-R[(i-1,j-1)]) / ((1 << (2*(j-1)))-1) h = h / 2.0 if show: precis = 5 width = 8 formstr = "%" + str(width) + '.' + str(precis)+'f' print('\nMétodo de Romberg') print('----------------------------------') for i in range(1,k+1): for j in range(1,i+1): print(formstr % R[(i,j)], end=' ') print() print('----------------------------------') return R[(k,k)]
def _trapz_error2(dy,start,stop,x,dx): # not tested nd = len(dy.shape) if start is None: start = 0 step = 1 all = (slice(None),)*nd slicek = tupleset(all, axis, slice(start, stop+2, step)) # all points; stop+2 ?? slice0 = tupleset(all, axis, slice(start, start+1)) # first point sliceN = tupleset(all, axis, slice(stop+1, stop+2)) # last point Dfk = dy[slicek] # 2k Df0 = dy[slice0] # first = 0 DfN = dy[sliceN] # last = N if x is None: # from error propagation of I = (h/2) * (f_1 + 2f_2 + 2f_3 + ... + 2f_{N-1) + f_N) result = (0.5*dx)**2 * (4 * numpy.add.reduce(Dfk**2, axis) - 3*(Df0**2 + DfN**2)) else: raise NotImplementedError return result
def _trapz_error2(dy, start, stop, x, dx): # not tested nd = len(dy.shape) if start is None: start = 0 step = 1 all = (slice(None), ) * nd slicek = tupleset(all, axis, slice(start, stop + 2, step)) # all points; stop+2 ?? slice0 = tupleset(all, axis, slice(start, start + 1)) # first point sliceN = tupleset(all, axis, slice(stop + 1, stop + 2)) # last point Dfk = dy[slicek] # 2k Df0 = dy[slice0] # first = 0 DfN = dy[sliceN] # last = N if x is None: # from error propagation of I = (h/2) * (f_1 + 2f_2 + 2f_3 + ... + 2f_{N-1) + f_N) result = (0.5 * dx)**2 * (4 * numpy.add.reduce(Dfk**2, axis) - 3 * (Df0**2 + DfN**2)) else: raise NotImplementedError return result
def _naive_simps_error2(dy,start,stop,x,dx,axis): """Simple squared error on Simpson's rule integration. The implementation assumes that the errors on the individual intervals are independent and hence their squares can be summed. This is wrong. DO NOT USE. :Arguments: *dy* errors at function values *start*, *stop* first and last index at which a Simpson 3-point interval starts *x* abscissa values (provide if spacing not equal) *dx* constant spacing (is overridden by *dx*) *axis* axis in *dy* along which the data lie """ import warnings warnings.warn("The Simpson error from un-even intervals is not correct. _naive_simps_error2() will be removed.", category=DeprecationWarning) nd = len(dy.shape) if start is None: start = 0 step = 2 all = (slice(None),)*nd slice0 = tupleset(all, axis, slice(start, stop, step)) slice1 = tupleset(all, axis, slice(start+1, stop+1, step)) slice2 = tupleset(all, axis, slice(start+2, stop+2, step)) Df1 = dy[slice0] # 2k Df2 = dy[slice1] # 2k-1 Df3 = dy[slice2] # 2k-2 if x is None: # Even spaced Simpson's rule. # Simpson error propgation for points 0 <= i <= 2M # error**2 = (h/3)**2 * sum_k=1^M dy[2k]**2 + (4*dy[2k-1])**2 + dy[2k-2]**2 # error**2 = (h/3)**2 * (Df1**2 + 4*Df2**2 + Df**2) h = dx result = numpy.add.reduce((h/3.0)**2 * (Df1**2+(4*Df2)**2+Df3**2), axis) else: # This is too naive: adds the squared errors for every individual Simpson-triplet but that misses # the correlations in errors from the overlapping endpoints. # for spacing h1 and h2 (evaluated in Sage from the integral of the Lagrange interpolating # polynomials and propagation of errors) # Z = h1**2 * h2 + h1 * h2**2 # error**2 = 1/(6*Z)**2 * ((2*h1**3*h2 + 3*h1**2*h2**2 - h2**4)**2*Df_1**2 # + (h1**4 - 3*h1**2*h2**2 - 2*h1*h2**3)**2*Df_3**2 # + (h1**4 + 4*h1**3*h2 + 6*h1**2*h2**2 + 4*h1*h2**3 + h2**4)**2*Df_2**2) h1 = x[slice1] - x[slice0] # check this! h2 = x[slice2] - x[slice1] #print 'h1', h1.shape, h1 #print 'h2', h2.shape, h2 Z = h1**2 * h2 + h1 * h2**2 #print 'Z ', Z #print 'Df1 ', Df1 #print 'Df2 ', Df2 #print 'Df2 ', Df2 result = numpy.add.reduce(1/(6*Z)**2 * (((2*h1**3*h2 + 3*h1**2*h2**2 - h2**4) * Df1)**2 \ + ((h1**4 + 4*h1**3*h2 + 6*h1**2*h2**2 + 4*h1*h2**3 + h2**4) * Df2)**2 \ + ((h1**4 - 3*h1**2*h2**2 - 2*h1*h2**3) * Df3)**2)) return result
def simps_error(dy, x=None, dx=1, axis=-1, even='avg'): """Error on integral evaluated with `Simpson's rule`_ from errors of points, *dy*. Evaluate the integral with :func:`scipy.integrate.simps`. For a given vector *dy* of errors on the function values, the error on the integral is calculated via `propagation of errors`_ from the `Newton-Cotes formula`_ for the 3rd `Lagrange interpolating polynomial`_. The results are exact for the cases of even spacing *dx*; for uneven spacing we currently average all spacings (exact solution is in the works...) :Arguments: *dy* errors for the tabulated values of the integrand f *x* values of abscissa at which f was tabulated (can be ``None`` and then *dx* should be provided) *dx* constant spacing of the abscissa *axis* axis in *dy* along which the data lies *even* see :func:`scipy.integrate.simps` ('avg', 'first', 'last') .. _Simpson's rule: http://mathworld.wolfram.com/SimpsonsRule.html .. _propagation of errors: http://mathworld.wolfram.com/ErrorPropagation.html .. _`Newton-Cotes formula`: http://mathworld.wolfram.com/Newton-CotesFormulas.html .. _Lagrange interpolating polynomial: http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html """ # copied basic structure from scipy.integrate.quadrature.simps dy = numpy.asarray(dy) nd = len(dy.shape) N = dy.shape[axis] if x is not None: x = numpy.asarray(x) if len(x.shape) == 1: shapex = numpy.ones(nd) shapex[axis] = x.shape[0] saveshape = x.shape returnshape = True x=x.reshape(tuple(shapex)) elif len(x.shape) != len(dy.shape): raise ValueError, "If given, shape of x must be 1-d or the " \ "same as dy." if x.shape[axis] != N: raise ValueError, "If given, length of x along axis must be the " \ "same as dy." else: last_dx = first_dx = dx avglast_dx = avgfirst_dx = dx returnshape = False if N % 2 == 0: val = 0.0 # holds trapezoidal error**2 result = 0.0 # holds Simpson error**2 correction = 0.0 # overlap correction for combining trapz and Simpson slice1 = (slice(None),)*nd slice2 = (slice(None),)*nd if not even in ['avg', 'last', 'first']: raise ValueError, \ "Parameter 'even' must be 'avg', 'last', or 'first'." if even in ['avg', 'first']: # Compute using Simpson's rule on first intervals slice0 = tupleset(slice1, axis, -3) slice1 = tupleset(slice1, axis, -2) slice2 = tupleset(slice2, axis, -1) if x is not None: last_dx = x[slice2] - x[slice1] penult_dx = x[slice1] - x[slice0] avglast_dx = 0.5*(penult_dx + last_dx) val += _trapz_2pt_error2(dy[slice1], dy[slice2], last_dx) result += _simps_error2(dy,0,N-3,x,dx,axis) correction += _trapz_simps_overlap2(dy[slice1], avglast_dx) # avglast_dx not strictly correct for x!=None! if even in ['avg', 'last']: # Compute using Simpson's rule on last set of intervals slice1 = tupleset(slice1, axis, 0) slice2 = tupleset(slice2, axis, 1) slice3 = tupleset(slice1, axis, 2) if x is not None: first_dx = x[slice2] - x[slice1] second_dx = x[slice3] - x[slice2] avgfirst_dx = 0.5*(second_dx + first_dx) val += _trapz_2pt_error2(dy[slice1], dy[slice2], first_dx) result += _simps_error2(dy,1,N-2,x,dx,axis) correction += _trapz_simps_overlap2(dy[slice2], avgfirst_dx) # avgfirst_dx not strictly correct for x!=None! if even == 'avg': # simply average the variances of 'odd' and 'even' because the two # data sets are not independent; hence it would be wrong to # estimate it as avg**2 = 0.5*sqrt(da1**2+da2**2) val /= 2.0 result /= 2.0 correction /= 2.0 result = result + val + correction # err_simps**2 + err_trapez**2 + correction else: result = _simps_error2(dy,0,N-2,x,dx,axis) if returnshape: x = x.reshape(saveshape) return numpy.sqrt(result)
def _naive_simps_error2(dy, start, stop, x, dx, axis): """Simple squared error on Simpson's rule integration. The implementation assumes that the errors on the individual intervals are independent and hence their squares can be summed. This is wrong. DO NOT USE. :Arguments: *dy* errors at function values *start*, *stop* first and last index at which a Simpson 3-point interval starts *x* abscissa values (provide if spacing not equal) *dx* constant spacing (is overridden by *dx*) *axis* axis in *dy* along which the data lie """ import warnings warnings.warn( "The Simpson error from un-even intervals is not correct. _naive_simps_error2() will be removed.", category=DeprecationWarning) nd = len(dy.shape) if start is None: start = 0 step = 2 all = (slice(None), ) * nd slice0 = tupleset(all, axis, slice(start, stop, step)) slice1 = tupleset(all, axis, slice(start + 1, stop + 1, step)) slice2 = tupleset(all, axis, slice(start + 2, stop + 2, step)) Df1 = dy[slice0] # 2k Df2 = dy[slice1] # 2k-1 Df3 = dy[slice2] # 2k-2 if x is None: # Even spaced Simpson's rule. # Simpson error propgation for points 0 <= i <= 2M # error**2 = (h/3)**2 * sum_k=1^M dy[2k]**2 + (4*dy[2k-1])**2 + dy[2k-2]**2 # error**2 = (h/3)**2 * (Df1**2 + 4*Df2**2 + Df**2) h = dx result = numpy.add.reduce( (h / 3.0)**2 * (Df1**2 + (4 * Df2)**2 + Df3**2), axis) else: # This is too naive: adds the squared errors for every individual Simpson-triplet but that misses # the correlations in errors from the overlapping endpoints. # for spacing h1 and h2 (evaluated in Sage from the integral of the Lagrange interpolating # polynomials and propagation of errors) # Z = h1**2 * h2 + h1 * h2**2 # error**2 = 1/(6*Z)**2 * ((2*h1**3*h2 + 3*h1**2*h2**2 - h2**4)**2*Df_1**2 # + (h1**4 - 3*h1**2*h2**2 - 2*h1*h2**3)**2*Df_3**2 # + (h1**4 + 4*h1**3*h2 + 6*h1**2*h2**2 + 4*h1*h2**3 + h2**4)**2*Df_2**2) h1 = x[slice1] - x[slice0] # check this! h2 = x[slice2] - x[slice1] #print 'h1', h1.shape, h1 #print 'h2', h2.shape, h2 Z = h1**2 * h2 + h1 * h2**2 #print 'Z ', Z #print 'Df1 ', Df1 #print 'Df2 ', Df2 #print 'Df2 ', Df2 result = numpy.add.reduce(1/(6*Z)**2 * (((2*h1**3*h2 + 3*h1**2*h2**2 - h2**4) * Df1)**2 \ + ((h1**4 + 4*h1**3*h2 + 6*h1**2*h2**2 + 4*h1*h2**3 + h2**4) * Df2)**2 \ + ((h1**4 - 3*h1**2*h2**2 - 2*h1*h2**3) * Df3)**2)) return result
def simps_error(dy, x=None, dx=1, axis=-1, even='avg'): """Error on integral evaluated with `Simpson's rule`_ from errors of points, *dy*. Evaluate the integral with :func:`scipy.integrate.simps`. For a given vector *dy* of errors on the function values, the error on the integral is calculated via `propagation of errors`_ from the `Newton-Cotes formula`_ for the 3rd `Lagrange interpolating polynomial`_. The results are exact for the cases of even spacing *dx*; for uneven spacing we currently average all spacings (exact solution is in the works...) :Arguments: *dy* errors for the tabulated values of the integrand f *x* values of abscissa at which f was tabulated (can be ``None`` and then *dx* should be provided) *dx* constant spacing of the abscissa *axis* axis in *dy* along which the data lies *even* see :func:`scipy.integrate.simps` ('avg', 'first', 'last') .. _Simpson's rule: http://mathworld.wolfram.com/SimpsonsRule.html .. _propagation of errors: http://mathworld.wolfram.com/ErrorPropagation.html .. _`Newton-Cotes formula`: http://mathworld.wolfram.com/Newton-CotesFormulas.html .. _Lagrange interpolating polynomial: http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html """ # copied basic structure from scipy.integrate.quadrature.simps dy = numpy.asarray(dy) nd = len(dy.shape) N = dy.shape[axis] if x is not None: x = numpy.asarray(x) if len(x.shape) == 1: shapex = numpy.ones(nd) shapex[axis] = x.shape[0] saveshape = x.shape returnshape = True x = x.reshape(tuple(shapex)) elif len(x.shape) != len(dy.shape): raise ValueError, "If given, shape of x must be 1-d or the " \ "same as dy." if x.shape[axis] != N: raise ValueError, "If given, length of x along axis must be the " \ "same as dy." else: last_dx = first_dx = dx avglast_dx = avgfirst_dx = dx returnshape = False if N % 2 == 0: val = 0.0 # holds trapezoidal error**2 result = 0.0 # holds Simpson error**2 correction = 0.0 # overlap correction for combining trapz and Simpson slice1 = (slice(None), ) * nd slice2 = (slice(None), ) * nd if not even in ['avg', 'last', 'first']: raise ValueError, \ "Parameter 'even' must be 'avg', 'last', or 'first'." if even in ['avg', 'first']: # Compute using Simpson's rule on first intervals slice0 = tupleset(slice1, axis, -3) slice1 = tupleset(slice1, axis, -2) slice2 = tupleset(slice2, axis, -1) if x is not None: last_dx = x[slice2] - x[slice1] penult_dx = x[slice1] - x[slice0] avglast_dx = 0.5 * (penult_dx + last_dx) val += _trapz_2pt_error2(dy[slice1], dy[slice2], last_dx) result += _simps_error2(dy, 0, N - 3, x, dx, axis) correction += _trapz_simps_overlap2( dy[slice1], avglast_dx) # avglast_dx not strictly correct for x!=None! if even in ['avg', 'last']: # Compute using Simpson's rule on last set of intervals slice1 = tupleset(slice1, axis, 0) slice2 = tupleset(slice2, axis, 1) slice3 = tupleset(slice1, axis, 2) if x is not None: first_dx = x[slice2] - x[slice1] second_dx = x[slice3] - x[slice2] avgfirst_dx = 0.5 * (second_dx + first_dx) val += _trapz_2pt_error2(dy[slice1], dy[slice2], first_dx) result += _simps_error2(dy, 1, N - 2, x, dx, axis) correction += _trapz_simps_overlap2( dy[slice2], avgfirst_dx) # avgfirst_dx not strictly correct for x!=None! if even == 'avg': # simply average the variances of 'odd' and 'even' because the two # data sets are not independent; hence it would be wrong to # estimate it as avg**2 = 0.5*sqrt(da1**2+da2**2) val /= 2.0 result /= 2.0 correction /= 2.0 result = result + val + correction # err_simps**2 + err_trapez**2 + correction else: result = _simps_error2(dy, 0, N - 2, x, dx, axis) if returnshape: x = x.reshape(saveshape) return numpy.sqrt(result)