.. image:: https://github.com/ymahlau/bonni/blob/main/docs/source/_static/bonni.png?raw=true
   :alt: BONNI Logo
   :align: center

BONNI Documentation
==========================================================================================

BONNI optimizes any black box function **WITH** gradient information.
Especially in optimizations with many degrees of freedom, gradient-information increases optimization speed.
In the image below, the surrogate fits the function almost perfectly with few observations.

.. image:: https://github.com/ymahlau/bonni/blob/main/docs/source/_static/surrogate.png?raw=true
   :alt: Surrogate fitting
   :align: center

Installation
------------

You can install BONNI simply via pip:

.. code-block:: bash

   pip install bonni

We recommend installing also the GPU-acceleration from JAX, which will massively increase speed:

.. code-block:: bash

   pip install jax[cuda]

Usage
-----

BONNI provides a nice optimization wrapper similar to the ``scipy.minimize`` API:

.. code-block:: python

   from bonni import optimize_bonni
   from pathlib import Path
   import numpy as np

   def fn(x: np.ndarray):
       # Input function should return function value and gradient
       value = x[0] ** 2 + x[1]
       grad = np.asarray([2 * x[0], 1])
       return value, grad

   xs, ys, gs = optimize_bonni(
       fn=fn,
       bounds=np.asarray([[-1, 1], [0, 1]], dtype=float),
       # BO requires some samples before iterations start. You can either explicitly provide 
       # previous fn evals via `xs=..., ys=..., gs=... or specify a number of random samples. 
       num_bonni_iterations=5,
       num_random_samples=2,
       direction="minimize",
       save_path=Path.cwd(), # save data as npz here
       seed=42,
   )

Additionally, BONNI includes a convenient wrapper for IPOPT. The standard IPOPT package can be difficult to install/use, so we created a convenient wrapper shown below:

.. code-block:: python

   from bonni import optimize_ipopt
   
   xs, ys, gs = optimize_ipopt(
       fn=fn,
       x0=np.asarray([0.5, 0.5]),  # startpoint of optimization
       bounds=np.asarray([[-1, 1], [0, 1]], dtype=float),
       # IPOPT performs line search each iteration, such that the number 
       # of iterations and fn_eval may not be the same
       max_fn_eval=5,
       max_iterations=3,
       direction="maximize",
       save_path=Path.cwd(),
   )

For the full documentation, check out the API.

Citation
--------

If you find this repository helpful for your research, please consider citing:

TODO insert citation as soon as paper online.

.. toctree::
   :maxdepth: 2
   :caption: Contents:
   :hidden:

   self
   api