io-shaker
io-shaker provides Python bindings for the C++ Intelligent Optimization heuristics.
The package exposes two local search heuristics for continuous function optimization:
-
the inertial shaker [1, 3]: a "search box" is maintained around the current best point and its sizes along the coordinates are expanded and contracted according to the outcomes of explorative evaluations along the coordinated axes;
-
the affine shaker [1, 2, 3], which maintains a more flexible search region whose size and shape are again determined by explorative evaluations.
References
[1] R. Battiti and G. Tecchiolli. Learning with first, second, and no derivatives: a case study in high energy physics. Neurocomputing, 6:181–206, 1994.
[2] M. Brunato and R. Battiti. RASH: A self-adaptive random search method. In Carlos Cotta, Marc Sevaux, and Kenneth Sörensen, editors, Adaptive and Multilevel Meta- heuristics, volume 136 of Studies in Computational Intelligence. Springer, 2008. ISBN 978-3-540-79437-0.
[3] Roberto Battiti, Kevin Tierney, Mauro Brunato. Intelligent Optimization - Optimization meets Machine Learning. LION association, Italy, 2026. Downloadable at https://intelligent-optimization.org/iobook/
Installing
From PyPi
Install the io-shaker package:
python -m pip install io-shaker
The package contains binding to a C++ library; therefore, a C++ compiler and Python development headers may be required if a suitable pre-compiled platform wheel is not found.
In such case, please follow your platform's instructions to install the compiler and Python headers, then repeat the above command.
Note: while the official package name is io-shaker(with a dash), the import command requires an underscore:
from io_shaker import reactive_affine_shaker, inertial_shaker
From the source tree
A C++ compiler and Python development headers are required when installing from
the source tree. SWIG is not required because its
generated files, io_shaker.py and io_shaker_wrap.cxx, are included in the package.
python -m pip install .
To build a wheel for the current Python and platform:
python -m pip wheel .
Setuptools selects the platform compiler and gives the native _io_shaker
extension the correct filename for Linux, macOS, or Windows.
If needed, the SWIG-generated files io_shaker.py and io_shaker_wrap.cxx can be recreated by
swig -python -c++ io_shaker.i
Native library
The following cross-platform commands compile a standalone C++ library
(cmake build system needed):
cmake -S . -B cbuild
cmake --build cbuild --config Release
Usage and example
Python library
See test/test.py:
from io_shaker import reactive_affine_shaker, inertial_shaker
# Function to be optimized
def f(x: list[float]) -> float:
return (x[0]-.5)**2 + (x[1]-.45)**2
# OPTIONAL: called whenever current best is updated
def print_update(evals: int, value: float, x: list[float]) -> None:
print('\tNew best @', evals, x, value)
# Test the solvers 10 times with different seeds
for seed in range(10):
best_x, best_f = reactive_affine_shaker(f, 2, seed=seed, update_callback=print_update)
print('BEST AFFINE', best_x, best_f)
best_x, best_f = inertial_shaker(f, 2, seed=seed, update_callback=print_update)
print('BEST INERTIAL', best_x, best_f)
The two functions reactive_affine_shaker and inertial_shaker accept the following parameters:
function(mandatory, typeCallable[[list[float]],float]): a Python function accepting a float array and returning a float value.dimension(mandatory, typeint): the function's domain dimension.seed(typeint, default 0): randome number generator seed, for reproducibility.lb(typelist[float], default[0.0]*dimension): domain lower bounds.ub(typelist[float], default[1.0]*dimension): domain upper bounds.fraction(typefloat, default .1): initial size of the search box as a fraction of the [lb,ub] interval along each dimension.reducefactor(typefloat, default .9): search box reduction factor upon failed improvement.expandfactor(typefloat, default 1.0/reducefactor): search box expansion factor upon succeeded improvement.update_callback(typeCallable[[int,float,list[float]],None], defaultNone): if passed, function to be invoked at every best value update; receives three arguments: number of function evaluations, best value, and optimizer coordinates.
Both functions return a two-element tuple containing the minimizer coordinates and the corresponding value (Tuple[List[float],float]).
Native library
See test/test.cpp:
#include <iostream>
#include "function.h"
#include "c_io_shaker.h"
using namespace std;
class MyFunction: public Function {
protected:
// variable holding the function's latest evaluation
double result_value;
// evaluation function
virtual const double *evaluate (const double *x);
public:
// Constructor
MyFunction();
};
MyFunction::MyFunction():
// base constructor: domain and codomain dimensions, random rotations (useful for tests)
Function(2, 1, false)
{
// Basic initialization: lower and upper bound for each coordinate
xmin[0] = xmin[1] = 0.0;
xmax[0] = xmax[1] = 1.0;
}
const double *MyFunction::evaluate(const double *x) {
double
x0 = x[0]-.5,
x1 = x[1]-.45;
result_value = x0*x0 + x1*x1;
// The function returns a pointer to the value
return &result_value;
}
int main() {
MyFunction f;
double best_x[2];
// Test the solvers ten times with different seeds
for (int seed = 0; seed < 10; seed++ ) {
double best_f = reactive_affine_shaker(f, best_x, seed);
cout << "BEST AFFINE " << best_f << " @ " << best_x[0] << ',' << best_x[1] << endl;
best_f = inertial_shaker(f, best_x, seed);
cout << "BEST INERTIAL " << best_f << " @ " << best_x[0] << ',' << best_x[1] << endl;
}
return 0;
}
To compile from inside test, add .. and ../libRSO as include directories and link the object file with ../cbuild/libIOShaker.a:
cd test
g++ -c -I.. -I../libRSO test.cpp
g++ -o test -L../cbuild test.o -lIOShaker
./test
The two functions reactive_affine_shaker and inertial_shaker accept a parameter list similar to their Python counterparts:
function(classFunction): a reference to an object from a subclass ofFunction.best_x(typedouble[function.dimension]): a double array apt at containing the minimizer's coordinates.seed(typeint, default 0): randome number generator seed, for reproducibility.fraction(typedouble, default .1): initial size of the search box as a fraction of the [lb,ub] interval along each dimension.reducefactor(typedouble, default .9): search box reduction factor upon failed improvement.expandfactor(typedouble, default 1.0/reducefactor): search box expansion factor upon succeeded improvement.
The differences from the Python functions are the following:
functionis a reference to an object from a subclass ofFunctioninstead of a generic callable object.best_xmust be provided as argument, while in the Python version it is created and returned by the solver.- parameters
dimension,lbandubare omitted because the domain is specified in thedimension,xminandxmaxmembers ofFunction.
As shown in the example above, the Function subclass must define at least two methods:
- a constructor invoking the one from the base class, setting the domain's and codomain's dimension (the latter must be 1), and filling the lower and upper bound arrays;
- a protected
evaluatefunction that accepts a constant double array and provides a pointer to a double variable containing the result.
Metadata
Release files for io-shaker 0.9.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| io_shaker-0.9.2.tar.gz | 60.7 kB | Details |
Release files / io_shaker-0.9.2.tar.gz
| Download URL | io_shaker-0.9.2.tar.gz |
|---|---|
| Size | 60.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
210408c4121a4738fef8e766ed95f8f2c68c3172446400bb5c2d217ed8b573c5
|
|
BLAKE2b-256 checksum How to use checksums |
7def0c0abacc657a9ed55026c993982a1753c68a4204f851c4f3e18d42c5cb47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|