Spaces:
Build error
Build error
| #!/usr/bin/env python3 | |
| from enum import Enum | |
| from typing import Callable, List, Tuple | |
| import torch | |
| class Riemann(Enum): | |
| left = 1 | |
| right = 2 | |
| middle = 3 | |
| trapezoid = 4 | |
| SUPPORTED_RIEMANN_METHODS = [ | |
| "riemann_left", | |
| "riemann_right", | |
| "riemann_middle", | |
| "riemann_trapezoid", | |
| ] | |
| SUPPORTED_METHODS = SUPPORTED_RIEMANN_METHODS + ["gausslegendre"] | |
| def approximation_parameters( | |
| method: str, | |
| ) -> Tuple[Callable[[int], List[float]], Callable[[int], List[float]]]: | |
| r"""Retrieves parameters for the input approximation `method` | |
| Args: | |
| method: The name of the approximation method. Currently only `riemann` | |
| and gauss legendre are | |
| """ | |
| if method in SUPPORTED_RIEMANN_METHODS: | |
| return riemann_builders(method=Riemann[method.split("_")[-1]]) | |
| if method == "gausslegendre": | |
| return gauss_legendre_builders() | |
| raise ValueError("Invalid integral approximation method name: {}".format(method)) | |
| def riemann_builders( | |
| method: Riemann = Riemann.trapezoid, | |
| ) -> Tuple[Callable[[int], List[float]], Callable[[int], List[float]]]: | |
| r"""Step sizes are identical and alphas are scaled in [0, 1] | |
| Args: | |
| n: The number of integration steps | |
| method: `left`, `right`, `middle` and `trapezoid` riemann | |
| Returns: | |
| 2-element tuple of **step_sizes**, **alphas**: | |
| - **step_sizes** (*callable*): | |
| `step_sizes` takes the number of steps as an | |
| input argument and returns an array of steps sizes which | |
| sum is smaller than or equal to one. | |
| - **alphas** (*callable*): | |
| `alphas` takes the number of steps as an input argument | |
| and returns the multipliers/coefficients for the inputs | |
| of integrand in the range of [0, 1] | |
| """ | |
| def step_sizes(n: int) -> List[float]: | |
| assert n > 1, "The number of steps has to be larger than one" | |
| deltas = [1 / n] * n | |
| if method == Riemann.trapezoid: | |
| deltas[0] /= 2 | |
| deltas[-1] /= 2 | |
| return deltas | |
| def alphas(n: int) -> List[float]: | |
| assert n > 1, "The number of steps has to be larger than one" | |
| if method == Riemann.trapezoid: | |
| return torch.linspace(0, 1, n).tolist() | |
| elif method == Riemann.left: | |
| return torch.linspace(0, 1 - 1 / n, n).tolist() | |
| elif method == Riemann.middle: | |
| return torch.linspace(1 / (2 * n), 1 - 1 / (2 * n), n).tolist() | |
| elif method == Riemann.right: | |
| return torch.linspace(1 / n, 1, n).tolist() | |
| else: | |
| raise AssertionError("Provided Reimann approximation method is not valid.") | |
| # This is not a standard riemann method but in many cases it | |
| # leades to faster approaximation. Test cases for small number of steps | |
| # do not make sense but for larger number of steps the approximation is | |
| # better therefore leaving this option available | |
| # if method == 'riemann_include_endpoints': | |
| # return [i / (n - 1) for i in range(n)] | |
| return step_sizes, alphas | |
| def gauss_legendre_builders() -> Tuple[ | |
| Callable[[int], List[float]], Callable[[int], List[float]] | |
| ]: | |
| r"""Numpy's `np.polynomial.legendre` function helps to compute step sizes | |
| and alpha coefficients using gauss-legendre quadrature rule. | |
| Since numpy returns the integration parameters in different scales we need to | |
| rescale them to adjust to the desired scale. | |
| Gauss Legendre quadrature rule for approximating the integrals was originally | |
| proposed by [Xue Feng and her intern Hauroun Habeeb] | |
| (https://research.fb.com/people/feng-xue/). | |
| Args: | |
| n (int): The number of integration steps | |
| Returns: | |
| 2-element tuple of **step_sizes**, **alphas**: | |
| - **step_sizes** (*callable*): | |
| `step_sizes` takes the number of steps as an | |
| input argument and returns an array of steps sizes which | |
| sum is smaller than or equal to one. | |
| - **alphas** (*callable*): | |
| `alphas` takes the number of steps as an input argument | |
| and returns the multipliers/coefficients for the inputs | |
| of integrand in the range of [0, 1] | |
| """ | |
| # allow using riemann even without np | |
| import numpy as np | |
| def step_sizes(n: int) -> List[float]: | |
| assert n > 0, "The number of steps has to be larger than zero" | |
| # Scaling from 2 to 1 | |
| return list(0.5 * np.polynomial.legendre.leggauss(n)[1]) | |
| def alphas(n: int) -> List[float]: | |
| assert n > 0, "The number of steps has to be larger than zero" | |
| # Scaling from [-1, 1] to [0, 1] | |
| return list(0.5 * (1 + np.polynomial.legendre.leggauss(n)[0])) | |
| return step_sizes, alphas | |