Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add files for genz intergration benchmark #45

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions benchmarks/genz-integration/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
122 changes: 122 additions & 0 deletions benchmarks/genz-integration/README.md
Original file line number Diff line number Diff line change
@@ -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)
35 changes: 35 additions & 0 deletions benchmarks/genz-integration/minimal_server.py
Original file line number Diff line number Diff line change
@@ -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)
33 changes: 33 additions & 0 deletions benchmarks/genz-integration/test_output.py
Original file line number Diff line number Diff line change
@@ -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)