# Introduction

BoFire is a framework to define and solve black-box optimization problems. 
These problems can arise in a number of closely related fields including experimental design, multi-objective optimization and active learning.

BoFire problem specifications are json serializable for use in RESTful APIs and are to a large extent agnostic to the specific methods and frameworks in which the problems are solved.

You can find code-examples in the Getting Started section of this document, as well as full worked-out examples of code-usage in the /tutorials section of this repository!

## Experimental design

In the context of experimental design BoFire allows to define a design space

$$
\mathbb{X} = x_1 \otimes x_2 \ldots \otimes x_D
$$

where the design parameters may take values depending on their type and domain, e.g.

* continuous: $x_1 \in [0, 1]$
* discrete: $x_2 \in \{1, 2, 5, 7.5\}$
* categorical: $x_3 \in \{A, B, C\}$

and a set of equations define additional experimental constraints, e.g.

* linear equality: $\sum x_i = 1$
* linear inequality: $2 x_1 \leq x_2$
* non-linear inequality: $\sum x_i^2 \leq 1$
* n-choose-k: only $k$ out of $n$ parameters can take non-zero values.

## Multi-objective optimization

In the context of multi-objective optimization BoFire allows to define a vector-valued optimization problem

$$
\min_{x \in \mathbb{X}} s(y(x))
$$

where

* $x \in \mathbb{X}$ is again the experimental design space
* $y = \{y_1, \ldots y_M\}$ are known functions describing your experimental outputs and
* $s = \{s_1, \ldots s_M\}$ are the objectives to be minimized, e.g. $s_1$ is the identity function if $y_1$ is to be minimized.

Since the objectives are in general conflicting, there is no point $x$ that simultaneously optimizes all objectives.
Instead the goal is to find the Pareto front of all optimal compromises.

A decision maker can then explore these compromises to get a deep understanding of the problem and make the best informed decision.

## Bayesian optimization

In the context of Bayesian optimization we want to simultaneously learn the unknown function $y(x)$ (exploration), while focusing the experimental effort on promising regions (exploitation).
This is done by using the experimental data to fit a probabilistic model $p(y|x, {data})$ that estimates the distribution of possible outcomes for $y$.
An acquisition function $a$ then formulates the desired trade-off between exploration and exploitation

$$
\min_{x \in \mathbb{X}} a(s(p_y(x)))
$$

and the minimizer $x_\mathrm{opt}$ of this acquisition function determines the next experiment $y(x)$ to run.

When there are multiple competing objectives, the task is again to find a suitable approximation of the Pareto front.

## Design of Experiments

BoFire can be used to generate optimal experimental designs with respect to various optimality criteria like D-optimality, A-optimality or uniform space filling.

For this, the user specifies a design space and a model formula, then chooses an optimality criterion and the desired number of experiments in the design. The resulting optimization problem is then solved by [IPOPT](https://coin-or.github.io/Ipopt/).

The doe subpackage also supports a wide range of constraints on the design space including linear and nonlinear equalities and inequalities as well a (limited) use of NChooseK constraints. The user can provide fixed experiments that will be treated as part of the design but remain fixed during the optimization process. While some of the *optimization* algorithms support non-continuous design variables, the doe subpackage only supports those that are continuous.

By default IPOPT uses the freely available linear solver MUMPS. For large models choosing a different linear solver (e.g. ma57 from Coin-HSL) can vastly reduce optimization time. A free academic license for Coin-HSL can be obtained [here](https://licences.stfc.ac.uk/product/coin-hsl). Instructions on how to install additional linear solvers for IPOPT are given in the [IPOPT documentation](https://coin-or.github.io/Ipopt/INSTALL.html#DOWNLOAD_HSL). For choosing a specific (HSL) linear solver in BoFire you can just pass the name of the solver to `find_local_max_ipopt()` with the `linear_solver` option together with the library's name in the option `hsllib`, e.g.
```
find_local_max_ipopt(domain, "fully-quadratic", ipopt_options={"linear_solver":"ma57", "hsllib":"libcoinhsl.so"})
```
