From bfb1940cf5c7ce5c9a3b440d1efd8f8c4128fed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ingvar=20Dahlgren?= Date: Sat, 7 Aug 2021 08:32:01 +0200 Subject: [PATCH] formating using black --- examples/err.py | 43 +++-- examples/sine.py | 50 ++--- examples/tests/test_examples.py | 6 +- finitediff/__init__.py | 17 +- finitediff/_config.py | 2 +- finitediff/_release.py | 2 +- finitediff/grid/__init__.py | 7 +- finitediff/grid/make.py | 4 +- finitediff/grid/plotting.py | 26 ++- finitediff/grid/rebalance.py | 45 +++-- finitediff/grid/refine.py | 82 +++++--- finitediff/grid/tests/_common.py | 18 +- finitediff/grid/tests/test_make.py | 12 +- finitediff/grid/tests/test_rebalance.py | 30 ++- finitediff/grid/tests/test_refine.py | 12 +- finitediff/grid/tests/test_util.py | 5 +- finitediff/grid/util.py | 14 +- finitediff/tests/test_finitediff.py | 247 +++++++++++++++++------- finitediff/util.py | 32 +-- scripts/coverage_badge.py | 17 +- scripts/release.sh | 2 +- scripts/run_tests.sh | 2 +- setup.cfg | 3 +- setup.py | 141 ++++++++------ 24 files changed, 520 insertions(+), 299 deletions(-) diff --git a/examples/err.py b/examples/err.py index 8169101..a2da0ca 100644 --- a/examples/err.py +++ b/examples/err.py @@ -14,27 +14,34 @@ def demo_err(): """ max_order = 7 n = 20 - l = 0.25 - fmt1 = '{0: <5s}\t{1: <21s}\t{2: >21s}\t{3: >21s}\t{4: >21s}' - fmt2 = '{0: <5d}\t{1:20.18f}\t{2: >21.18f}\t{3: >21.18f}\t{4: >21.18f}' - x = np.cumsum(np.random.rand(n)*l) - x = np.concatenate((x[::-1]*-1, x)) + lp = 0.25 + fmt1 = "{0: <5s}\t{1: <21s}\t{2: >21s}\t{3: >21s}\t{4: >21s}" + fmt2 = "{0: <5d}\t{1:20.18f}\t{2: >21.18f}\t{3: >21.18f}\t{4: >21.18f}" + x = np.cumsum(np.random.rand(n) * lp) + x = np.concatenate((x[::-1] * -1, x)) lst = [] derivs = np.zeros(n) - for order in range(max_order+1): - print('Order', order) - for m in range(1+order//2, n+1): - sub_x = x[n-m:n+m] - derivs[m-1] = derivatives_at_point_by_finite_diff( - sub_x, np.exp(sub_x), 0, order)[order] - print(fmt1.format('m', 'val', 'diff', 'analytical error', - 'diff/analytical')) + for order in range(max_order + 1): + print("Order", order) + for m in range(1 + order // 2, n + 1): + sub_x = x[n - m : n + m] + derivs[m - 1] = derivatives_at_point_by_finite_diff( + sub_x, np.exp(sub_x), 0, order + )[order] + print(fmt1.format("m", "val", "diff", "analytical error", "diff/analytical")) for m in range(1, n): - print(fmt2.format( - (m+1)*2, derivs[m], derivs[m]-derivs[m-1], - derivs[m]-1, (derivs[m]-derivs[m-1])/(derivs[m]-1))) - lst.append((derivs[-1], abs(derivs[-1]-derivs[-2]))) + print( + fmt2.format( + (m + 1) * 2, + derivs[m], + derivs[m] - derivs[m - 1], + derivs[m] - 1, + (derivs[m] - derivs[m - 1]) / (derivs[m] - 1), + ) + ) + lst.append((derivs[-1], abs(derivs[-1] - derivs[-2]))) print(np.array(lst)) -if __name__ == '__main__': + +if __name__ == "__main__": demo_err() diff --git a/examples/sine.py b/examples/sine.py index 6db7b64..538fa6e 100644 --- a/examples/sine.py +++ b/examples/sine.py @@ -5,9 +5,7 @@ import numpy as np -from finitediff import ( - derivatives_at_point_by_finite_diff, interpolate_by_finite_diff -) +from finitediff import derivatives_at_point_by_finite_diff, interpolate_by_finite_diff def demo_usage(n_data=50, n_fit=537, nhead=5, ntail=5, plot=False, alt=0): @@ -28,15 +26,17 @@ def demo_usage(n_data=50, n_fit=537, nhead=5, ntail=5, plot=False, alt=0): x0, xend = 0, 5 # shaky linspace -5% to +5% noise - x_data = (np.linspace(x0, xend, n_data) + - np.random.rand(n_data)*(xend-x0)/n_data/1.5) - y_data = np.sin(x_data) * (1.0+0.1*(np.random.rand(n_data)-0.5)) + x_data = ( + np.linspace(x0, xend, n_data) + + np.random.rand(n_data) * (xend - x0) / n_data / 1.5 + ) + y_data = np.sin(x_data) * (1.0 + 0.1 * (np.random.rand(n_data) - 0.5)) x_fit = np.linspace(x0, xend, n_fit) # Edges behave badly, work around: - x_fit[0] = x_fit[0] + (x_fit[1]-x_fit[0])/2 - x_fit[-1] = x_fit[-2]+(x_fit[-1]-x_fit[-2])/2 + x_fit[0] = x_fit[0] + (x_fit[1] - x_fit[0]) / 2 + x_fit[-1] = x_fit[-2] + (x_fit[-1] - x_fit[-2]) / 2 if alt: y_fit = np.empty(n_fit) @@ -44,17 +44,16 @@ def demo_usage(n_data=50, n_fit=537, nhead=5, ntail=5, plot=False, alt=0): for i, xf in enumerate(x_fit): # get index j of first data point beyond xf j = np.where(x_data > xf)[0][0] - lower_bound = max(0, j-alt) - upper_bound = min(n_data-1, j+alt) + lower_bound = max(0, j - alt) + upper_bound = min(n_data - 1, j + alt) y_fit[i] = derivatives_at_point_by_finite_diff( - x_data[lower_bound:upper_bound], - y_data[lower_bound:upper_bound], xf, 0) + x_data[lower_bound:upper_bound], y_data[lower_bound:upper_bound], xf, 0 + ) dydx_fit[i] = derivatives_at_point_by_finite_diff( - x_data[lower_bound:upper_bound], - y_data[lower_bound:upper_bound], xf, 1)[1] + x_data[lower_bound:upper_bound], y_data[lower_bound:upper_bound], xf, 1 + )[1] else: - interp = interpolate_by_finite_diff(x_data, y_data, x_fit, - 1, nhead, ntail) + interp = interpolate_by_finite_diff(x_data, y_data, x_fit, 1, nhead, ntail) y_fit = interp[:, 0] dydx_fit = interp[:, 1] @@ -62,30 +61,33 @@ def demo_usage(n_data=50, n_fit=537, nhead=5, ntail=5, plot=False, alt=0): import matplotlib.pyplot as plt plt.subplot(221) - plt.plot(x_data, y_data, 'x', label='Data points (sin)') - plt.plot(x_fit, y_fit, '-', label='Fitted curve (order=0)') - plt.plot(x_data, np.sin(x_data), '-', label='Analytic sin(x)') + plt.plot(x_data, y_data, "x", label="Data points (sin)") + plt.plot(x_fit, y_fit, "-", label="Fitted curve (order=0)") + plt.plot(x_data, np.sin(x_data), "-", label="Analytic sin(x)") plt.legend() plt.subplot(222) - plt.plot(x_fit, y_fit-np.sin(x_fit), label='Error in order=0') + plt.plot(x_fit, y_fit - np.sin(x_fit), label="Error in order=0") plt.legend() plt.subplot(223) - plt.plot(x_fit, dydx_fit, '-', label='Fitted derivative (order=1)') - plt.plot(x_data, np.cos(x_data), '-', label='Analytic cos(x)') + plt.plot(x_fit, dydx_fit, "-", label="Fitted derivative (order=1)") + plt.plot(x_data, np.cos(x_data), "-", label="Analytic cos(x)") plt.legend() plt.subplot(224) - plt.plot(x_fit, dydx_fit-np.cos(x_fit), label='Error in order=1') + plt.plot(x_fit, dydx_fit - np.cos(x_fit), label="Error in order=1") plt.legend() plt.show() -if __name__ == '__main__': + +if __name__ == "__main__": try: from argh import dispatch_command except ImportError: + def dispatch_command(cb): return cb() + dispatch_command(demo_usage) diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 2688430..f3e048b 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -14,11 +14,11 @@ import pytest -tests = glob.glob(os.path.join(os.path.dirname(__file__), '../*.py')) +tests = glob.glob(os.path.join(os.path.dirname(__file__), "../*.py")) -@pytest.mark.parametrize('pypath', tests) +@pytest.mark.parametrize("pypath", tests) def test_examples(pypath): - py_exe = 'python3' if sys.version_info.major == 3 else 'python' + py_exe = "python3" if sys.version_info.major == 3 else "python" p = subprocess.Popen([py_exe, pypath]) assert p.wait() == 0 # SUCCESS==0 diff --git a/finitediff/__init__.py b/finitediff/__init__.py index 965f1d9..16cb96c 100644 --- a/finitediff/__init__.py +++ b/finitediff/__init__.py @@ -2,19 +2,24 @@ """ Finite difference weights for any derivative order on arbitrarily spaced grids. """ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from ._release import __version__ from ._finitediff_c import ( - derivatives_at_point_by_finite_diff, interpolate_by_finite_diff, - get_weights + derivatives_at_point_by_finite_diff, + interpolate_by_finite_diff, + get_weights, ) -__all__ = ['derivatives_at_point_by_finite_diff', 'interpolate_by_finite_diff', 'get_weights'] +__all__ = [ + "derivatives_at_point_by_finite_diff", + "interpolate_by_finite_diff", + "get_weights", +] def get_include(): from pkg_resources import resource_filename, Requirement - return resource_filename(Requirement.parse(__name__), - '%s/include' % __name__) + + return resource_filename(Requirement.parse(__name__), "%s/include" % __name__) diff --git a/finitediff/_config.py b/finitediff/_config.py index 4a0ca9b..f7c5e2d 100644 --- a/finitediff/_config.py +++ b/finitediff/_config.py @@ -1 +1 @@ -env = {'USE_FORTRAN': '0'} +env = {"USE_FORTRAN": "0"} diff --git a/finitediff/_release.py b/finitediff/_release.py index 48835a7..2ad8f14 100644 --- a/finitediff/_release.py +++ b/finitediff/_release.py @@ -1 +1 @@ -__version__ = '0.6.5.dev0+git' +__version__ = "0.6.5.dev0+git" diff --git a/finitediff/grid/__init__.py b/finitediff/grid/__init__.py index dcf02b0..8a4df96 100644 --- a/finitediff/grid/__init__.py +++ b/finitediff/grid/__init__.py @@ -1,4 +1,9 @@ -from .rebalance import rebalanced_grid, pre_pruning_mask, combine_grids, grid_pruning_mask +from .rebalance import ( + rebalanced_grid, + pre_pruning_mask, + combine_grids, + grid_pruning_mask, +) from .refine import refine_grid from .make import adapted_grid from .util import locate_discontinuity, pool_discontinuity_approx, grid_error diff --git a/finitediff/grid/make.py b/finitediff/grid/make.py index 238390f..6542e9f 100644 --- a/finitediff/grid/make.py +++ b/finitediff/grid/make.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from .refine import refine_grid def adapted_grid(xstart, xstop, cb, grid_additions=(50, 50), **kwargs): - """" Creates an adapted (1D) grid by subsequent subgrid insertions. + """ " Creates an adapted (1D) grid by subsequent subgrid insertions. Parameters ---------- diff --git a/finitediff/grid/plotting.py b/finitediff/grid/plotting.py index 0f4a096..7215ed9 100644 --- a/finitediff/grid/plotting.py +++ b/finitediff/grid/plotting.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from .make import adapted_grid @@ -10,8 +10,9 @@ def plot_convergence(key, values, cb, metric=None, xstart=0, xstop=2, **kwargs): if key in kwargs: raise ValueError("Cannot have key=%s when given in kwargs" % key) - fig, axes = plt.subplots(1, len(values), figsize=(16, 5), - sharey=True, gridspec_kw={'wspace': 0}) + fig, axes = plt.subplots( + 1, len(values), figsize=(16, 5), sharey=True, gridspec_kw={"wspace": 0} + ) scores, grid_sizes = [], [] if key is None and len(values) != 1: raise ValueError("Not considering key") @@ -19,13 +20,20 @@ def plot_convergence(key, values, cb, metric=None, xstart=0, xstop=2, **kwargs): if key is not None: kwargs[key] = val grid, results = adapted_grid(xstart, xstop, cb, metric=metric, **kwargs) - y = results if metric is None else np.array( - [metric(r) for r in results]) - ax.vlines(grid, 0, 1, transform=ax.get_xaxis_transform(), - linestyle='--', color='k', alpha=.3, linewidth=.5) + y = results if metric is None else np.array([metric(r) for r in results]) + ax.vlines( + grid, + 0, + 1, + transform=ax.get_xaxis_transform(), + linestyle="--", + color="k", + alpha=0.3, + linewidth=0.5, + ) ax.plot(grid, y) - between_x = grid[:-1] + np.diff(grid)/2 - between_y = y[:-1] + np.diff(y)/2 + between_x = grid[:-1] + np.diff(grid) / 2 + between_y = y[:-1] + np.diff(y) / 2 rbx = cb(between_x) ybx = rbx if metric is None else np.array([metric(r) for r in rbx]) scores.append(np.sum(np.abs(between_y - ybx))) diff --git a/finitediff/grid/rebalance.py b/finitediff/grid/rebalance.py index ac80a5c..ab56825 100644 --- a/finitediff/grid/rebalance.py +++ b/finitediff/grid/rebalance.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import math import numpy as np @@ -10,31 +10,36 @@ def _avgdiff(x): dx = np.diff(x) dx2 = np.zeros_like(x) dx2[0], dx2[-1] = dx[0], dx[-1] - dx2[1:-1] = 0.5*(dx[1:] + dx[:-1]) + dx2[1:-1] = 0.5 * (dx[1:] + dx[:-1]) return dx2 -def rebalanced_grid(grid, err, base=0.25, num=None, resolution_factor=10, smooth_fact=1.0): +def rebalanced_grid( + grid, err, base=0.25, num=None, resolution_factor=10, smooth_fact=1.0 +): if num is None: num = grid.size dx = np.diff(grid) - area_err = 0.5*np.dot(err[1:] + err[:-1], dx) # trapezoidal rule + area_err = 0.5 * np.dot(err[1:] + err[:-1], dx) # trapezoidal rule dx2 = _avgdiff(grid) def smooth_err(x): tot = 0 for i, (gx, e) in enumerate(zip(grid, err)): - fwhm = dx2[i]*smooth_fact - tot += e*np.exp(-(x-gx)**2/(2*(fwhm/2.35482)**2)) + fwhm = dx2[i] * smooth_fact + tot += e * np.exp(-((x - gx) ** 2) / (2 * (fwhm / 2.35482) ** 2)) return tot - finegrid = np.zeros((grid.size-1) * resolution_factor + 1) - for i in range(grid.size-1): - finegrid[i*resolution_factor:(i+1)*resolution_factor] = np.linspace( - grid[i], grid[i+1], resolution_factor+1)[:-1] - finegrid[-resolution_factor-1:] = np.linspace(grid[-2], grid[-1], resolution_factor + 1) - smoothed = smooth_err(finegrid) + base*area_err/(grid[-1] - grid[0]) + finegrid = np.zeros((grid.size - 1) * resolution_factor + 1) + for i in range(grid.size - 1): + finegrid[i * resolution_factor : (i + 1) * resolution_factor] = np.linspace( + grid[i], grid[i + 1], resolution_factor + 1 + )[:-1] + finegrid[-resolution_factor - 1 :] = np.linspace( + grid[-2], grid[-1], resolution_factor + 1 + ) + smoothed = smooth_err(finegrid) + base * area_err / (grid[-1] - grid[0]) assert np.all(smoothed > 0) assert np.all(_avgdiff(finegrid) > 0) interr = np.cumsum(smoothed * _avgdiff(finegrid)) @@ -43,7 +48,7 @@ def smooth_err(x): def pre_pruning_mask(grid, rtol=1e-12, atol=0.0): - """ Returns a mask for grid pruning. + """Returns a mask for grid pruning. Any grid spacing smaller than ``rtol*gridvalue + atol`` will be pruned. In general the value on the right is removed unless it is @@ -62,10 +67,10 @@ def pre_pruning_mask(grid, rtol=1e-12, atol=0.0): """ if np.any(np.diff(grid) < 0): raise ValueError("grid needs to be monotonic") - limit = grid[-1] - (atol + abs(rtol*grid[-1])) + limit = grid[-1] - (atol + abs(rtol * grid[-1])) mask = np.empty(grid.size, dtype=np.bool_) mask[grid.size - 1] = True # rightmost point included - for ridx in range(grid.size-2, -1, -1): + for ridx in range(grid.size - 2, -1, -1): if grid[ridx] < limit: mask[ridx] = True break @@ -74,18 +79,18 @@ def pre_pruning_mask(grid, rtol=1e-12, atol=0.0): else: raise ValueError("no grid-points left") mask[0] = True # leftmost point included - limit = grid[0] + abs(rtol*grid[0]) + atol + limit = grid[0] + abs(rtol * grid[0]) + atol for idx in range(1, ridx): if grid[idx] < limit: mask[idx] = False else: mask[idx] = True - limit = grid[idx] + abs(rtol*grid[idx]) + atol + limit = grid[idx] + abs(rtol * grid[idx]) + atol return mask def combine_grids(grids, **kwargs): - """ Combines multiple grids and prunes them using pre_pruning mask + """Combines multiple grids and prunes them using pre_pruning mask Parameters ---------- @@ -104,7 +109,7 @@ def combine_grids(grids, **kwargs): def grid_pruning_mask(grid, err, ndrop=None, protect_sparse=None, pow_err=2, pow_dx=2): - """ Returns a mask for grid pruning. + """Returns a mask for grid pruning. Parameters ---------- @@ -126,7 +131,7 @@ def grid_pruning_mask(grid, err, ndrop=None, protect_sparse=None, pow_err=2, pow protect_sparse = math.ceil(grid.size * 0.25) dx = _avgdiff(grid) protected = np.argsort(dx)[-protect_sparse:] - score = err**pow_err * dx**pow_dx + score = err ** pow_err * dx ** pow_dx importance = np.argsort(score) drop = [] for considered in importance: diff --git a/finitediff/grid/refine.py b/finitediff/grid/refine.py index c53e58b..6d19140 100644 --- a/finitediff/grid/refine.py +++ b/finitediff/grid/refine.py @@ -1,14 +1,24 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from ..util import interpolate_ahead -def refine_grid(grid, cb, grid_additions=(50, 50), ntrail=2, blurs=((), ()), metric=None, - atol=None, rtol=None, extremum_refinement=None, snr=False): - """ Refines an existing grid by adding points to it. +def refine_grid( + grid, + cb, + grid_additions=(50, 50), + ntrail=2, + blurs=((), ()), + metric=None, + atol=None, + rtol=None, + extremum_refinement=None, + snr=False, +): + """Refines an existing grid by adding points to it. Parameters ---------- @@ -39,10 +49,10 @@ def refine_grid(grid, cb, grid_additions=(50, 50), ntrail=2, blurs=((), ()), met """ for na in grid_additions: if (na % 2) != 0: - raise ValueError('Need even number of grid points for each addition') - if extremum_refinement == 'max': + raise ValueError("Need even number of grid points for each addition") + if extremum_refinement == "max": extremum_refinement = (np.argmax, 1, lambda y, i: True) - elif extremum_refinement == 'min': + elif extremum_refinement == "min": extremum_refinement = (np.argmin, 1, lambda y, i: True) def add_to(adds, grd, res, ys): @@ -58,14 +68,18 @@ def add_to(adds, grd, res, ys): ptr = 1 yslices = [] for gi, nloc in enumerate(adds): - nextgrid[ptr:ptr+nloc+1] = np.linspace(grd[gi], grd[gi+1], 2+nloc)[1:] - nextresults[ptr+nloc] = res[gi+1] - nexty[ptr+nloc] = ys[gi+1] + nextgrid[ptr : ptr + nloc + 1] = np.linspace( + grd[gi], grd[gi + 1], 2 + nloc + )[1:] + nextresults[ptr + nloc] = res[gi + 1] + nexty[ptr + nloc] = ys[gi + 1] if nloc > 0: - yslices.append(slice(ptr, ptr+nloc)) + yslices.append(slice(ptr, ptr + nloc)) ptr += nloc + 1 newresults = cb(np.concatenate([nextgrid[yslc] for yslc in yslices])) - newy = newresults if metric is None else np.array([metric(r) for r in newresults]) + newy = ( + newresults if metric is None else np.array([metric(r) for r in newresults]) + ) ystart, ystop = 0, 0 for yslc in yslices: ystop += yslc.stop - yslc.start @@ -76,7 +90,9 @@ def add_to(adds, grd, res, ys): return nextgrid, nextresults, nexty results = cb(grid) - y = np.array(results if metric is None else [metric(r) for r in results], dtype=np.float64) + y = np.array( + results if metric is None else [metric(r) for r in results], dtype=np.float64 + ) for na in grid_additions: if extremum_refinement: @@ -85,7 +101,7 @@ def add_to(adds, grd, res, ys): if predicate_cb(y, argext): additions = np.zeros(grid.size - 1, dtype=int) if argext > 0: # left of - additions[argext-1] = extremum_n + additions[argext - 1] = extremum_n elif argext < grid.size - 1: # right of additions[argext] = extremum_n grid, results, y = add_to(additions, grid, results, y) @@ -93,24 +109,26 @@ def add_to(adds, grd, res, ys): additions = np.zeros(grid.size - 1, dtype=int) done = True if atol is not None or rtol is not None else False slcs, errs = [], [] - for direction in ('fw', 'bw'): + for direction in ("fw", "bw"): est, slc = interpolate_ahead(grid, y, ntrail, direction) err = np.abs(y[slc] - est) if atol is not None: done = done and np.all(err < atol) if rtol is not None: - done = done and np.all(err/y[slc] < rtol) + done = done and np.all(err / y[slc] < rtol) slcs.append(slc) errs.append(err) if snr: - all_errs = np.array([[.0]*ntrail + errs[0].tolist(), errs[1].tolist() + [.0]*ntrail]) - min__max = np.amin(all_errs, axis=0)/np.amax(all_errs, axis=0) + all_errs = np.array( + [[0.0] * ntrail + errs[0].tolist(), errs[1].tolist() + [0.0] * ntrail] + ) + min__max = np.amin(all_errs, axis=0) / np.amax(all_errs, axis=0) dgrid = np.diff(grid) delta = np.empty_like(grid) - delta[0] = dgrid[0]**-2 - delta[-1] = dgrid[-1]**-2 - delta[1:-1] = 1/(dgrid[:-1]*dgrid[1:]) + delta[0] = dgrid[0] ** -2 + delta[-1] = dgrid[-1] ** -2 + delta[1:-1] = 1 / (dgrid[:-1] * dgrid[1:]) lndelta = np.log(delta) normlndelta = lndelta - np.max(lndelta) @@ -118,21 +136,27 @@ def add_to(adds, grd, res, ys): errs[i] *= (1.0 + 1e-8) - min__max[slcs[i]] errs[i] *= np.exp(normlndelta[slcs[i]]) - for direction, blur, slc, err in zip(('fw', 'bw'), blurs, slcs, errs): + for direction, blur, slc, err in zip(("fw", "bw"), blurs, slcs, errs): for ib, b in enumerate(blur, 1): blur_slices = (slice(ib, None), slice(None, -ib)) - err[blur_slices[direction == 'bw']] += b*err[blur_slices[direction == 'fw']] - rerr = np.array(np.round(err*na/2/np.sum(err)), dtype=int) - delta = np.sum(rerr) - na//2 + err[blur_slices[direction == "bw"]] += ( + b * err[blur_slices[direction == "fw"]] + ) + rerr = np.array(np.round(err * na / 2 / np.sum(err)), dtype=int) + delta = np.sum(rerr) - na // 2 if delta == 0: pass else: sorted_indices = np.argsort(rerr) - for i in sorted_indices[-abs(delta):]: + for i in sorted_indices[-abs(delta) :]: rerr[i] += 1 if delta < 0 else -1 - if (np.sum(rerr) - na//2): - raise ValueError('Balancing failed.') - additions[slice(ntrail-1, None) if direction == 'fw' else slice(None, 1-ntrail)] += rerr + if np.sum(rerr) - na // 2: + raise ValueError("Balancing failed.") + additions[ + slice(ntrail - 1, None) + if direction == "fw" + else slice(None, 1 - ntrail) + ] += rerr grid, results, y = add_to(additions, grid, results, y) if done: break diff --git a/finitediff/grid/tests/_common.py b/finitediff/grid/tests/_common.py index d1f249a..2f8e07c 100644 --- a/finitediff/grid/tests/_common.py +++ b/finitediff/grid/tests/_common.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import math import numpy as np def sigm(x, m, o, n): - return m*(x-o)*((m*(x-o))**n + 1)**-(1./n) + return m * (x - o) * ((m * (x - o)) ** n + 1) ** -(1.0 / n) def g(x): @@ -14,15 +14,19 @@ def g(x): s2 = sigm(x, 5, 1, 4) g.nelem_call += x.size g.nfev += 1 - return 10*(1-s2**2)*np.exp(20*s)/np.exp(20*x)+0.1*(x-3)**2 + return 10 * (1 - s2 ** 2) * np.exp(20 * s) / np.exp(20 * x) + 0.1 * (x - 3) ** 2 + g.nelem_call = 0 g.nfev = 0 def g2(x): - return g(2*x) + g(x) + return g(2 * x) + g(x) + -g3 = np.vectorize(lambda t: ( - math.exp(-t) + (.2 if round(t*1234567) % 3 == 0 else 0) -) if t < 5 else 0.5*math.exp(5-t)) # noisy function +g3 = np.vectorize( + lambda t: (math.exp(-t) + (0.2 if round(t * 1234567) % 3 == 0 else 0)) + if t < 5 + else 0.5 * math.exp(5 - t) +) # noisy function diff --git a/finitediff/grid/tests/test_make.py b/finitediff/grid/tests/test_make.py index 49fb36f..144fb37 100644 --- a/finitediff/grid/tests/test_make.py +++ b/finitediff/grid/tests/test_make.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from ..make import adapted_grid @@ -7,18 +7,18 @@ def test_adapted_grid(): - ag, y = adapted_grid(0, 2, g, (8,)*4) + ag, y = adapted_grid(0, 2, g, (8,) * 4) assert ag.shape == (32,) assert y.shape == (32,) def test_adapted_grid__performance(): r = [] - for ga in [(32,), (16,)*2, (8,)*4]: + for ga in [(32,), (16,) * 2, (8,) * 4]: grid, y = adapted_grid(0, 2, g, ga) - bx = grid[:-1] + np.diff(grid)/2 - by = y[:-1] + np.diff(y)/2 + bx = grid[:-1] + np.diff(grid) / 2 + by = y[:-1] + np.diff(y) / 2 r.append(np.sum(np.abs(by - g(bx)))) - assert np.all(r < [.272, 0.25, .15]) + assert np.all(r < [0.272, 0.25, 0.15]) assert np.all(np.diff(r) < 0) diff --git a/finitediff/grid/tests/test_rebalance.py b/finitediff/grid/tests/test_rebalance.py index f4e8ed2..5a97325 100644 --- a/finitediff/grid/tests/test_rebalance.py +++ b/finitediff/grid/tests/test_rebalance.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np import pytest @@ -8,15 +8,27 @@ def test_pre_pruning_mask(): - assert np.all(pre_pruning_mask(np.array([0., 1e+00, 2e+00, 3, 4])) == [True]*5) - assert np.all(pre_pruning_mask(np.array([0., 1e-14, 2e+00, 3, 4]), atol=1e-12) == [True, False, True, True, True]) - assert np.all(pre_pruning_mask(np.array([0., 1e-14, 2e-14, 3, 4]), atol=1e-12) == [True, False, False, True, True]) - assert np.all(pre_pruning_mask(np.array([0., 1e+00, 2e+00, 4, 4 + 2e-12])) == [True, True, True, False, True]) - assert np.all(pre_pruning_mask(np.array([0., 1e+00, 4, 4 + 1e-12, 4 + 2e-12])) == [True, True, False, False, True]) + assert np.all(pre_pruning_mask(np.array([0.0, 1e00, 2e00, 3, 4])) == [True] * 5) assert np.all( - pre_pruning_mask(np.array([0., 1e-14, 2e+00, 4, 4 + 2e-12]), - atol=1e-12) == [True, False, True, False, True] + pre_pruning_mask(np.array([0.0, 1e-14, 2e00, 3, 4]), atol=1e-12) + == [True, False, True, True, True] + ) + assert np.all( + pre_pruning_mask(np.array([0.0, 1e-14, 2e-14, 3, 4]), atol=1e-12) + == [True, False, False, True, True] + ) + assert np.all( + pre_pruning_mask(np.array([0.0, 1e00, 2e00, 4, 4 + 2e-12])) + == [True, True, True, False, True] + ) + assert np.all( + pre_pruning_mask(np.array([0.0, 1e00, 4, 4 + 1e-12, 4 + 2e-12])) + == [True, True, False, False, True] + ) + assert np.all( + pre_pruning_mask(np.array([0.0, 1e-14, 2e00, 4, 4 + 2e-12]), atol=1e-12) + == [True, False, True, False, True] ) with pytest.raises(ValueError): - assert pre_pruning_mask(np.array([1., 1 + 1e-13, 1 + 2e-13])) + assert pre_pruning_mask(np.array([1.0, 1 + 1e-13, 1 + 2e-13])) diff --git a/finitediff/grid/tests/test_refine.py b/finitediff/grid/tests/test_refine.py index 3079784..d6352e8 100644 --- a/finitediff/grid/tests/test_refine.py +++ b/finitediff/grid/tests/test_refine.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from ..refine import refine_grid @@ -8,19 +8,19 @@ def test_refine_grid(): grid = np.linspace(0, 2, 8) - rg, y = refine_grid(grid, g, (8,)*3) + rg, y = refine_grid(grid, g, (8,) * 3) assert rg.shape == (32,) assert y.shape == (32,) def test_refine_grid__performance(): r = [] - for ga in [(16,)*2, (8,)*4]: + for ga in [(16,) * 2, (8,) * 4]: grid = np.linspace(0, 2, ga[0]) grid, y = refine_grid(grid, g, ga[1:]) - bx = grid[:-1] + np.diff(grid)/2 - by = y[:-1] + np.diff(y)/2 + bx = grid[:-1] + np.diff(grid) / 2 + by = y[:-1] + np.diff(y) / 2 r.append(np.sum(np.abs(by - g(bx)))) - assert np.all(r < [.272, 0.25, .15]) + assert np.all(r < [0.272, 0.25, 0.15]) assert np.all(np.diff(r) < 0) diff --git a/finitediff/grid/tests/test_util.py b/finitediff/grid/tests/test_util.py index d25b0c8..fefaccf 100644 --- a/finitediff/grid/tests/test_util.py +++ b/finitediff/grid/tests/test_util.py @@ -5,8 +5,9 @@ def test_locate_discontinuity__pool_discontinuity_approx(): for snr in [False, True]: - loc_res = locate_discontinuity(*adapted_grid(0, 10, g3, grid_additions=(16,)*8, snr=snr), - consider=5) + loc_res = locate_discontinuity( + *adapted_grid(0, 10, g3, grid_additions=(16,) * 8, snr=snr), consider=5 + ) avg, s = pool_discontinuity_approx(loc_res) if snr: assert abs(avg - 5) < 0.03 diff --git a/finitediff/grid/util.py b/finitediff/grid/util.py index c61fb1b..8346fd8 100644 --- a/finitediff/grid/util.py +++ b/finitediff/grid/util.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from ..util import interpolate_ahead, avg_stddev @@ -11,11 +11,11 @@ def locate_discontinuity(grid, y, consider, trnsfm=lambda x: x, ntrail=2): tg = trnsfm(grid) dtg = np.diff(tg) err = np.zeros(y.size) - for d in ('fw', 'bw'): + for d in ("fw", "bw"): est, slc = interpolate_ahead(tg, y, ntrail, d) - start = (ntrail - 1) if d == 'fw' else 0 - stop = -(ntrail - 1) if d == 'bw' else None - err[slc] += np.abs(y[slc] - est)/dtg[start:stop]*dy[start:stop] + start = (ntrail - 1) if d == "fw" else 0 + stop = -(ntrail - 1) if d == "bw" else None + err[slc] += np.abs(y[slc] - est) / dtg[start:stop] * dy[start:stop] imax = np.argsort(err)[-consider:][::-1] return [(tg[m], err[m]) for m in imax] @@ -27,7 +27,7 @@ def pool_discontinuity_approx(loc_res, consistency_criterion=10): def grid_error(grid, y, ntrail=2): - """ Estimates error at each grid point from extrapolation. + """Estimates error at each grid point from extrapolation. Extrapolates from left ('fw') or from right ('bw'). @@ -39,5 +39,5 @@ def grid_error(grid, y, ntrail=2): Number of points to include in the look-ahead extrapolation. """ - est, _ = interpolate_ahead(grid, y, ntrail, direction='both') + est, _ = interpolate_ahead(grid, y, ntrail, direction="both") return est - y diff --git a/finitediff/tests/test_finitediff.py b/finitediff/tests/test_finitediff.py index c4f11ff..c0b23ba 100644 --- a/finitediff/tests/test_finitediff.py +++ b/finitediff/tests/test_finitediff.py @@ -1,37 +1,35 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import ( - print_function, division, absolute_import, unicode_literals -) +from __future__ import print_function, division, absolute_import, unicode_literals import numpy as np from finitediff import ( - interpolate_by_finite_diff, derivatives_at_point_by_finite_diff, - get_weights + interpolate_by_finite_diff, + derivatives_at_point_by_finite_diff, + get_weights, ) def test_derivatives_at_point_by_finite_diff(): out = derivatives_at_point_by_finite_diff( - np.array([.0, .5, 1.]), - np.array([.0, .25, 1.]), .5, 2) # y=x**2 - assert np.allclose(np.array([.25, 1.0, 2.0]), out) + np.array([0.0, 0.5, 1.0]), np.array([0.0, 0.25, 1.0]), 0.5, 2 + ) # y=x**2 + assert np.allclose(np.array([0.25, 1.0, 2.0]), out) - x = np.array([.0, .5, 1., 1.5, 2.0]) + x = np.array([0.0, 0.5, 1.0, 1.5, 2.0]) def f(x): - return 3 + x - x**3 + return 3 + x - x ** 3 pnt = 1.25 - out = derivatives_at_point_by_finite_diff( - x, f(x), pnt, 2) + out = derivatives_at_point_by_finite_diff(x, f(x), pnt, 2) def Df(x): - return 1 - 3*x**2 + return 1 - 3 * x ** 2 def D2f(x): - return -6*x + return -6 * x assert np.allclose([f(pnt), Df(pnt), D2f(pnt)], out) @@ -40,36 +38,42 @@ def test_interpolate_by_finite_diff(): xarr = np.linspace(-1.5, 1.7, 53) yarr = np.exp(xarr) xtest = np.linspace(-1.4, 1.6, 57) - y = interpolate_by_finite_diff(xarr.tolist(), yarr.tolist(), xtest.tolist(), - maxorder=4, ntail=5, nhead=5) - if __name__ == '__main__': + y = interpolate_by_finite_diff( + xarr.tolist(), yarr.tolist(), xtest.tolist(), maxorder=4, ntail=5, nhead=5 + ) + if __name__ == "__main__": import matplotlib.pyplot as plt + for ci in range(y.shape[1]): - plt.plot(xtest, y[:, ci]-np.exp(xtest)) + plt.plot(xtest, y[:, ci] - np.exp(xtest)) plt.show() yexact = np.exp(xtest) for ci in range(y.shape[1]): - tol = 10**-(13-ci*2) - assert np.allclose(yexact, y[:, ci], - rtol=tol, atol=tol) + tol = 10 ** -(13 - ci * 2) + assert np.allclose(yexact, y[:, ci], rtol=tol, atol=tol) def test_get_weights(): - c = get_weights(np.array([5., 6., 7.]), 5, maxorder=1) - assert np.allclose(c[:, 1], np.array([-3/2, 2, -1/2])) + c = get_weights(np.array([5.0, 6.0, 7.0]), 5, maxorder=1) + assert np.allclose(c[:, 1], np.array([-3 / 2, 2, -1 / 2])) # Table 1, p. 702 in doi:10.1090/S0025-5718-1988-0935077-0 # -------------------------------------------------------- # x = [[0], [-1, 0, 1], ...] - xl = [[j for j in range(-i, i+1)] for i in range(0, 5)] + xl = [[j for j in range(-i, i + 1)] for i in range(0, 5)] # d holds all coefficients - d = [get_weights(np.array(xl[i], dtype=np.float64), 0, - maxorder={0: 0, 1: 2, 2: 4, 3: 4, 4: 4}[i]) - for i in range(5)] + d = [ + get_weights( + np.array(xl[i], dtype=np.float64), + 0, + maxorder={0: 0, 1: 2, 2: 4, 3: 4, 4: 4}[i], + ) + for i in range(5) + ] def S(x): - return 1.0*x + return 1.0 * x def test_d(d, i, o, r): print(d[i]) @@ -79,58 +83,163 @@ def test_d(d, i, o, r): test_d(d, 0, 0, [1]) # First derivative - test_d(d, 1, 1, [-S(1)/2, S(0), S(1)/2]) - test_d(d, 2, 1, [S(1)/12, -S(2)/3, S(0), S(2)/3, -S(1)/12]) - test_d(d, 3, 1, [-S(1)/60, S(3)/20, -S(3)/4, S(0), S(3)/4, -S(3)/20, - S(1)/60]) - test_d(d, 4, 1, [S(1)/280, -S(4)/105, S(1)/5, -S(4)/5, S(0), S(4)/5, - -S(1)/5, S(4)/105, -S(1)/280]) + test_d(d, 1, 1, [-S(1) / 2, S(0), S(1) / 2]) + test_d(d, 2, 1, [S(1) / 12, -S(2) / 3, S(0), S(2) / 3, -S(1) / 12]) + test_d( + d, + 3, + 1, + [-S(1) / 60, S(3) / 20, -S(3) / 4, S(0), S(3) / 4, -S(3) / 20, S(1) / 60], + ) + test_d( + d, + 4, + 1, + [ + S(1) / 280, + -S(4) / 105, + S(1) / 5, + -S(4) / 5, + S(0), + S(4) / 5, + -S(1) / 5, + S(4) / 105, + -S(1) / 280, + ], + ) # Second derivative test_d(d, 1, 2, [S(1), -S(2), S(1)]) - test_d(d, 2, 2, [-S(1)/12, S(4)/3, -S(5)/2, S(4)/3, -S(1)/12]) - test_d(d, 3, 2, [S(1)/90, -S(3)/20, S(3)/2, -S(49)/18, S(3)/2, - -S(3)/20, S(1)/90]) - test_d(d, 4, 2, [-S(1)/560, S(8)/315, -S(1)/5, S(8)/5, -S(205)/72, - S(8)/5, -S(1)/5, S(8)/315, -S(1)/560]) + test_d(d, 2, 2, [-S(1) / 12, S(4) / 3, -S(5) / 2, S(4) / 3, -S(1) / 12]) + test_d( + d, + 3, + 2, + [S(1) / 90, -S(3) / 20, S(3) / 2, -S(49) / 18, S(3) / 2, -S(3) / 20, S(1) / 90], + ) + test_d( + d, + 4, + 2, + [ + -S(1) / 560, + S(8) / 315, + -S(1) / 5, + S(8) / 5, + -S(205) / 72, + S(8) / 5, + -S(1) / 5, + S(8) / 315, + -S(1) / 560, + ], + ) # Third derivative - test_d(d, 2, 3, [-S(1)/2, S(1), S(0), -S(1), S(1)/2]) - test_d(d, 3, 3, [S(1)/8, -S(1), S(13)/8, S(0), -S(13)/8, S(1), -S(1)/8]) - test_d(d, 4, 3, [-S(7)/240, S(3)/10, -S(169)/120, S(61)/30, S(0), - -S(61)/30, S(169)/120, -S(3)/10, S(7)/240]) + test_d(d, 2, 3, [-S(1) / 2, S(1), S(0), -S(1), S(1) / 2]) + test_d(d, 3, 3, [S(1) / 8, -S(1), S(13) / 8, S(0), -S(13) / 8, S(1), -S(1) / 8]) + test_d( + d, + 4, + 3, + [ + -S(7) / 240, + S(3) / 10, + -S(169) / 120, + S(61) / 30, + S(0), + -S(61) / 30, + S(169) / 120, + -S(3) / 10, + S(7) / 240, + ], + ) # Fourth derivative test_d(d, 2, 4, [S(1), -S(4), S(6), -S(4), S(1)]) - test_d(d, 3, 4, [-S(1)/6, S(2), -S(13)/2, S(28)/3, -S(13)/2, S(2), - -S(1)/6]) - test_d(d, 4, 4, [S(7)/240, -S(2)/5, S(169)/60, -S(122)/15, S(91)/8, - -S(122)/15, S(169)/60, -S(2)/5, S(7)/240]) + test_d( + d, 3, 4, [-S(1) / 6, S(2), -S(13) / 2, S(28) / 3, -S(13) / 2, S(2), -S(1) / 6] + ) + test_d( + d, + 4, + 4, + [ + S(7) / 240, + -S(2) / 5, + S(169) / 60, + -S(122) / 15, + S(91) / 8, + -S(122) / 15, + S(169) / 60, + -S(2) / 5, + S(7) / 240, + ], + ) # Table 2, p. 703 in doi:10.1090/S0025-5718-1988-0935077-0 # -------------------------------------------------------- - xl = [[j/S(2) for j in list(range(-i*2+1, 0, 2))+list(range(1, i*2+1, 2))] - for i in range(1, 5)] + xl = [ + [j / S(2) for j in list(range(-i * 2 + 1, 0, 2)) + list(range(1, i * 2 + 1, 2))] + for i in range(1, 5) + ] # e holds all coefficients - d = [get_weights(np.array(xl[i], dtype=np.float64), 0, - maxorder={0: 1, 1: 2, 2: 4, 3: 4}[i]) for i in range(4)] + d = [ + get_weights( + np.array(xl[i], dtype=np.float64), 0, maxorder={0: 1, 1: 2, 2: 4, 3: 4}[i] + ) + for i in range(4) + ] # Zeroth derivative - test_d(d, 0, 0, [S(1)/2, S(1)/2]) - test_d(d, 1, 0, [-S(1)/16, S(9)/16, S(9)/16, -S(1)/16]) - test_d(d, 2, 0, [S(3)/256, -S(25)/256, S(75)/128, S(75)/128, - -S(25)/256, S(3)/256]) - test_d(d, 3, 0, [-S(5)/2048, S(49)/2048, -S(245)/2048, S(1225)/2048, - S(1225)/2048, -S(245)/2048, S(49)/2048, -S(5)/2048]) + test_d(d, 0, 0, [S(1) / 2, S(1) / 2]) + test_d(d, 1, 0, [-S(1) / 16, S(9) / 16, S(9) / 16, -S(1) / 16]) + test_d( + d, + 2, + 0, + [S(3) / 256, -S(25) / 256, S(75) / 128, S(75) / 128, -S(25) / 256, S(3) / 256], + ) + test_d( + d, + 3, + 0, + [ + -S(5) / 2048, + S(49) / 2048, + -S(245) / 2048, + S(1225) / 2048, + S(1225) / 2048, + -S(245) / 2048, + S(49) / 2048, + -S(5) / 2048, + ], + ) # First derivative test_d(d, 0, 1, [-S(1), S(1)]) - test_d(d, 1, 1, [S(1)/24, -S(9)/8, S(9)/8, -S(1)/24]) - test_d(d, 2, 1, [-S(3)/640, S(25)/384, -S(75)/64, S(75)/64, -S(25)/384, - S(3)/640]) - test_d(d, 3, 1, [S(5)/7168, -S(49)/5120, S(245)/3072, S(-1225)/1024, - S(1225)/1024, -S(245)/3072, S(49)/5120, -S(5)/7168]) + test_d(d, 1, 1, [S(1) / 24, -S(9) / 8, S(9) / 8, -S(1) / 24]) + test_d( + d, + 2, + 1, + [-S(3) / 640, S(25) / 384, -S(75) / 64, S(75) / 64, -S(25) / 384, S(3) / 640], + ) + test_d( + d, + 3, + 1, + [ + S(5) / 7168, + -S(49) / 5120, + S(245) / 3072, + S(-1225) / 1024, + S(1225) / 1024, + -S(245) / 3072, + S(49) / 5120, + -S(5) / 7168, + ], + ) # Reasonably the rest of the table is also correct... (testing of that # deemed excessive at the moment) @@ -148,17 +257,19 @@ def test_interpolate_by_finite_diff__multiple_ydata__data(): xarr = np.linspace(-1.5, 1.7, 53) ny = 4 xtest = np.linspace(-1.4, 1.6, 57) - yarr = [i*np.exp(xarr) for i in range(1, ny+1)] - yexact = np.array([i*np.exp(xtest) for i in range(1, ny+1)]).T + yarr = [i * np.exp(xarr) for i in range(1, ny + 1)] + yexact = np.array([i * np.exp(xtest) for i in range(1, ny + 1)]).T maxorder = 4 - y = interpolate_by_finite_diff(xarr, yarr, xtest, maxorder=maxorder, ntail=5, nhead=5) - assert y.shape == (xtest.size, ny, maxorder+1) + y = interpolate_by_finite_diff( + xarr, yarr, xtest, maxorder=maxorder, ntail=5, nhead=5 + ) + assert y.shape == (xtest.size, ny, maxorder + 1) for ci in range(y.shape[2]): - tol = 10**-(13-ci*2) + tol = 10 ** -(13 - ci * 2) assert np.allclose(yexact, y[..., ci], rtol=tol, atol=tol) -if __name__ == '__main__': +if __name__ == "__main__": test_interpolate_by_finite_diff() test_derivatives_at_point_by_finite_diff() test_get_weights() diff --git a/finitediff/util.py b/finitediff/util.py index 1d8340a..e321097 100644 --- a/finitediff/util.py +++ b/finitediff/util.py @@ -1,25 +1,25 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import numpy as np from . import interpolate_by_finite_diff -def interpolate_ahead(x, y, n, direction='fw'): +def interpolate_ahead(x, y, n, direction="fw"): if not np.all(np.diff(x) > 0): raise ValueError("x not strictly monotonic.") - if direction == 'both': - y1, slc1 = interpolate_ahead(x, y, n, 'fw') - y2, slc2 = interpolate_ahead(x, y, n, 'bw') + if direction == "both": + y1, slc1 = interpolate_ahead(x, y, n, "fw") + y2, slc2 = interpolate_ahead(x, y, n, "bw") y = np.zeros_like(x) y[slc1] = y1 y[slc2] += y2 - y[slc1.start:slc2.stop] /= 2 + y[slc1.start : slc2.stop] /= 2 return y, slice(None, None) - elif direction == 'fw': + elif direction == "fw": forward = True - elif direction == 'bw': + elif direction == "bw": forward = False else: raise ValueError("Unknown direction: %s" % direction) @@ -27,15 +27,19 @@ def interpolate_ahead(x, y, n, direction='fw'): rev = slice(None, None, 1) if forward else slice(None, None, -1) values = [] for idx, (xv, yv) in enumerate(zip(x[rev][n:], y[rev][n:])): - _x = np.ascontiguousarray(x[rev][idx:idx+n], dtype=np.float64) - _y = np.ascontiguousarray(y[rev][idx:idx+n], dtype=np.float64) + _x = np.ascontiguousarray(x[rev][idx : idx + n], dtype=np.float64) + _y = np.ascontiguousarray(y[rev][idx : idx + n], dtype=np.float64) _v = np.array([xv]) - values.append(interpolate_by_finite_diff(_x, _y, _v, maxorder=0, ntail=n, nhead=0)) - return np.array(values[rev]).squeeze(), slice(n, None) if forward else slice(None, -n) + values.append( + interpolate_by_finite_diff(_x, _y, _v, maxorder=0, ntail=n, nhead=0) + ) + return np.array(values[rev]).squeeze(), slice(n, None) if forward else slice( + None, -n + ) def avg_stddev(arr, w): - """ Calculates the average and standard deviation. + """Calculates the average and standard deviation. Parameters ---------- @@ -51,5 +55,5 @@ def avg_stddev(arr, w): """ avg, wsum = np.average(arr, weights=w, returned=True) res = arr - avg - stddev = np.sqrt(np.sum(np.dot(w, np.square(res))/(res.size - 1)/wsum)) + stddev = np.sqrt(np.sum(np.dot(w, np.square(res)) / (res.size - 1) / wsum)) return avg, stddev diff --git a/scripts/coverage_badge.py b/scripts/coverage_badge.py index 955411a..7a88732 100755 --- a/scripts/coverage_badge.py +++ b/scripts/coverage_badge.py @@ -11,7 +11,7 @@ """ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function import os # this template was generated from shields.io on 2015-10-11 @@ -41,22 +41,23 @@ def get_coverage(htmldir): - for line in open(os.path.join(htmldir, 'index.html'), 'rt'): - if 'pc_cov' in line: - return int(line.split('pc_cov')[1].split( - '>')[1].split('<')[0].rstrip('%')) + for line in open(os.path.join(htmldir, "index.html"), "rt"): + if "pc_cov" in line: + return int(line.split("pc_cov")[1].split(">")[1].split("<")[0].rstrip("%")) raise ValueError("Could not find pc_cov in index.html") def write_cov_badge_svg(path, percent): - colors = '#e05d44 #fe7d37 #dfb317 #a4a61d #97CA00 #4c1'.split() + colors = "#e05d44 #fe7d37 #dfb317 #a4a61d #97CA00 #4c1".split() limits_le = 50, 60, 70, 80, 90, 100 c = next(clr for lim, clr in zip(limits_le, colors) if percent <= lim) - with open(path, 'wt') as f: + with open(path, "wt") as f: f.write(template.format(c, str(percent))) -if __name__ == '__main__': + +if __name__ == "__main__": import sys + assert len(sys.argv) == 3 cov_percent = get_coverage(sys.argv[1]) write_cov_badge_svg(sys.argv[2], cov_percent) diff --git a/scripts/release.sh b/scripts/release.sh index 7b1d069..a7cccb6 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -19,7 +19,7 @@ cd $(dirname $0)/.. PKG=$(find . -maxdepth 2 -name __init__.py -print0 | xargs -0 -n1 dirname | xargs basename) PKG_UPPER=$(echo $PKG | tr '[:lower:]' '[:upper:]') ./scripts/run_tests.sh -env ${PKG_UPPER}_RELEASE_VERSION=v$VERSION python setup.py sdist +env ${PKG_UPPER}_RELEASE_VERSION=v$VERSION ${PYTHON:-python3} setup.py sdist if [[ -e ./scripts/generate_docs.sh ]]; then env ${PKG_UPPER}_RELEASE_VERSION=v$VERSION ./scripts/generate_docs.sh fi diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 608f1aa..e8aa6b9 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -4,5 +4,5 @@ # or # $ ./scripts/run_tests.sh --cov pycvodes --cov-report html ${PYTHON:-python3} setup.py build_ext -i -${PYTHON:-python3} -m pytest --doctest-modules --flake8 --flakes $@ +${PYTHON:-python3} -m pytest --doctest-modules --black --flake8 $@ ${PYTHON:-python3} -m doctest README.rst diff --git a/setup.cfg b/setup.cfg index 2bb5bbe..8ac992a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,12 +21,13 @@ flake8-ignore = # E222: Multiple spaces after operator. # E226: Missing space around arithmetic operator. # E251: unexpected spaces around keyword/parameter equals - # F401: Multiple imports on one line. + # F401: module imported but not used # F403: Module import not at top of file. # W503: Break before binary operator; warn on breaking after. # W504: Break after binary operator; warn on breaking before. * E203 W503 W504 __init__.py F401 F403 + grid/__init__.py F401 F403 doc/conf.py ALL # conf.py is a generated file diff --git a/setup.py b/setup.py index 74380e8..1c85e63 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ import warnings from setuptools import setup + try: import cython except ImportError: @@ -19,117 +20,145 @@ _HAVE_CYTHON = True assert cython # silence pep8 -pkg_name = 'finitediff' -url = 'https://github.com/bjodah/' + pkg_name -license = 'BSD' +pkg_name = "finitediff" +url = "https://github.com/bjodah/" + pkg_name +license = "BSD" def _path_under_setup(*args): return os.path.join(*args) -release_py_path = _path_under_setup(pkg_name, '_release.py') -config_py_path = _path_under_setup(pkg_name, '_config.py') +release_py_path = _path_under_setup(pkg_name, "_release.py") +config_py_path = _path_under_setup(pkg_name, "_config.py") env = None # silence pyflakes, 'env' is actually set on the next line with open(config_py_path) as ifh: exec(ifh.read()) for k, v in list(env.items()): - env[k] = os.environ.get('%s_%s' % (pkg_name.upper(), k), v) + env[k] = os.environ.get("%s_%s" % (pkg_name.upper(), k), v) -_version_env_var = '%s_RELEASE_VERSION' % pkg_name.upper() -RELEASE_VERSION = os.environ.get(_version_env_var, '') +_version_env_var = "%s_RELEASE_VERSION" % pkg_name.upper() +RELEASE_VERSION = os.environ.get(_version_env_var, "") -if len(RELEASE_VERSION) > 1 and RELEASE_VERSION[0] == 'v': +if len(RELEASE_VERSION) > 1 and RELEASE_VERSION[0] == "v": TAGGED_RELEASE = True __version__ = RELEASE_VERSION[1:] else: TAGGED_RELEASE = False # read __version__ attribute from _release.py: - with io.open(release_py_path, encoding='utf-8') as ifh: + with io.open(release_py_path, encoding="utf-8") as ifh: exec(ifh.read()) - if __version__.endswith('git'): + if __version__.endswith("git"): try: - _git_version = subprocess.check_output( - ['git', 'describe', '--dirty']).rstrip().decode('utf-8').replace('-dirty', '.dirty') + _git_version = ( + subprocess.check_output(["git", "describe", "--dirty"]) + .rstrip() + .decode("utf-8") + .replace("-dirty", ".dirty") + ) except subprocess.CalledProcessError: - warnings.warn("A git-archive is being installed - version information incomplete.") + warnings.warn( + "A git-archive is being installed - version information incomplete." + ) else: - if 'develop' not in sys.argv: + if "develop" not in sys.argv: warnings.warn("Using git to derive version: dev-branches may compete.") - __version__ = re.sub('v([0-9.]+)-(\d+)-(\w+)', r'\1.post\2+\3', _git_version) # .dev < '' < .post + __version__ = re.sub( + r"v([0-9.]+)-(\d+)-(\w+)", r"\1.post\2+\3", _git_version + ) # .dev < '' < .post -basename = '_finitediff_c' -_src = {ext: _path_under_setup(pkg_name, "%s.%s" % (basename, ext)) for ext in "c pyx".split()} +basename = "_finitediff_c" +_src = { + ext: _path_under_setup(pkg_name, "%s.%s" % (basename, ext)) + for ext in "c pyx".split() +} if _HAVE_CYTHON and os.path.exists(_src["pyx"]): # Possible that a new release of Python needs a re-rendered Cython source, # or that we want to include possible bug-fix to Cython, disable by manually # deleting .pyx file from source distribution. USE_CYTHON = True - if os.path.exists(_src['c']): - os.unlink(_src['c']) # ensure c++ source is re-generated. + if os.path.exists(_src["c"]): + os.unlink(_src["c"]) # ensure c++ source is re-generated. else: USE_CYTHON = False other_sources = [ - os.path.join('src', 'finitediff_c.c'), - os.path.join('finitediff', 'external', 'newton_interval', 'src', 'newton_interval.c') + os.path.join("src", "finitediff_c.c"), + os.path.join( + "finitediff", "external", "newton_interval", "src", "newton_interval.c" + ), ] cmdclass = {} ext_modules = [] -if len(sys.argv) > 1 and '--help' not in sys.argv[1:] and not any(arg in ( - '--help-commands', 'egg_info', 'clean', '--version') for arg in sys.argv[1:]): +if ( + len(sys.argv) > 1 + and "--help" not in sys.argv[1:] + and not any( + arg in ("--help-commands", "egg_info", "clean", "--version") + for arg in sys.argv[1:] + ) +): # e.g. egg_info must not import from dependencies (pycompilation) import numpy + include_dirs = [ - os.path.join('finitediff', 'external', 'newton_interval', 'include'), - os.path.join('finitediff', 'include'), - numpy.get_include() + os.path.join("finitediff", "external", "newton_interval", "include"), + os.path.join("finitediff", "include"), + numpy.get_include(), ] from setuptools.extension import Extension + ext_modules = [ - Extension("%s.%s" % (pkg_name, basename), - [_src["pyx" if USE_CYTHON else "c"]], include_dirs=include_dirs) + Extension( + "%s.%s" % (pkg_name, basename), + [_src["pyx" if USE_CYTHON else "c"]], + include_dirs=include_dirs, + ) ] if USE_CYTHON: from Cython.Build import cythonize - ext_modules = cythonize(ext_modules, include_path=include_dirs, - compiler_directives={'embedsignature': True, 'binding': True}) + + ext_modules = cythonize( + ext_modules, + include_path=include_dirs, + compiler_directives={"embedsignature": True, "binding": True}, + ) else: ext_modules[0].sources += other_sources - if ext_modules[0].sources[0].startswith('/'): + if ext_modules[0].sources[0].startswith("/"): raise ValueError("Absolute path not allowed: %s" % ext_modules[0].sources[0]) submodules = [ - pkg_name + '.grid', + pkg_name + ".grid", ] tests = [ - pkg_name + '.tests', - pkg_name + '.grid.tests', + pkg_name + ".tests", + pkg_name + ".grid.tests", ] classifiers = [ "Development Status :: 4 - Beta", - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: Mathematics', + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Mathematics", ] -with io.open(_path_under_setup(pkg_name, '__init__.py'), 'rt', encoding='utf-8') as f: - short_description = f.read().split('"""')[1].split('\n')[1] +with io.open(_path_under_setup(pkg_name, "__init__.py"), "rt", encoding="utf-8") as f: + short_description = f.read().split('"""')[1].split("\n")[1] if not 10 < len(short_description) < 255: warnings.warn("Short description from __init__.py proably not read correctly.") -with io.open(_path_under_setup('README.rst'), encoding='utf-8') as ifh: +with io.open(_path_under_setup("README.rst"), encoding="utf-8") as ifh: long_description = ifh.read() if not len(long_description) > 100: warnings.warn("Long description from README.rst probably not read correctly.") -with io.open(_path_under_setup('AUTHORS'), 'rt', encoding='utf-8') as ifh: - _author, _author_email = ifh.readline().split('<') +with io.open(_path_under_setup("AUTHORS"), "rt", encoding="utf-8") as ifh: + _author, _author_email = ifh.readline().split("<") setup_kwargs = dict( @@ -138,7 +167,7 @@ def _path_under_setup(*args): description=short_description, long_description=long_description, author=_author.strip(), - author_email=_author_email.split('>')[0].strip(), + author_email=_author_email.split(">")[0].strip(), url=url, license=license, keywords=["finite-difference", "taylor series", "extrapolation", "interpolation"], @@ -147,25 +176,27 @@ def _path_under_setup(*args): cmdclass=cmdclass, ext_modules=ext_modules, classifiers=classifiers, - setup_requires=['numpy'] + (['cython'] if USE_CYTHON else []), - install_requires=['numpy'], - extras_require={'all': ['scipy', 'pytest', 'sphinx', 'sphinx_rtd_theme', 'numpydoc']} + setup_requires=["numpy"] + (["cython"] if USE_CYTHON else []), + install_requires=["numpy"], + extras_require={ + "all": ["scipy", "pytest", "sphinx", "sphinx_rtd_theme", "numpydoc"] + }, ) -if __name__ == '__main__': +if __name__ == "__main__": try: if TAGGED_RELEASE: # Same commit should generate different sdist # depending on tagged version (set FINITEDIFF_RELEASE_VERSION) # this will ensure source distributions contain the correct version - shutil.move(release_py_path, release_py_path+'__temp__') - with open(release_py_path, 'wt') as ofh: + shutil.move(release_py_path, release_py_path + "__temp__") + with open(release_py_path, "wt") as ofh: ofh.write("__version__ = '{}'\n".format(__version__)) - shutil.move(config_py_path, config_py_path+'__temp__') - with open(config_py_path, 'wt') as fh: + shutil.move(config_py_path, config_py_path + "__temp__") + with open(config_py_path, "wt") as fh: fh.write("env = {}\n".format(pprint.pformat(env))) setup(**setup_kwargs) finally: if TAGGED_RELEASE: - shutil.move(release_py_path+'__temp__', release_py_path) - shutil.move(config_py_path+'__temp__', config_py_path) + shutil.move(release_py_path + "__temp__", release_py_path) + shutil.move(config_py_path + "__temp__", config_py_path)