Code indexing in gitaly is broken and leads to code not being visible to the user. We work on the issue with highest priority.

Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
FixedPisaNsga2.tcc 34.78 KiB
#include <iostream>
#include <sstream>
#include <cassert>
#include <cmath>
#include <cstring>
#include <set>
#include <cstdlib>
#include <string>
#include <limits>

#include <sys/stat.h>

#include "boost/algorithm/string.hpp"

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/map.hpp>

#include "Util/OptPilotException.h"
#include "Util/MPIHelper.h"

#include "Util/Trace/TraceComponent.h"
#include "Util/Trace/Timestamp.h"
#include "Util/Trace/FileSink.h"


template< template <class> class CO, template <class> class MO >
FixedPisaNsga2<CO, MO>::FixedPisaNsga2(
                           Expressions::Named_t objectives,
                           Expressions::Named_t constraints,
                           DVarContainer_t dvars,
                           size_t dim, Comm::Bundle_t comms,
                           CmdArguments_t args,
                           std::vector<double> hypervolRef)
             : Optimizer(comms.opt)
             , statistics_(new Statistics<size_t>("individuals"))
             , comms_(comms)
             , objectives_m(objectives)
             , constraints_m(constraints)
             , dvars_m(dvars)
             , args_m(args)
             , dim_m(dim)
             , hvol_ref_m(hypervolRef)
{
    my_local_pid_ = 0;
    MPI_Comm_rank(comms_.opt, &my_local_pid_);

    //FIXME: proper rand gen initialization (use boost?!)
    srand(time(NULL) + comms_.island_id);

    dump_freq_       = args->getArg<int>("dump-freq", 1, false);
    maxGenerations_m = args->getArg<int>("maxGenerations", true);
    resultFile_m     = args->getArg<std::string>("outfile", "-th_generation.dat", false);
    resultDir_m      = args->getArg<std::string>("outdir", "generations", false);

    // create output directory if it does not exists
    struct stat dirInfo;
    if(stat(resultDir_m.c_str(),&dirInfo) != 0)
        mkdir((const char*)(resultDir_m.c_str()), 0777);

    // solution exchange frequency
    exchangeSolStateFreq_m = args->getArg<size_t>("sol-synch", 0, false);

    // convergence arguments
    hvol_eps_           = args->getArg<double>("epsilon", 1e-3, false);
    expected_hvol_      = args->getArg<double>("expected-hypervol", 0.0,
                                               false);
    conv_hvol_progress_ = args->getArg<double>("conv-hvol-prog", 0.0, false);
    current_hvol_       = std::numeric_limits<double>::max();
    hvol_progress_      = std::numeric_limits<double>::max();

    //XXX: we can also set alpha_m to number of workers
    size_t num_ind_in_generation = 2;
    num_ind_in_generation = args->getArg<int>("num-ind-gen", 2, false);
    alpha_m               = args->getArg<int>("initialPopulation", true);
    lambda_m              = num_ind_in_generation;
    //mu_m                = num_ind_in_generation;
    initialOptimization_m = args->getArg<bool>("initial-optimization", false);

    file_param_descr_ = "%ID,";

    Expressions::Named_t::iterator it;
    for(it = objectives_m.begin(); it != objectives_m.end(); it++)
        file_param_descr_ += '%' + it->first + ',';

    file_param_descr_ += " DVAR: ";

    DVarContainer_t::iterator itr;
    std::vector<std::string> dNames;

    for(itr = dvars_m.begin(); itr != dvars_m.end(); itr++) {
        std::string dName = boost::get<VAR_NAME>(itr->second);
        file_param_descr_ += '%' + dName + ',';
        dNames.push_back(dName);
        dVarBounds_m.push_back(
                std::pair<double, double>
                    (boost::get<LOWER_BOUND>(itr->second),
                     boost::get<UPPER_BOUND>(itr->second)));
    }
    file_param_descr_ = file_param_descr_.substr(0,
                                                 file_param_descr_.size()-1);


    // setup variator
    variator_m.reset(new Variator_t(dNames, dVarBounds_m, constraints_m, args));


    // Traces and statistics
    std::ostringstream trace_filename;
    trace_filename << "opt.trace." << comms_.island_id;
    job_trace_.reset(new Trace("Optimizer Job Trace"));
    job_trace_->registerComponent( "sink",
            boost::shared_ptr<TraceComponent>(
                new FileSink(trace_filename.str())));

    std::ostringstream prog_filename;
    prog_filename << "opt.progress." << comms_.island_id;
    progress_.reset(new Trace("Optimizer Progress"));
    progress_->registerComponent( "timestamp",
            boost::shared_ptr<TraceComponent>(new Timestamp()));
    progress_->registerComponent( "sink",
            boost::shared_ptr<TraceComponent>(
                new FileSink(prog_filename.str())));

    statistics_->registerStatistic("accepted", 0);
    statistics_->registerStatistic("infeasible", 0);
}

template<
      template <class> class CO
    , template <class> class MO
>
FixedPisaNsga2<CO, MO>::~FixedPisaNsga2()
{}

template<
      template <class> class CO
    , template <class> class MO
>
void FixedPisaNsga2<CO, MO>::initialize() {

    curState_m = Initialize;
    initialized_m = false;

    // start poll loop
    run_clock_start_ = boost::chrono::system_clock::now();
    last_clock_      = boost::chrono::system_clock::now();
    run();

    // run has ended
    bool compHyvol = (objectives_m.size() > (hyper_opt / 2 + 1));
    if (compHyvol)
        current_hvol_ =
          variator_m->population()->computeHypervolume(comms_.island_id, hvol_ref_m);

    boost::chrono::duration<double> total =
        boost::chrono::system_clock::now() - run_clock_start_;
    std::ostringstream stats;
    stats << "__________________________________________" << std::endl;
    stats << "GENERATION " <<  act_gen << std::endl;
    stats << "TOTAL = " << total.count() << "s" << std::endl;
    if (compHyvol)
        stats << "HYPERVOLUME = " << current_hvol_ << std::endl;
    stats << "time per accepted ind   = "
          << total.count()/statistics_->getStatisticValue("accepted")
          << std::endl;
    stats << "time per infeasible ind = "
          << total.count()/statistics_->getStatisticValue("infeasible")
          << std::endl;
    statistics_->dumpStatistics(stats);
    stats << "__________________________________________" << std::endl;
    progress_->log(stats);

}


template< template <class> class CO, template <class> class MO >
bool FixedPisaNsga2<CO, MO>::onMessage(MPI_Status status, size_t length) {

    typedef typename FixedPisaNsga2::Individual_t individual;

    MPITag_t tag = MPITag_t(status.MPI_TAG);
    switch(tag) {

    case EXCHANGE_SOL_STATE_RES_SIZE_TAG: {

        size_t buf_size = length;
        size_t pilot_rank = status.MPI_SOURCE;

        std::ostringstream dump;
        dump << "new results from other cores " << buf_size << std::endl;
        job_trace_->log(dump);

        char *buffer = new char[buf_size];
        MPI_Recv(buffer, buf_size, MPI_CHAR, pilot_rank,
                 MPI_EXCHANGE_SOL_STATE_RES_TAG, comms_.opt, &status);

        dump.clear();
        dump.str(std::string());
        dump << "got results from other cores " << buf_size << std::endl;
        job_trace_->log(dump);

        SolutionState_t new_states;
        std::istringstream is(buffer);
        boost::archive::text_iarchive ia(is);
        ia >> new_states;
        delete[] buffer;

        std::set<unsigned int> new_state_ids;
        for (individual ind : new_states) {

            // only insert individual if not already in population
            if(variator_m->population()->isRepresentedInPopulation(ind.genes))
                continue;

            boost::shared_ptr<individual> new_ind(new individual);
            new_ind->genes = ind.genes;
            new_ind->objectives = ind.objectives;

            //XXX:   can we pass more than lambda_m files to selector?
            unsigned int id =
                variator_m->population()->add_individual(new_ind);
            finishedBuffer_m.push(id);

            dump.clear();
            dump.str(std::string());
            dump << "individual (ID: " << id
                 << ") successfully migrated from another island" << std::endl;
            job_trace_->log(dump);
        }

        return true;
    }

    case REQUEST_FINISHED: {

        unsigned int jid = static_cast<unsigned int>(length);
        typename std::map<size_t, boost::shared_ptr<individual> >::iterator it;
        it = jobmapping_m.find(jid);

        std::ostringstream dump;
        dump << "job with ID " << jid << " delivered results" << std::endl;
        job_trace_->log(dump);

        if(it == jobmapping_m.end()) {
            dump << "\t |-> NOT FOUND!" << std::endl;
            job_trace_->log(dump);
            std::cout << "NON-EXISTING JOB with ID = " << jid << std::endl;
            throw OptPilotException("FixedPisaNsga2::onMessage",
                    "non-existing job");
        }

        boost::shared_ptr<individual> ind = it->second;
        jobmapping_m.erase(it);

        //size_t dummy = 1;
        //MPI_Send(&dummy, 1, MPI_UNSIGNED_LONG, status.MPI_SOURCE,
        //         MPI_WORKER_FINISHED_ACK_TAG, comms_.listen);

        reqVarContainer_t res;
        MPI_Recv_reqvars(res, status.MPI_SOURCE, comms_.opt);

        ind->objectives.clear();

        //XXX: check order of genes
        reqVarContainer_t::iterator itr = res.begin();
        for(; itr != res.end(); ++itr) {
            // mark invalid if expression could not be evaluated or constraint does not hold
            if(!itr->second.is_valid || (itr->second.value.size() > 1 && !itr->second.value[0])) {
                std::ostringstream dump;
                if (!itr->second.is_valid) {
                    dump << "invalid individual, objective or constraint\"" << itr->first
                         << "\" failed to be evaluated correctly"
                         << std::endl;
                } else {
                    dump << "invalid individual, constraint \"" << itr->first
                         << "\" failed to yield true; result: " << itr->second.value[1]
                         << std::endl;
                }
                job_trace_->log(dump);
                variator_m->infeasible(ind);
                statistics_->changeStatisticBy("infeasible", 1);
                dispatch_forward_solves();
                return true;
            } else {
                 // update objective value for valid objective
                 if(itr->second.value.size() == 1)
                    ind->objectives.push_back(itr->second.value[0]);
            }
        }

        finishedBuffer_m.push(jid);
        statistics_->changeStatisticBy("accepted", 1);

        return true;
    }

    default: {
        std::cout << "(FixedPisaNsga2) Error: unexpected MPI_TAG: "
                  << status.MPI_TAG << std::endl;
        return false;
    }
    }
}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::postPoll() {

    // whenever lambda children are ready and we are in the variator phase
    // run the selector
    // std::ostringstream debug;
    // debug << "IN POST POLL: ";
    // debug << finishedBuffer_m.size() << " / " << lambda_m << std::endl;
    // debug << getStateString(curState_m) << std::endl;
    // progress_->log(debug);
    if(finishedBuffer_m.size() >= lambda_m && curState_m == Variate) {
        //std::cout << "▉";
        std::cout << "░" << std::flush;

        bool compHyvol = (objectives_m.size() > (hyper_opt / 2 + 1));
        if (compHyvol) {
            double hvol =
              variator_m->population()->computeHypervolume(comms_.island_id, hvol_ref_m);
            hvol_progress_ = fabs(current_hvol_ - hvol) / current_hvol_;
            current_hvol_ = hvol;
        }

        boost::chrono::duration<double> total =
            boost::chrono::system_clock::now() - run_clock_start_;
        boost::chrono::duration<double> dt    =
            boost::chrono::system_clock::now() - last_clock_;
        last_clock_ = boost::chrono::system_clock::now();
        std::ostringstream stats;
        stats << "__________________________________________" << std::endl;
        stats << "Arriving at generation " << act_gen + 1     << std::endl;
        stats << "dt = " << dt.count() << "s, total = " << total.count()
              << "s" << std::endl;
        if (compHyvol)
            stats << "Hypervolume = " << current_hvol_        << std::endl;
        stats << "__________________________________________" << std::endl;
        progress_->log(stats);

        // dump parents of current generation for visualization purposes
        if((act_gen + 1) % dump_freq_ == 0) {
            dumpPopulationToFile();
            dumpPopulationToJSON();
        }

        // we can only send all finished individuals (selector does not support
        // variable size).
        toSelectorAndCommit();

        exchangeSolutionStates();

        //XXX: at the end of the Variate state (previous runStateMachine()
        //     call) we can safely change the state.
        curState_m = Select;

        act_gen++;
    }

    // state will be reset to whatever is in the state file
    runStateMachine();
}


template< template <class> class CO , template <class> class MO >
void FixedPisaNsga2<CO, MO>::exchangeSolutionStates() {

    typedef typename FixedPisaNsga2::Individual_t individual;

    size_t num_masters = args_m->getArg<size_t>("num-masters", 1, false);

    if(num_masters <= 1 ||
       exchangeSolStateFreq_m == 0 ||
       act_gen % exchangeSolStateFreq_m != 0)
        return;

    int pilot_rank = comms_.master_local_pid;

    std::ostringstream os;
    boost::archive::text_oarchive oa(os);

    SolutionState_t population;
    typename std::map<unsigned int, boost::shared_ptr<individual> >::iterator itr;
    for(itr  = variator_m->population()->begin();
        itr != variator_m->population()->end(); itr++) {

        individual ind;
        ind.genes = std::vector<double>(itr->second->genes);
        ind.objectives = std::vector<double>(itr->second->objectives);
        population.push_back(ind);
    }

    oa << population;

    size_t buf_size = os.str().length();

    std::ostringstream dump;
    dump << "sending my buffer size " << buf_size << " bytes to PILOT"
         << std::endl;
    job_trace_->log(dump);

    MPI_Send(&buf_size, 1, MPI_UNSIGNED_LONG, pilot_rank,
             EXCHANGE_SOL_STATE_TAG, comms_.opt);

    char *buffer = new char[buf_size];
    memcpy(buffer, os.str().c_str(), buf_size);
    MPI_Send(buffer, buf_size, MPI_CHAR, pilot_rank,
             MPI_EXCHANGE_SOL_STATE_DATA_TAG, comms_.opt);
    delete[] buffer;

    dump.clear();
    dump.str(std::string());
    dump << "Sent " << buf_size << " bytes to PILOT" << std::endl;
    job_trace_->log(dump);
}

template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::toSelectorAndCommit() {

    to_selector_.clear();
    selector_mu_ = finishedBuffer_m.size();
    for(size_t i = 0; i < selector_mu_; i++) {
        unsigned int id = finishedBuffer_m.front();
        to_selector_.insert(id);
        finishedBuffer_m.pop();
    }

    variator_m->population()->commit_individuals(to_selector_);
}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::dispatch_forward_solves() {

    typedef typename FixedPisaNsga2::Individual_t individual;

    while(variator_m->hasMoreIndividualsToEvaluate()) {

        //reqVarContainer_t reqs;
        //Expressions::Named_t::iterator it;
        //for(it = objectives_m.begin(); it != objectives_m.end(); it++) {
            //std::set<std::string> vars = it->second.getReqVars();
            //std::set<std::string>::iterator setitr;
            //for(setitr = vars.begin(); setitr != vars.end(); setitr++) {
                //if(reqs.count(*setitr) == 0) {
                    //reqVarInfo_t tmp;
                    //tmp.type = EVALUATE;
                    //tmp.value = 0.0;
                    //reqs.insert(std::pair<std::string, reqVarInfo_t>
                        //(*setitr, tmp));
                //}
            //}
        //}

        boost::shared_ptr<individual> ind =
            variator_m->popIndividualToEvaluate();
        Param_t params;
        DVarContainer_t::iterator itr;
        size_t i = 0;
        for(itr = dvars_m.begin(); itr != dvars_m.end(); itr++, i++) {
            params.insert(
                std::pair<std::string, double>
                    (boost::get<VAR_NAME>(itr->second),
                     ind->genes[i]));
        }

        size_t jid = static_cast<size_t>(ind->id);
        int pilot_rank = comms_.master_local_pid;

        // now send the request to the pilot
        MPI_Send(&jid, 1, MPI_UNSIGNED_LONG, pilot_rank, OPT_NEW_JOB_TAG, comms_.opt);

        MPI_Send_params(params, pilot_rank, comms_.opt);

        //MPI_Send_reqvars(reqs, pilot_rank, comms_.opt);

        jobmapping_m.insert(
                std::pair<size_t, boost::shared_ptr<individual> >(jid, ind));

        std::ostringstream dump;
        dump << "dispatched simulation with ID " << jid << std::endl;
        job_trace_->log(dump);
    }
}

template<
      template <class> class CO
    , template <class> class MO
>
void FixedPisaNsga2<CO, MO>::runStateMachine() {

    switch(curState_m) {

    // State 2 of the FSM: we read mu parents
    // and create lambda children by variation of the individuals specified
    // in sel file.
    case Variate: {

        if( (maxGenerations_m > 0 && act_gen >= maxGenerations_m) ||
            (hvol_progress_ < conv_hvol_progress_) ||
            (expected_hvol_ > 0.0 && fabs(current_hvol_ - expected_hvol_) < hvol_eps_)
          ) {
            curState_m = Stop;
        } else {
            if(parent_queue_.size() > 0) {
                std::vector<unsigned int> parents(parent_queue_.begin(),
                                                  parent_queue_.end());
                parent_queue_.clear();

                // remove non-surviving individuals
                //std::set<unsigned int> survivors(archive_.begin(),
                                                 //archive_.end());
                std::set<unsigned int> survivors(pp_all.begin(),
                                                 pp_all.end());

                variator_m->population()->keepSurvivors(survivors);

                // only enqueue new individuals to pilot, the poll loop will
                // feed results back to the selector.
                //FIXME: variate works on staging set!!!
                variator_m->variate(parents);
                dispatch_forward_solves();

                curState_m = Variate;
            }
        }
        break;
    }

    // State 0 of the FSM: generate initial population and write
    // information about initial population to ini file.
    case Initialize: {
        if(initialized_m == false) {
            if (initialOptimization_m == true) {
              // double population such that workers keep busy until full initial population is found
              variator_m->initial_population(2*alpha_m);
            } else {
              variator_m->initial_population(alpha_m);
            }
            dispatch_forward_solves();
            initialized_m = true;
        }

        //XXX: wait till the first alpha_m individuals are ready then start
        //     the selector
        if(finishedBuffer_m.size() >= alpha_m) {

            act_gen = 1;
            toSelectorAndCommit();
            curState_m = InitializeSelector;

            if (initialOptimization_m == false) {
                //FIXME: and double the population again to have all workers busy
                variator_m->initial_population(alpha_m);
                dispatch_forward_solves();
            }
        }
        break;
    }

    // State 4 of the FSM: stopping state for the variator.
    case Stop: {
        // variator_m->population()->keepSurvivors(archive_);
        dumpPopulationToFile();
        dumpPopulationToJSON();

        // don't clean population otherwise final hypervolume calculation can't be done
        //variator_m->population()->clean_population();
        curState_m = VariatorStopped;

        // notify pilot that we have converged
        int dummy = 0;
        MPI_Request req;
        MPI_Isend(&dummy, 1, MPI_INT, comms_.master_local_pid,
                  MPI_OPT_CONVERGED_TAG, comms_.opt, &req);

        break;
    }

    // State 7 of the FSM: stopping state for the selector.
    // case SelectorStopped: {
    //     curState_m = Stop;
    //     break;
    // }

    // State 8 of the FSM: reset the variator, restart in state 0.
    // case Reset: {
    //     act_gen = 1;
    //     variator_m->population()->keepSurvivors(archive_);
    //     dumpPopulationToFile();
    //     dumpPopulationToJSON();

    //     variator_m->population()->clean_population();
    //     curState_m = ReadyForReset;
    //     break;
    // }

    // State 11 of the FSM: selector has just reset and is ready to
    // start again in state 1.
    // case Restart: {
    //     curState_m = Initialize;
    //     break;
    // }


//-----------------------|    selector STM   |-----------------------------//

    case InitializeSelector: {

        selection();

        // write arc file (all individuals that could ever be used again)
        typedef typename FixedPisaNsga2::Individual_t individual;
        typename std::map<unsigned int, boost::shared_ptr<individual> >
            ::iterator it;
        for(it = variator_m->population()->begin();
            it != variator_m->population()->end(); it++) {
            //archive_.insert(it->first);
            pp_all.push_back(it->first);
        }

        curState_m = Variate;

        break;
    }

    case Select: {
        selection();
        curState_m = Variate;

        break;
    }

    // variator terminated
    case VariatorStopped: {
        curState_m = VariatorTerminate;
        break;
    }

    // // variator ready for reset
    // case ReadyForReset: {
    //     curState_m = ReadyForResetS;
    //     break;
    // }

    // // reset
    // case ReadyForResetS: {
    //     curState_m = Restart;
    //     break;
    // }

    case VariatorTerminate:
    default:
        break;

    }
}




template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::dumpPopulationToFile() {

    // only dump old data format if the user requests it
    if(! args_m->getArg<bool>("dump-dat", false, false)) return;

    typedef typename FixedPisaNsga2::Individual_t individual;
    boost::shared_ptr<individual> temp;

    std::ofstream file;
    std::ostringstream filename;
    filename << resultDir_m << "/" << act_gen << "_" << resultFile_m
             << "_" << comms_.island_id;
    file.open(filename.str().c_str(), std::ios::out);

    typename std::map<unsigned int, boost::shared_ptr<individual> >::iterator it;
    it = variator_m->population()->begin();
    auto maxID = it->first;
    for(it ++; it != variator_m->population()->end(); it++) {
        if (it->first > maxID)
            maxID = it->first;
    }
    size_t numDigits = std::to_string(maxID).length();
    size_t next = 0, last = 0;
    std::string delimiter = ",";
    next = file_param_descr_.find(delimiter, last);
    file << std::setw(numDigits + 1) << file_param_descr_.substr(last, next - last) << " ";
    last = next + 1;
    while ((next = file_param_descr_.find(delimiter, last)) != std::string::npos) {
        size_t next2 = file_param_descr_.substr(last, next - last).find(":");
        if (next2 != std::string::npos) {
            last += next2 + 1;
            file << "DVAR: ";
        }
        file << std::setw(14) << file_param_descr_.substr(last, next - last) << " ";
        last = next + 1;
    }
    file << std::setw(14) << file_param_descr_.substr(last) << std::endl;

    file.precision(6);
    file << std::scientific;
    for(it = variator_m->population()->begin();
        it != variator_m->population()->end(); it++) {

        file << std::setw(numDigits + 1) << it->first << " ";

        temp = it->second;
        for(size_t i=0; i<temp->objectives.size(); i++)
            file << std::setw(14) << temp->objectives[i] << " ";
        file << "      ";
        for(size_t i=0; i<temp->genes.size(); i++)
            file << std::setw(14) << temp->genes[i] << " ";

        file << std::endl;
    }

    file.close();

}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::dumpPopulationToJSON() {

    typedef typename FixedPisaNsga2::Individual_t individual;
    boost::shared_ptr<individual> temp;

    std::ofstream file;
    std::ostringstream filename;
    filename << resultDir_m << "/" << act_gen << "_" << resultFile_m
             << "_" << comms_.island_id << ".json";
    file.open(filename.str().c_str(), std::ios::out);

    file << "{" << std::endl;
    file << "\t" << "\"name\": " << "\"opt-pilot\"," << std::endl;
    
    file << "\t" << "\"dvar-bounds\": {" << std::endl;
    DVarContainer_t::iterator itr = dvars_m.begin();
    for ( bounds_t::iterator it = dVarBounds_m.begin();
          it != dVarBounds_m.end(); ++it, ++itr )
    {
         file << "\t\t\"" << boost::get<VAR_NAME>(itr->second) << "\": "
              << "[ " << it->first << ", " << it->second << " ]";
         
         if ( it != dVarBounds_m.end() - 1 )
             file << ",";
         file << "\n";
    }
    file << "\t}\n\t," << std::endl;
    
    file << "\t" << "\"constraints\": [" << std::endl;
    for ( Expressions::Named_t::iterator it = constraints_m.begin();
          it != constraints_m.end(); ++it )
    {
        std::string s = it->second->toString();
        /// cleanup string to make json reader happy
        s.erase(std::remove(s.begin(), s.end(), '"'), s.end());

        file << "\t\t\"" << s << "\"";
        
        if ( it != std::prev(constraints_m.end(), 1) )
            file << ",";
        file << "\n";
    }
    file << "\t]\n\t," << std::endl;
    
    file << "\t" << "\"solutions\": " << "[" << std::endl;

    typename std::map<unsigned int, boost::shared_ptr<individual> >::iterator it;
    for(it = variator_m->population()->begin();
        it != variator_m->population()->end(); it++) {

        if(it != variator_m->population()->begin())
            file << "\t" << "," << std::endl;

        file << "\t" << "{" << std::endl;
        file << "\t" << "\t" << "\"ID\": " << it->first << "," << std::endl;

        Expressions::Named_t::iterator expr_it;
        expr_it = objectives_m.begin();
        temp = it->second;
        
        file << "\t\t\"obj\":\n" << "\t\t{\n";
        for(size_t i=0; i < temp->objectives.size(); i++, expr_it++) {
            file << "\t" << "\t" << "\t" << "\"" << expr_it->first << "\": "
                 << temp->objectives[i];
            if( i + 1 != temp->objectives.size())
                file << ",";
            file << std::endl;
        }
        file << "\t\t}\n" << "\t\t,\n" << "\t\t\"dvar\":\n" << "\t\t{\n";
        size_t i = 0;
        for(itr = dvars_m.begin(); itr != dvars_m.end(); ++i, ++itr) {
            file << "\t\t\t\"" << boost::get<VAR_NAME>(itr->second) << "\": "
                 << temp->genes[i];
            if ( i + 1 != temp->genes.size())
                file << ",";
            file << "\n";
        }
        file << "\t\t}\n";

        file << "\t" << "}" << std::endl;
    }

    file << "\t" << "]" << std::endl;
    file << "}" << std::endl;
    file.close();
}




/*-----------------------| selection functions|--------------------------*/

template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::selection() {

    /* Join offspring individuals from variator to population */
    mergeOffspring();

    int size = pp_all.size();

    /* Create internal data structures for selection process */
    copies.resize(size);
    dist.resize(size);
    for (int i = 0; i < size; i++) front.push_back(std::vector<int>(size));


    /* Calculates NSGA2 fitness values for all individuals */
    calcFitnesses();

    /* Calculates distance cuboids */
    calcDistances();
    /* Performs environmental selection
       (truncates 'pp_all' to size 'alpha') */
    environmentalSelection();

    /* Performs mating selection
       (fills mating pool / offspring population pp_sel */
    matingSelection();


    copies.clear();
    dist.clear();
    front.clear();
}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::mergeOffspring() {
    pp_all.insert(pp_all.end(), to_selector_.begin(), to_selector_.end());
    to_selector_.clear();
}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::calcFitnesses()
{
    int i, j, l;
    int size = pp_all.size();

    std::vector<int> d(size);
    std::vector<int> f(size);

    /* initialize fitness and strength values */
    for (i = 0; i < size; i++) {
        fitness_.insert(std::pair<size_t, double>(pp_all[i], 0.0));
        d[i] = 1;
        f[i] = 1;
        copies[i] = 0;
    }

    /* calculate strength values */
    int num = size;
    for (l = 0; l < size; l++) {
        /* find next front */
        for (i = 0; i < size; i++) {
            d[i] = 0;
            if (f[i] != -1) {
                for (j = 0; j < i && d[i] == 0; j++)
                    if (f[j] != -1)
                        if (dominates(pp_all[j], pp_all[i]))
                            d[i] = 1;
                for(j = i+1; j < size && d[i] == 0; j++)
                    if (f[j] != -1)
                        if (dominates(pp_all[j], pp_all[i]))
                            d[i] = 1;
            }
        }

        /* extract front */
        for (i = 0; i < size; i++) {
            if (f[i] != -1 && d[i] == 0) {
                fitness_[pp_all[i]] = l;
                f[i] = -1;
                num--;
                front[l][copies[l]] = i;
                copies[l] += 1;
            }
        }

        if (num == 0)
            break;
    }
}


template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::calcDistances()
{
    typedef typename FixedPisaNsga2::Individual_t individual;
    int i, j, l;
    int size = pp_all.size();
    double dmax = 1E99 / (dim_m + 1);

    for (i = 0; i < size; i++) {
        dist[i] = 1;
    }

    for (l = 0; l < size; l++) {
        for (size_t d = 0; d < dim_m; d++) {
            /* sort accorting to d-th objective */
            for (i = 0; i < copies[l]; i++) {
                int min_index = -1;
                int min = i;
                size_t idx1 = pp_all[front[l][i]];
                boost::shared_ptr<individual> ind1 =
                    variator_m->population()->get_individual(idx1);
                double obj_min = ind1->objectives[d];

                for (j = i + 1; j < copies[l]; j++) {

                    // size_t idx1 = pp_all[front[l][j]];
                    size_t idx2 = pp_all[front[l][j]];
                    // boost::shared_ptr<individual> ind1 =
                    //     variator_m->population()->get_individual(idx1);
                    boost::shared_ptr<individual> ind2 =
                        variator_m->population()->get_individual(idx2);
                    // double obj1 = ind1->objectives[d];
                    double obj2 = ind2->objectives[d];

                    if ( obj2 < obj_min ) {
                        min = j;
                        obj_min = obj2;
                    }
                }
                min_index = front[l][min];
                front[l][min] = front[l][i];
                front[l][i] = min_index;
            }

            /* add distances */
            for (i = 0; i < copies[l]; i++) {
                if (i == 0 || i == copies[l] - 1)
                    dist[front[l][i]] += dmax;
                else {
                    size_t idx1 = pp_all[front[l][i+1]];
                    size_t idx2 = pp_all[front[l][i-1]];
                    boost::shared_ptr<individual> ind1 =
                        variator_m->population()->get_individual(idx1);
                    boost::shared_ptr<individual> ind2 =
                        variator_m->population()->get_individual(idx2);
                    double obj1 = ind1->objectives[d];
                    double obj2 = ind2->objectives[d];

                    dist[front[l][i]] += obj1 - obj2;
                }
            }
        }
    }
}

template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::environmentalSelection() {

    int size = pp_all.size();

    for (int i = 0; i < size; i++)
        fitness_[pp_all[i]] += 1.0 / dist[i];

    for (size_t i = 0; i < alpha_m; i++) {
        int min = i;
        for (int j = i + 1; j < size; j++) {
            if (fitness_[pp_all[j]] < fitness_[pp_all[min]])
                min = j;
        }

        unsigned int p_min = pp_all[min];
        pp_all[min] = pp_all[i];
        pp_all[i] = p_min;
    }

    pp_all.erase(pp_all.begin() + alpha_m, pp_all.end());
}


/* Fills mating pool 'pp_sel' */
template< template <class> class CO, template <class> class MO >
void FixedPisaNsga2<CO, MO>::matingSelection() {

    //FIXME:
    int tournament = 2;

    for (size_t i = 0; i < selector_mu_; i++) {
        int winner = irand(pp_all.size());

        for (int j = 1; j < tournament; j++) {
            int opponent = irand(pp_all.size());
            if (fitness_[pp_all[opponent]] < fitness_[pp_all[winner]]
                || winner == opponent) {
                winner = opponent;
            }
        }
        parent_queue_.push_back(pp_all[winner]);
    }
}


/* Determines if one individual dominates another.
   Minimizing fitness values. */
template< template <class> class CO, template <class> class MO >
int FixedPisaNsga2<CO, MO>::dominates(unsigned int p_ind_a, unsigned int p_ind_b) {

    int a_is_worse = 0, b_is_worse = 0;
    // int equal = 1;

    typedef typename FixedPisaNsga2::Individual_t individual;
    boost::shared_ptr<individual> ind1 =
        variator_m->population()->get_individual(p_ind_a);
    boost::shared_ptr<individual> ind2 =
        variator_m->population()->get_individual(p_ind_b);

    for (size_t i = 0; i < ind1->objectives.size()/* && !a_is_worse*/; i++) {
        if (ind1->objectives[i] > ind2->objectives[i]) a_is_worse = 1;
        if (ind1->objectives[i] < ind2->objectives[i]) b_is_worse = 1;
        // a_is_worse = ind1->objectives[i] > ind2->objectives[i];
        // equal = (ind1->objectives[i] == ind2->objectives[i]) && equal;
    }

    return (b_is_worse && !a_is_worse);
    // return (!equal && !a_is_worse);
}

template< template <class> class CO, template <class> class MO >
std::string FixedPisaNsga2<CO, MO>::getStateString(PisaState_t state) const {
    switch(state) {
    case Initialize:
        return "Initialize";
    case InitializeSelector:
        return "InitializeSelector";
    case Variate:
        return "Variate";
    case Select:
        return "Select";
    case Stop:
        return "Stop";
    case VariatorStopped:
        return "VariatorStopped";
    case VariatorTerminate:
        return "VariatorTerminate";
    // case SelectorStopped:
    //     return "SelectorStopped";
    // case Reset:
    //     return "Reset";
    // case ReadyForReset:
    //     return "ReadyForReset";
    // case ReadyForResetS:
    //     return "ReadyForResetS";
    // case Restart:
    //     return "Restart";
    default:
        return "";
    }
}