Hosaki Function

Note

This problem can be resolved without the surrogate modeling 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 surrogate modeling on a simple and computationally inexpensive problem.

Principles learned

  • Create an external function

  • Enable the surrogate modeling on an external function

  • Set an evaluation limit to the function

Problem

../_images/hosaki_blackbox.svg

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 an external function. It receives the argument values via the LSExternalArgumentValues, and returns the value of the function at this point.

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 external function, an O_Call expression is created. This expression is then minimized.

To use the surrogate modeling feature, the method enableSurrogateModeling available on the LSExternalContext of the function is called. This method returns the LSSurrogateParameters, on which the maximum number of calls to the function can be set, as the function is usually computationally expensive.

Execution:
localsolver hosaki.lsp [evaluationLimit=] [solFileName=]
/********* hosaki.lsp *********/
use io;

// External function
function hosaki(x1, x2) {
    return (1 - 8*x1 + 7*pow(x1, 2) - 7*pow(x1, 3)/3 + pow(x1, 4)/4) * pow(x2, 2)
            * exp(-x2);
}

// Declares the optimization model
function model() {
    // Numerical decisions
    x1 <- float(0, 5);
    x2 <- float(0, 6);

    // Creates and calls the function
    f <- doubleExternalFunction(hosaki);
    funcCall <- call(f, x1, x2);

    // Enables surrogate modeling
    surrogateParams = f.context.enableSurrogateModeling();

    // Minimizes function call
    minimize(funcCall);
}

// Parameterizes the solver
function param() {
    if (evaluationLimit == nil) surrogateParams.evaluationLimit = 30;
    else surrogateParams.evaluationLimit = evaluationLimit;
}

// Writes the solution in a file
function output() {
    if (solFileName != nil) {
        local solFile = io.openWrite(solFileName);
        solFile.println("obj=", funcCall.value);
        solFile.println("x1=", x1.value);
        solFile.println("x2=", x2.value);
    }
}
Execution (Windows)
set PYTHONPATH=%LS_HOME%\bin\python
python hosaki.py
Execution (Linux)
export PYTHONPATH=/opt/localsolver_11_0/bin/python
python hosaki.py
########## hosaki.py ##########

import localsolver
import sys
import math


# External 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 the function
        f = model.create_double_external_function(hosaki_function)
        func_call = model.call(f, x1, x2)

        # Enables surrogate modeling
        surrogate_params = f.external_context.enable_surrogate_modeling()

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

        # Parameterizes the solver
        surrogate_params.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" % func_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\localsolver110.lib
hosaki
Compilation / Execution (Linux)
g++ hosaki.cpp -I/opt/localsolver_11_0/include -llocalsolver110 -lpthread -o hosaki
hosaki
/********* hosaki.cpp *********/

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

using namespace localsolver;
using namespace std;

/* External function */
class HosakiFunction : public LSExternalFunction<lsdouble> {
    lsdouble call(const LSExternalArgumentValues& 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 funcCall;

    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 the function
        HosakiFunction funcClass;
        LSExpression func = model.createExternalFunction(&funcClass);
        funcCall = model.call(func, x1, x2);

        // Enables surrogate modeling
        LSExternalContext context = func.getExternalContext();
        LSSurrogateParameters surrogateParams = context.enableSurrogateModeling();

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

        // Parameterizes the solver
        surrogateParams.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=" << funcCall.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
{
    /* External function */
    public class HosakiFunction {
        public double Call(LSExternalArgumentValues 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 funcCall;

    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 the function
        HosakiFunction hosakiFunction = new HosakiFunction();
        LSDoubleExternalFunction func = new LSDoubleExternalFunction(hosakiFunction.Call);
        LSExpression funcExpr = model.DoubleExternalFunction(func);
        funcCall = model.Call(funcExpr, x1, x2);

        // Enables surrogate modeling
        LSExternalContext context = funcExpr.GetExternalContext();
        LSSurrogateParameters surrogateParams = context.EnableSurrogateModeling();

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

        // Parameterizes the solver
        surrogateParams.SetEvaluationLimit(evaluationLimit);

        localsolver.Solve();
    }

    // Writes the solution in a file
    public void WriteSolution(string fileName)
    {
        using (StreamWriter output = new StreamWriter(fileName))
        {
            output.WriteLine("obj=" + funcCall.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_11_0/bin/localsolver.jar
java -cp /opt/localsolver_11_0/bin/localsolver.jar:. Hosaki
/********** Hosaki.java **********/

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

public class Hosaki {

    /* External function */
    private static class HosakiFunction implements LSDoubleExternalFunction {
        @Override
        public double call(LSExternalArgumentValues 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 funcCall;

    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 the function
        HosakiFunction function = new HosakiFunction();
        LSExpression func = model.doubleExternalFunction(function);
        funcCall = model.call(func, x1, x2);

        // Enables surrogate modeling
        LSExternalContext context = func.getExternalContext();
        LSSurrogateParameters surrogateParams = context.enableSurrogateModeling();

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

        // Parameterizes the solver
        surrogateParams.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=" + funcCall.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);
        }
    }
}