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);
        }
    }
}