LocalSolver logo
is now
Hexaly logo

We're excited to share that we are moving forward. We're leaving behind the LocalSolver brand and transitioning to our new identity: Hexaly. This represents a leap forward in our mission to enable every organization to make better decisions faster when faced with operational and strategic challenges.

This page is for an old version of Hexaly Optimizer. We recommend that you update your version and read the documentation for the latest stable release.

Black-Box Optimization

LocalSolver allows you to optimize function through a costly black-box interface. The solver is only able to evaluate the value of the function at a given point. This type of problems appears in practice when you need to simulate a physical process using an external library or simply when the mathematical description of the objective function is too complex to model.

Note

Note that this functionality requires using LocalSolver through its LocalSolverBlackBox APIs, it is not available in the LSP language.

In this section we detail how to optimize a black-box function in each programming language (C++, Java, .NET, Python). To illustrate this description we will minimize the branin function through a black-box interface. The number of evaluations will be limited to 20.

Branin function is defined by f(x) = a(x2 - b*x1² + c*x1 - r)² + s(1-t)cos(x1) + s with a=1, b=5.1/(4π²), c=5/π, s=10 and t=1/(8π). The domains of x1 and x2 are respectively [-5,10] and [0,15].

../_images/branin.png

For more details, see : branin.html

Black-Box optimization in C++

In C++, a black-box function is passed to LocalSolverBlackBox as an object extending the LSBBNativeFunction class. This class has a single virtual method call taking as parameter a LSBBNativeContext object. The``call`` method uses this context to access the decision values of the current point to evaluate and returns the value of the function at this point:

#include <iostream>
#include "api/blackbox/localsolverblackbox.h"

using namespace localsolverblackbox;

#define PI 3.14159

class Branin : public LSBBNativeFunction {
    virtual lsdouble call(const LSBBNativeContext& context){
        lsdouble x = context.getDoubleValue(0);
        lsdouble y = context.getDoubleValue(1);
        return pow(y - (5.1 / (4.0 * PI * PI)) * x * x + 5.0 / PI * x - 6, 2)
            + 10 * (1 - 1 / (8.0 * PI)) * cos(x) + 10;
    }
};

A LocalSolverBlackBox model is then created to optimize this black-box function. The black-box function is transformed into a LSBBExpression object using the createNativeFunction method. The decisions are associated to the function using a LSBBExpression object of type O_Call. The model is then parametrize to use 20 evaluations of the black-box function. The solution value can be obtained after the resolution using a LSBBSolution object:

int main(){
    Branin braninFunction;

    LocalSolverBlackBox ls;
    LSBBModel model = ls.getModel();

    LSBBExpression x = model.floatVar(-5,10);
    LSBBExpression y = model.floatVar(0,15);
    LSBBExpression f = model.createNativeFunction(&braninFunction);
    LSBBExpression call = model.call();
    call.addOperand(f);
    call.addOperand(x);
    call.addOperand(y);
    model.addObjective(call, OD_Minimize);
    model.close();

    ls.getParam().setEvaluationLimit(20);
    ls.solve();

    LSBBSolution sol = ls.getSolution();
    std::cout << "x=" << sol.getDoubleValue(x) << std::endl;
    std::cout << "y=" << sol.getDoubleValue(y) << std::endl;
    std::cout << "obj: " << sol.getDoubleValue(call) << std::endl;
}

Black-Box optimization in Java

In Java, a black-box function is passed to LocalSolverBlackBox as an object implementing the LSBBNativeFunction interface. This interface has a single method call taking as parameter a LSBBNativeContext object. The call method uses this context to access the decision values of the current point to evaluate and returns the value of the function at this point.

A LocalSolverBlackBox model is then created to optimize this black-box function. The black-box function is transformed into a LSBBExpression object using the createNativeFunction method. The decisions are associated to the function using a LSBBExpression object of type Call. The model is then parametrize to use 20 evaluations of the black-box function. The solution value can be obtained after the resolution using a LSBBSolution object:

import localsolverblackbox.*;

public class Branin{
    public static void main(String [] args) {

        LocalSolverBlackBox ls = new LocalSolverBlackBox();
        LSBBModel model = ls.getModel();
        LSBBExpression f = model.createNativeFunction(new LSBBNativeFunction(){
        public double call(LSBBNativeContext context){
            double x = context.getDoubleValue(0);
            double y = context.getDoubleValue(1);
            return Math.pow(y - (5.1 / (4.0 * Math.PI * Math.PI)) * x * x
                + 5.0 / Math.PI * x - 6, 2)
                + 10 * (1 - 1 / (8.0 * Math.PI)) * Math.cos(x) + 10;
        }
        });

        LSBBExpression x = model.floatVar(-5,10);
        LSBBExpression y = model.floatVar(0,15);
        LSBBExpression call = model.call();
        call.addOperand(f);
        call.addOperand(x);
        call.addOperand(y);
        model.addObjective(call, LSBBObjectiveDirection.Minimize);
        model.close();

        ls.getParam().setEvaluationLimit(20);
        ls.solve();

        LSBBSolution solution = ls.getSolution();
        System.out.println("x=" + solution.getDoubleValue(x));
        System.out.println("y=" + solution.getDoubleValue(y));
        System.out.println("obj:" + solution.getDoubleValue(call));
    }
}

Black-Box optimization in .NET

In .NET, a black-box function is passed to LocalSolverBlackBox as a delegate method taking as a single parameter a LSBBNativeContext object. The method uses this context to access the decision values of the current point to evaluate and returns the value of the function at this point. In the example we use a static method:

public static double BraninEval(LSBBNativeContext context) {
    double x = context.GetDoubleValue(0);
    double y = context.GetDoubleValue(1);

    return Math.Pow(y - (5.1 / (4.0 * Math.PI * Math.PI)) * x * x
        + 5.0 / Math.PI * x - 6, 2)
        + 10 * (1 - 1 / (8.0 * Math.PI)) * Math.Cos(x) + 10;
}

A LocalSolverBlackBox model is then created to optimize this black-box function. The black-box function is transformed into a LSBBExpression object using the CreateNativeFunction method. The decisions are associated to the function using a LSBBExpression object of type Call. The model is then parametrize to use 20 evaluations of the black-box function. The solution value can be obtained after the resolution using a LSBBSolution object:

public static void Main(string[] args)
{
    LocalSolverBlackBox ls = new LocalSolverBlackBox();
    LSBBModel model = ls.GetModel();
    LSBBExpression x = model.Float(-5,10);
    LSBBExpression y = model.Float(0,15);
    LSBBExpression f = model.CreateNativeFunction(BraninEval);
    LSBBExpression call = model.Call();
    call.AddOperand(f);
    call.AddOperand(x);
    call.AddOperand(y);
    model.AddObjective(call, LSBBObjectiveDirection.Minimize);
    model.Close();
    ls.GetParam().SetEvaluationLimit(20);
    ls.Solve();
    LSBBSolution sol = ls.GetSolution();
    Console.WriteLine("x="+sol.GetDoubleValue(x));
    Console.WriteLine("y="+sol.GetDoubleValue(y));
    Console.WriteLine("obj:"+sol.GetDoubleValue(call));
}

Black-Box optimization in Python

In Python, a black-function is simply a function or a method passed to LocalSolverBlackBox. This method only have a single parameter of type LSBBNativeContext object. The method uses this context to access the decision values of the current point to evaluate and returns the value of the function at this point:

def branin_eval(context):
    x = context.get(0)
    y = context.get(1)
    return math.pow(y - (5.1 / (4.0 * math.pi * math.pi)) * x * x
        + 5.0 / math.pi * x - 6, 2)
        + 10 * (1 - 1 / (8.0 * math.pi)) * math.cos(x) + 10

A LocalSolverBlackBox model is then created to optimize this black-box function. The black-box function is transformed into a LSBBExpression object using the create_native_function method. The decisions are associated to the function using a LSBBExpression object of type CALL. The model is then parametrize to use 20 evaluations of the black-box function. The solution value can be obtained after the resolution using a LSBBSolution object:

with localsolverblackbox.LocalSolverBlackBox() as ls:
   model = ls.get_model()
   x = model.float(-5,10)
   y = model.float(0,15)
   f = model.create_native_function(branin_eval)
   call = model.call()
   call.add_operand(f)
   call.add_operand(x)
   call.add_operand(y)
   model.add_objective(call, localsolverblackbox.LSBBObjectiveDirection.MINIMIZE)
   model.close()
   ls.get_param().set_evaluation_limit(20)
   ls.solve()
   sol = ls.get_solution()
   print "x=" + str(sol.get_value(x))
   print "y=" + str(sol.get_value(y))
   print "obj:" + str(sol.get_value(call))