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.

Hosaki function

Note

This problem can be resolved without the black-box functionality (see branin function). Indeed, this functionality is useful when the objective function is computationally expensive. The purpose of this example is only to illustrate the use of a black-box function on a simple and computationally inexpensive problem.

Principles learned

  • Create a black-box function
  • Call a black-box function
  • Set the evaluation limit for the black-box function

Problem

../_images/hosaki_blackbox.png

Hosaki function is defined by

\[f(x) = (1 - 8x_1 + 7x_1^2 - \frac{7}{3} x_1^3 + \frac{1}{4} x_1^4)x_2^2 e^{-x_2}\]

This is a box-constrained problem.

For more details, see: hosaki_function.html

Download the example

Program

The objective function is defined by a black-box function. It receives the argument values via the LSBlackBoxArgumentValues, and returns the value of the function at this point.

The model is quite similar to a LocalSolver model without a black-box function. Two floating decision variables x1 and x2 are declared. The domains of these variables are respectively [0,5] and [0,6]. To calculate the value returned by the black-box function, an O_Call expression is created. This expression is then minimized.

As a black-box function is usually computationally expensive, the maximum number of calls to the function can be set with the LSBlackBoxContext.

Execution (Windows)
set PYTHONPATH=%LS_HOME%\bin\python
python hosaki.py
Execution (Linux)
export PYTHONPATH=/opt/localsolver_10_0/bin/python
python hosaki.py
########## hosaki.py ##########

import localsolver
import sys
import math

# Black-box function
def hosaki_function(argument_values):
    x1 = argument_values[0]
    x2 = argument_values[1]
    return ((1 - 8*x1 + 7*pow(x1, 2) - 7*pow(x1, 3)/3 + pow(x1, 4)/4) * pow(x2, 2)
        * math.exp(-x2))

def main(evaluation_limit, output_file):
    with localsolver.LocalSolver() as ls:
        # Declares the optimization model
        model = ls.model

        # Numerical decisions
        x1 = model.float(0, 5)
        x2 = model.float(0, 6)

        # Creates and calls blackbox function
        f = model.create_double_blackbox_function(hosaki_function)
        bb_call = model.call(f, x1, x2)

        # Minimizes function call
        model.minimize(bb_call)
        model.close()

        # Parameterizes the solver
        f.blackbox_context.evaluation_limit = evaluation_limit

        ls.solve()

        # Writes the solution in a file
        if output_file is not None:
            with open(output_file, 'w') as f:
                f.write("obj=%f\n" % bb_call.value)
                f.write("x1=%f\n" % x1.value)
                f.write("x2=%f\n" % x2.value)

if __name__ == '__main__':
    output_file = sys.argv[1] if len(sys.argv) > 1 else None
    evaluation_limit = int(sys.argv[2]) if len(sys.argv) > 2 else 30

    main(evaluation_limit, output_file)
Compilation / Execution (Windows)
cl /EHsc hosaki.cpp -I%LS_HOME%\include /link %LS_HOME%\bin\localsolver100.lib
hosaki
Compilation / Execution (Linux)
g++ hosaki.cpp -I/opt/localsolver_10_0/include -llocalsolver100 -lpthread -o hosaki
hosaki
//********* hosaki.cpp *********

#include <iostream>
#include <fstream>
#include <cmath>
#include "localsolver.h"

using namespace localsolver;
using namespace std;

/* Black-box function */
class HosakiFunction : public LSBlackBoxFunction<lsdouble> {
    lsdouble call(const LSBlackBoxArgumentValues& argumentValues) override {
        lsdouble x1 = argumentValues.getDoubleValue(0);
        lsdouble x2 = argumentValues.getDoubleValue(1);
        return (1 - 8*x1 + 7*x1*x1 - 7*pow(x1, 3)/3 + pow(x1, 4)/4) * x2*x2
            * exp(-x2);
    }
};

class Hosaki {
public:
    // Solver
    LocalSolver localsolver;

    // LS Program variables
    LSExpression x1;
    LSExpression x2;
    LSExpression bbCall;

    void solve(int evaluationLimit) {
        // Declares the optimization model
        LSModel model = localsolver.getModel();

        // Numerical decisions
        x1 = model.floatVar(0, 5);
        x2 = model.floatVar(0, 6);

        // Creates and calls blackbox function
        HosakiFunction bbFuncClass;
        LSExpression bbFunc = model.createBlackBoxFunction(&bbFuncClass);
        bbCall = model.call(bbFunc, x1, x2);

        // Minimizes function call
        model.minimize(bbCall);
        model.close();

        // Parameterizes the solver
        LSBlackBoxContext context = bbFunc.getBlackBoxContext();
        context.setEvaluationLimit(evaluationLimit);

        localsolver.solve();
    }

    // Writes the solution in a file
    void writeSolution(const string& fileName) {
        ofstream outfile;
        outfile.exceptions(ofstream::failbit | ofstream::badbit);
        outfile.open(fileName.c_str());
        outfile << "obj=" << bbCall.getDoubleValue() << endl;
        outfile << "x1=" << x1.getDoubleValue() << endl;
        outfile << "x2=" << x2.getDoubleValue() << endl;
    }
};

int main(int argc, char** argv) {
    const char* solFile = argc > 1 ? argv[1] : NULL;
    const char* strEvaluationLimit = argc > 2 ? argv[2] : "30";

    try {
        Hosaki model;
        model.solve(atoi(strEvaluationLimit));
        if (solFile != NULL) model.writeSolution(solFile);
    } catch (const exception& e) {
        cerr << "An error occurred: " << e.what() << endl;
        return 1;
    }
    return 0;
}
Compilation / Execution (Windows)
copy %LS_HOME%\bin\localsolvernet.dll .
csc Hosaki.cs /reference:localsolvernet.dll
Hosaki
/********** Hosaki.cs **********/

using System;
using System.IO;
using localsolver;

public class Hosaki : IDisposable
{
    /* Black-box function */
    public class HosakiFunction {
        public double Call(LSBlackBoxArgumentValues argumentValues) {
            double x1 = argumentValues.GetDoubleValue(0);
            double x2 = argumentValues.GetDoubleValue(1);
            return (1 - 8*x1 + 7*x1*x1 - 7*Math.Pow(x1, 3)/3 + Math.Pow(x1, 4)/4) * x2*x2
                * Math.Exp(-x2);
        }
    }

    // Solver
    private LocalSolver localsolver;

    // LS Program variables
    private LSExpression x1;
    private LSExpression x2;
    private LSExpression bbCall;

    public Hosaki()
    {
        localsolver = new LocalSolver();
    }

    public void Dispose()
    {
        if (localsolver != null)
            localsolver.Dispose();
    }

    public void Solve(int evaluationLimit)
    {
        // Declares the optimization model
        LSModel model = localsolver.GetModel();

        // Numerical decisions
        x1 = model.Float(0, 5);
        x2 = model.Float(0, 6);

        // Creates and calls blackbox function
        HosakiFunction func = new HosakiFunction();
        LSDoubleBlackBoxFunction bbFunc = new LSDoubleBlackBoxFunction(func.Call);
        LSExpression bbFuncExpr = model.DoubleBlackBoxFunction(bbFunc);
        bbCall = model.Call(bbFuncExpr, x1, x2);

        // Minimizes function call
        model.Minimize(bbCall);
        model.Close();

        // Parameterizes the solver
        LSBlackBoxContext context = bbFuncExpr.GetBlackBoxContext();
        context.SetEvaluationLimit(evaluationLimit);

        localsolver.Solve();
    }

    // Writes the solution in a file
    public void WriteSolution(string fileName)
    {
        using (StreamWriter output = new StreamWriter(fileName))
        {
            output.WriteLine("obj=" + bbCall.GetDoubleValue());
            output.WriteLine("x1=" + x1.GetDoubleValue());
            output.WriteLine("x2=" + x2.GetDoubleValue());
        }
    }

    public static void Main(string[] args)
    {
        string outputFile = args.Length > 0 ? args[0] : null;
        string strEvaluationLimit = args.Length > 1 ? args[1] : "30";

        using (Hosaki model = new Hosaki())
        {
            model.Solve(int.Parse(strEvaluationLimit));
            if (outputFile != null)
                model.WriteSolution(outputFile);
        }
    }
}
Compilation / Execution (Windows)
javac Hosaki.java -cp %LS_HOME%\bin\localsolver.jar
java -cp %LS_HOME%\bin\localsolver.jar;. Hosaki
Compilation / Execution (Linux)
javac Hosaki.java -cp /opt/localsolver_10_0/bin/localsolver.jar
java -cp /opt/localsolver_10_0/bin/localsolver.jar:. Hosaki
/********** Hosaki.java **********/

import java.io.*;
import java.lang.Math;
import localsolver.*;

public class Hosaki {

    /* Black-box function */
    private static class HosakiFunction implements LSDoubleBlackBoxFunction {
        @Override
        public double call(LSBlackBoxArgumentValues argumentValues) {
            double x1 = argumentValues.getDoubleValue(0);
            double x2 = argumentValues.getDoubleValue(1);
            return (1 - 8*x1 + 7*x1*x1 - 7*Math.pow(x1, 3)/3 + Math.pow(x1, 4)/4)
                * x2*x2 * Math.exp(-x2);
        }
    }

    // Solver
    private final LocalSolver localsolver;

    // LS Program variables
    private LSExpression x1;
    private LSExpression x2;
    private LSExpression bbCall;

    private Hosaki(LocalSolver localsolver) {
        this.localsolver = localsolver;
    }

    // Declares the optimization model
    private void solve(int evaluationLimit) {
        LSModel model = localsolver.getModel();

        // Numerical decisions
        x1 = model.floatVar(0, 5);
        x2 = model.floatVar(0, 6);

        // Creates and calls blackbox function
        HosakiFunction function = new HosakiFunction();
        LSExpression bbFunc = model.doubleBlackBoxFunction(function);
        bbCall = model.call(bbFunc, x1, x2);

        // Minimizes function call
        model.minimize(bbCall);
        model.close();

        // Parameterizes the solver
        LSBlackBoxContext context = bbFunc.getBlackBoxContext();
        context.setEvaluationLimit(evaluationLimit);

        localsolver.solve();
    }

    // Writes the solution in a file
    private void writeSolution(String fileName) throws IOException {
        try (PrintWriter output = new PrintWriter(fileName)) {
            output.println("obj=" + bbCall.getDoubleValue());
            output.println("x1=" + x1.getDoubleValue());
            output.println("x2=" + x2.getDoubleValue());
        }
    }

    public static void main(String[] args) {
        String outputFile = args.length > 0 ? args[0] : null;
        String strEvaluationLimit = args.length > 1 ? args[1] : "30";

         try (LocalSolver localsolver = new LocalSolver()) {
            Hosaki model = new Hosaki(localsolver);
            model.solve(Integer.parseInt(strEvaluationLimit));
            if (outputFile != null) {
                model.writeSolution(outputFile);
            }
        } catch (Exception ex) {
            System.err.println(ex);
            ex.printStackTrace();
            System.exit(1);
        }
    }
}