diff --git a/benchmarks/genz-integration/Dockerfile b/benchmarks/genz-integration/Dockerfile new file mode 100644 index 00000000..341647a5 --- /dev/null +++ b/benchmarks/genz-integration/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:latest + +RUN apt-get update && \ + apt-get install -y python3-pip + +RUN update-ca-certificates && \ + pip3 config set global.trusted-host "pypi.org files.pythonhosted.org pypi.python.org" + +RUN pip3 install umbridge + +RUN pip3 install pyapprox + +COPY minimal_server.py / + +CMD python3 minimal_server.py diff --git a/benchmarks/genz-integration/README.md b/benchmarks/genz-integration/README.md new file mode 100644 index 00000000..acb4347f --- /dev/null +++ b/benchmarks/genz-integration/README.md @@ -0,0 +1,122 @@ +# Genz Integration Functions + +## Overview +This benchmark consists of fix families of analytical functions $F_i:\mathbb{R}^D\to\mathbb{R}$, $i=1,\ldots,6$, with means $\mathbb{E}[F(\theta)]$ that can be computed analytically. The number of inputs $D$ and the anisotropy (relative importance of each variable and interactions) of the functions can be adjusted. + +## Authors +John D. Jakeman + +## Run +docker run -it -p 4243:4243 linusseelinger/benchmark-genz + +## Properties + +Model | Description +---|--- +forward | Forward model + +### forward +Mapping | Dimensions | Description +---|---|--- +input | [D] | 2D coordinates $x \in \mathbb{R}^D$ +output | [1] | Function $F(x)$ evaluated at $x$ + +Feature | Supported +---|--- +Evaluate | True +Gradient | False +ApplyJacobian | False +ApplyHessian | False + +Config | Type | Default | Description +---|---|---|--- +nvars | int | D | Number of inputs +c_factor | double | 1.0 | Normalization parameter +w_factor | double | 0. | shift parameter +coef_type | string | "sqexp" | Coefficient decay type +name | string | "oscillatory" | Name of the test function + +The supported values of the coef_type and name config variables are: + +name=["oscillatory", "product_peak", "corner_peak", "gaussian", "c0continuous", "discontinuous"] +coef_type=["none", "quadratic", "quartic", "exp", "sqexp"] + + +## Mount directories +Mount directory | Purpose +---|--- +None | + +## Source code + +[Model sources here.](https://github.com/sandialabs/pyapprox/blob/master/pyapprox/benchmarks/genz.py) + + +## Description +The six Genz test function are: + +Oscillatory ('oscillatory') + +$$ f(z) = \cos\left(2\pi w_1 + \sum_{d=1}^D c_dz_d\right)$$ + +Product Peak ('product_peak') + +$$ f(z) = \prod_{d=1}^D \left(c_d^{-2}+(z_d-w_d)^2\right)^{-1}$$ + +Corner Peak ('corner_peak') + +$$ f(z)=\left( 1+\sum_{d=1}^D c_dz_d\right)^{-(D+1)}$$ + +Gaussian Peak ('gaussian') + +$$ f(z) = \exp\left( -\sum_{d=1}^D c_d^2(z_d-w_d)^2\right)$$ + +C0 Continuous ('c0continuous') + +$$ f(z) = \exp\left( -\sum_{d=1}^D c_d\lvert z_d-w_d\rvert\right)$$ + +Discontinuous ('discontinuous') + +$$ f(z) = \begin{cases} +0 & z_1>w_1 \;\mathrm{or}\; z_2>w_2\\ +\exp\left(\sum_{d=1}^D c_dz_d\right) & \mathrm{otherwise} +\end{cases}$$ + +Increasing $\lVert c \rVert$ will in general make +the integrands more difficult. + +The $0\le w_d \le 1$ parameters do not affect the difficulty +of the integration problem. We set $w_1=w_2=\ldots=W_D$. + +The coefficient types implement different decay rates for $c_d$. +This allows testing of methods that can identify and exploit anisotropy. +They are as follows: + +No decay (none) + +$$ \hat{c}_d=\frac{d+0.5}{D}$$ + +Quadratic decay (qudratic) + +$$ \hat{c}_d = \frac{1}{(D + 1)^2}$$ + +Quartic decay (quartic) + +$$ \hat{c}_d = \frac{1}{(D + 1)^4}$$ + +Exponential decay (exp) + +$$ \hat{c}_d=\exp\left(\log(c_\mathrm{min})\frac{d+1}{D}\right)$$ + +Squared-exponential decay (sqexp) + +$$ \hat{c}_d=10^{\left(\log_{10}(c_\mathrm{min})\frac{(d+1)^2}{D}\right)}$$ + +Here $c_\mathrm{min}$ is argument that sets the minimum value of $c_D$. + +Once the formula are used the coefficients are normalized such that + +$$ c_d = c_\text{factor}\frac{\hat{c}_d}{\sum_{d=1}^D \hat{c}_d}.$$ + +## References +This benchmark was first proposed [here.](https://doi.org/10.1007/978-94-009-3889-2_33) \ No newline at end of file diff --git a/benchmarks/genz-integration/minimal_server.py b/benchmarks/genz-integration/minimal_server.py new file mode 100755 index 00000000..a1ae7b5f --- /dev/null +++ b/benchmarks/genz-integration/minimal_server.py @@ -0,0 +1,35 @@ +import umbridge +import numpy as np + +from pyapprox.benchmarks.genz import GenzFunction + + +class GenzModel(umbridge.Model): + def __init__(self): + super().__init__("genz") + self._model = GenzFunction() + + def get_input_sizes(self, config): + return [config.get("nvars", 5)] + + def get_output_sizes(self, config): + return [1] + + def __call__(self, parameters, config): + sample = np.asarray(parameters).T + assert sample.shape[0] == config.get("nvars", 5) + self._model.set_coefficients( + sample.shape[0], + config.get("c_factor", 1), + config.get("coef_type", "sqexp"), + config.get("w_factor", 0.5)) + name = config.get("name", "oscillatory") + val = self._model(name, sample)[0, 0] + return [[val]] + + def supports_evaluate(self): + return True + + +models = [GenzModel()] +umbridge.serve_models(models, 4242) diff --git a/benchmarks/genz-integration/test_output.py b/benchmarks/genz-integration/test_output.py new file mode 100644 index 00000000..8fcfec2b --- /dev/null +++ b/benchmarks/genz-integration/test_output.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +import argparse +import itertools + +import umbridge +import numpy as np + + +parser = argparse.ArgumentParser(description='Model output test.') +parser.add_argument( + 'url', metavar='url', type=str, + help='the ULR on which the model is running, for example {0}'.format( + 'http://localhost:4242')) +args = parser.parse_args() +print(f"Connecting to host URL {args.url}") + +model = umbridge.HTTPModel(args.url, "genz") + +assert model.get_input_sizes({"nvars": 4}) == [4] +assert model.get_output_sizes() == [1] + +# check all combinations of config runs +names = ["oscillatory", "product_peak", "corner_peak", "gaussian", + "c0continuous", "discontinuous"] +nvars = np.arange(2, 7).astype(float) +decays = ["none", "quadratic", "quartic", "exp", "sqexp"] +test_scenarios = itertools.product(*[names, nvars, decays]) +for test_scenario in test_scenarios: + np.random.seed(1) + parameters = [np.random.uniform(0, 1, (int(test_scenario[1]))).tolist()] + config = {"name": test_scenario[0], "nvars": test_scenario[1], + "coef_type": test_scenario[2]} + value = model(parameters, config)