13.8.5. Macro “reoptimizeHollowBarVizirSplitRuns.C

13.8.5.1. Objective

The objective of the macro is to be able to run an evolutionary algorithm (here we are using a genetic one) with a limited number of code estimation and restart it from where it stopped if it has not converged the first time. This is of utmost usefulness when running a resource-consumming code or (/and) when running on a cluster with a limited number of cpu time. The classical hollow bar example defined in Sizing of a hollow bar example problem is used to obtain a nice Pareto set/front.

13.8.5.2. Macro Uranie

#define TOLERANCE 0.001
#define NBmaxEVAL 1200
#define SIZE 500

using namespace URANIE::DataServer;
using namespace URANIE::Relauncher;
using namespace URANIE::Reoptimizer;

bool LaunchVizir(int RunNumber, TCanvas *fig1, bool fixed_seed=false)
{

    // variables
    TAttribute x("x", 0.0, 1.0),
      y("y", 0.0, 1.0),
      thick("thick"),
      sect("sect"),
      defor("defor");

    TCIntEval code("barAllCost");
    code.addInput(&x);
    code.addInput(&y);
    code.addOutput(&thick);
    code.addOutput(&sect);
    code.addOutput(&defor);

    // Create a runner
    TSequentialRun runner(&code);
    runner.startSlave();

    // Output to state whether convergence is reached
    bool hasConverged=false;
    if(runner.onMaster())
    {
        // Create the TDS
        TDataServer tds("vizirDemo", "Param de l'opt vizir pour la barre");
        tds.addAttribute(&x);
        tds.addAttribute(&y);

        TVizirGenetic solv;
        // Name of the file that will contain
        string filename="genetic.dump";
        std::vector<char> cstr(filename.c_str(), filename.c_str() + filename.size() + 1);
        /* Test whether genetic.dump exists. If not, it creates it and returns false, so
        that the "else" part is done to start the initialisation of the vizir algorithm. */
        if ( solv.setResume(NBmaxEVAL, &cstr[0]))
            cout << "Restarting Vizir" << endl;
        else solv.setSize(SIZE, NBmaxEVAL);

        // Create the multi-objective constrained optimizer
        TVizir2 opt(&tds, &runner, &solv);
        opt.setTolerance(TOLERANCE);
        // add the objective
        opt.addObjective(&sect);
        opt.addObjective(&defor);
        TGreaterFit positiv(0.4);
        opt.addConstraint(&thick,&positiv);

        /* resolution */
        opt.solverLoop();
        hasConverged=opt.isConverged();
        // Stop the slave processes
        runner.stopSlave();

        fig1->cd(RunNumber+1);
        tds.getTuple()->SetMarkerColor(2);
        tds.draw("defor:sect");
        stringstream tit; tit << "Run number "<<RunNumber+1;
        if(hasConverged) tit << ": Converged !";
        ((TH1F*)gPad->GetPrimitive("__tdshisto__0"))->SetTitle( tit.str().c_str() );
    }

    return hasConverged;
}

void reoptimizeHollowBarVizirSplitRuns(const string& figure="figure.png", const string& style="",
    bool fixed_seed=false)
{
    gROOT->LoadMacro("UserFunctions.C");
    // Delete previous file if it exists
    gSystem->Unlink("genetic.dump");

    bool finished=false;
    int i=0;
    TCanvas *fig1 = new TCanvas("fig1","fig1",1200,800);
    fig1->Divide(2,2);
    while ( ! finished )
    {
        finished=LaunchVizir(i, fig1, fixed_seed);
        i++;
    }
}

The idea is to show how to run this kind of configuration: the function LaunchVizir is the usual script one can run to get an optimisation with Vizir on the hollow bar problem. The aim is to create a Pareto set of 500 points (SIZE) but only allowing 1200 estimation (NBmaxEVAL). With this configuration we are sure that a first round of estimation will not converge, so we will have to restart the optimisation from the point we stopped. With this regard, the beginning of this function is trivial and the main point to be discussed arises once the solver is created.

        TVizirGenetic solv;
        // Name of the file that will contain
        string filename="genetic.dump";
        std::vector<char> cstr(filename.c_str(), filename.c_str() + filename.size() + 1);
        /* Test whether genetic.dump exists. If not, it creates it and returns false, so
        that the "else" part is done to start the initialisation of the vizir algorithm. */
        if ( solv.setResume(NBmaxEVAL, &cstr[0]))
            cout << "Restarting Vizir" << endl;
        else solv.setSize(SIZE, NBmaxEVAL);

Clearly here, the interesting part apart, from the definition of the name of the file in which the final state will be kept, is the first test on the solver, before using the setSize method. A new methods called setResume is called, with two arguments : the number of elements requested in the Pareto set and the name of the file in which to save the state or to restart from. This method returns “true” if genetic.dump is found and “false” if not. In the first case, the code will assume that this file is the result of a previous run and it will start the optimisation from the its content trying to get all the population non-dominated (if it’s not yet the case). If, on the other hand, no file is found, then the code knows that it would have to store the results of its process, in a file whose name is the second argument, and because the function returns “false”, then we move to the “else” part, that starts the optimisation.

Apart from this, the rest of the function is doing the optimisation, and plotting the pareto front in a provided canvas. The only new part here is the fact that the solver (its master in fact) is now able to tell whether it has converged or not through the following method

        hasConverged=opt.isConverged();

this argument being return as the results of the function.

This macro contains another function called reoptimizeHollowBarVizirSplitRuns which plays the role of the user in front of a ROOT-console. It defines the correct namespace, loads the function file and destroys previously existing genetic.dump files. From there it runs the LaunchVizir function as many times as needed (thanks to the boolean returned) as the used would do, by restarting the macro, even after exiting the ROOT console.

The plot shown below represent the Pareto front every time the genetic algorithm stops (at the fourth run, it finally converges !).

13.8.5.3. Graph

../../_images/reoptimizeHollowBarVizirSplitRuns.png

Figure 13.58 Graph of the macro “reoptimizeHollowBarVizirSplitRuns.C”