/*-------------------------------------------------------------------------------------*/
/* NOMAD - Nonlinear Optimization by Mesh Adaptive Direct search - version 3.6.1 */
/* */
/* Copyright (C) 2001-2012 Mark Abramson - the Boeing Company, Seattle */
/* Charles Audet - Ecole Polytechnique, Montreal */
/* Gilles Couture - Ecole Polytechnique, Montreal */
/* John Dennis - Rice University, Houston */
/* Sebastien Le Digabel - Ecole Polytechnique, Montreal */
/* Christophe Tribes - Ecole Polytechnique, Montreal */
/* */
/* funded in part by AFOSR and Exxon Mobil */
/* */
/* Author: Sebastien Le Digabel */
/* */
/* Contact information: */
/* Ecole Polytechnique de Montreal - GERAD */
/* C.P. 6079, Succ. Centre-ville, Montreal (Quebec) H3C 3A7 Canada */
/* e-mail: nomad@gerad.ca */
/* phone : 1-514-340-6053 #6928 */
/* fax : 1-514-340-5665 */
/* */
/* This program is free software: you can redistribute it and/or modify it under the */
/* terms of the GNU Lesser General Public License as published by the Free Software */
/* Foundation, either version 3 of the License, or (at your option) any later */
/* version. */
/* */
/* This program is distributed in the hope that it will be useful, but WITHOUT ANY */
/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A */
/* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License along */
/* with this program. If not, see . */
/* */
/* You can find information on the NOMAD software at www.gerad.ca/nomad */
/*-------------------------------------------------------------------------------------*/
/**
\file Cache.cpp
\brief Cache memorizing all evaluations (implementation)
\author Sebastien Le Digabel
\date 2010-04-12
\see Cache.hpp
*/
#include "Cache.hpp"
/*-----------------------------------*/
/* static members initialization */
/*-----------------------------------*/
std::set NOMAD::Cache::_locked_files;
/*------------------------------------------------------*/
/* init of _sizeof (private) */
/*------------------------------------------------------*/
int NOMAD::Cache::sizeof_init ( void ) const
{
return
sizeof ( _cache1 ) +
sizeof ( _cache2 ) +
sizeof ( _cache3 ) +
sizeof ( _locked_file) +
sizeof ( _it ) +
sizeof ( _out ) +
sizeof ( _sizeof );
}
/*---------------------------------------------------------------------*/
/* insertion of a point into the list of extern points (_extern_pts) */
/*---------------------------------------------------------------------*/
void NOMAD::Cache::insert_extern_point ( const NOMAD::Eval_Point & x ) const
{
if ( !x.get_current_run() )
_extern_pts.push_front ( &x );
}
/*----------------------------------------------------------*/
/* get the first extern point and remove it from the list */
/* (returns NULL if there is no extern point) */
/*----------------------------------------------------------*/
const NOMAD::Eval_Point * NOMAD::Cache::get_and_remove_extern_point ( void ) const
{
if ( _extern_pts.empty() )
return NULL;
const NOMAD::Eval_Point * extern_point = *_extern_pts.begin();
_extern_pts.pop_front();
return extern_point;
}
/*------------------------------------------*/
/* . search x in the cache */
/* . return NULL if x is not in the cache */
/*------------------------------------------*/
const NOMAD::Eval_Point * NOMAD::Cache::find ( const NOMAD::Eval_Point & x ) const
{
// check the eval types:
if ( x.get_eval_type() != _eval_type )
throw NOMAD::Cache::Cache_Error ( "Cache.cpp" , __LINE__ ,
"NOMAD::Cache:find(x): x.eval_type != cache.eval_type" );
// find:
std::set::const_iterator it;
NOMAD::cache_index_type cache_index;
const NOMAD::Eval_Point * cache_x
= NOMAD::Cache::find ( x , it , cache_index );
return cache_x;
}
/*----------------------------------------------------*/
/* . search x in the cache */
/* . return NULL if x is not in the cache */
/* . this version returns an iterator and indicates */
/* in which set the point has been found */
/* . private method */
/*----------------------------------------------------*/
const NOMAD::Eval_Point * NOMAD::Cache::find
( const NOMAD::Eval_Point & x ,
std::set::const_iterator & it ,
NOMAD::cache_index_type & cache_index ) const
{
// search in _cache2 (points to write in a cache file):
NOMAD::Cache_Point cp ( &x );
it = _cache2.find ( cp );
if ( it != _cache2.end() ) {
cache_index = NOMAD::CACHE_2;
return it->get_point();
}
// search in _cache3 (points saved in a file):
it = _cache3.find ( cp );
if ( it != _cache3.end() ) {
cache_index = NOMAD::CACHE_3;
return it->get_point();
}
// search in _cache1 (points read in an initial cache file):
it = _cache1.find ( cp );
if ( it != _cache1.end() ) {
cache_index = NOMAD::CACHE_1;
return it->get_point();
}
cache_index = NOMAD::UNDEFINED_CACHE;
return NULL;
}
/*-----------------------------------------------------*/
/* erase a point */
/*-----------------------------------------------------*/
/* . return true if the point has been found and */
/* removed from the cache */
/* . the point is not deleted from memory if its */
/* address does not match the address of the point */
/* in cache */
/*-----------------------------------------------------*/
bool NOMAD::Cache::erase ( const NOMAD::Eval_Point & x )
{
// check the eval types:
if ( x.get_eval_type() != _eval_type )
throw NOMAD::Cache::Cache_Error ( "Cache.cpp" , __LINE__ ,
"NOMAD::Cache:erase(x): x.eval_type != cache.eval_type" );
std::set::iterator it;
NOMAD::cache_index_type cache_index;
// search in cache:
const NOMAD::Eval_Point * cache_x = find ( x , it , cache_index );
// the point has been found:
if ( cache_x ) {
// remove the point from the list of extern points:
if ( cache_x->get_current_run() || x.get_current_run() ) {
std::list::const_iterator end2 = _extern_pts.end();
std::list::iterator it2 = _extern_pts.begin();
while ( it2 != end2 ) {
if ( *it2 == cache_x || *it2 == &x ) {
_extern_pts.erase ( it2 );
break;
}
++it2;
}
}
// erase the point in cache if its address is different from &x:
if ( cache_x != &x )
delete cache_x;
// remove the point from the cache:
_sizeof -= x.size_of();
switch ( cache_index ) {
case NOMAD::CACHE_1:
_cache1.erase ( it );
break;
case NOMAD::CACHE_2:
_cache2.erase ( it );
break;
case NOMAD::CACHE_3:
_cache3.erase ( it );
break;
case NOMAD::UNDEFINED_CACHE:
break;
}
return true;
}
return false;
}
/*-----------------------------------------------------*/
/* erase all points in cache and unlock _locked_file */
/*-----------------------------------------------------*/
void NOMAD::Cache::clear ( void )
{
const NOMAD::Eval_Point * x = begin();
while ( x ) {
delete x;
x = next();
}
_cache1.clear();
_cache2.clear();
_cache3.clear();
unlock();
_extern_pts.clear();
_sizeof = static_cast ( sizeof_init() );
}
/*------------------------------------------------*/
/* . insertion of a point in the cache (_cache2) */
/* . supposes that x is NOT already in cache */
/*------------------------------------------------*/
void NOMAD::Cache::insert ( const NOMAD::Eval_Point & x )
{
// check the eval types:
if ( x.get_eval_type() != _eval_type )
throw NOMAD::Cache::Cache_Error ( "Cache.cpp" , __LINE__ ,
"NOMAD::Cache:insert(x): x.eval_type != cache.eval_type" );
// insertion in _extern_pts:
insert_extern_point ( x );
// insertion in _cache2:
NOMAD::Cache_Point cp ( &x );
_cache2.insert ( cp );
x.set_in_cache ( true );
_sizeof += x.size_of();
}
/*-------------------------------------------------------------------------*/
/* . insert all points of 'c' in the current cache */
/* . this empties 'c' points (to avoid Eval_Point copies) */
/* . c._locked_file and this->_locked_file are different by construction */
/*-------------------------------------------------------------------------*/
void NOMAD::Cache::insert ( Cache & c )
{
if ( &c == this )
return;
// check the eval types:
if ( c._eval_type != _eval_type )
throw NOMAD::Cache::Cache_Error ( "Cache.cpp" , __LINE__ ,
"NOMAD::Cache:insert(c): c._eval_type != this->_eval_type" );
// insertion:
NOMAD::Point bbo_cache , bbo_cur;
const NOMAD::Eval_Point * cache_x;
const NOMAD::Eval_Point * cur = c.begin();
while ( cur ) {
cache_x = find ( *cur );
// the current point is already in cache:
if ( cache_x ) {
update ( get_modifiable_point ( *cache_x ) , *cur );
delete cur;
}
// point not in cache:
else
insert ( *cur );
cur = c.next();
}
c._sizeof = static_cast ( sizeof_init() );
c._cache1.clear();
c._cache2.clear();
c._cache3.clear();
c._extern_pts.clear();
}
/*------------------------------------------------------------------*/
/* . begin() and next() methods, to browse the cache */
/* */
/* . example of use: */
/* */
/* const NOMAD::Eval_Point * cur = cache.begin(); */
/* while ( cur ) { */
/* ... */
/* cur = cache.next(); */
/* } */
/* */
/* . we browse in this order: _cache2, _cache3, and _cache_1 */
/*------------------------------------------------------------------*/
// begin():
// --------
const NOMAD::Eval_Point * NOMAD::Cache::begin ( void ) const
{
if ( !_cache2.empty() ) {
_it = _cache2.begin();
return _it->get_point();
}
if ( !_cache3.empty() ) {
_it = _cache3.begin();
return _it->get_point();
}
if ( !_cache1.empty() ) {
_it = _cache1.begin();
return _it->get_point();
}
return NULL;
}
// next() (supposes that begin() has been called)
// -------
const NOMAD::Eval_Point * NOMAD::Cache::next ( void ) const
{
++_it;
if ( !_cache2.empty() && _it == _cache2.end() ) {
if ( !_cache3.empty() ) {
_it = _cache3.begin();
return _it->get_point();
}
if ( !_cache1.empty() ) {
_it = _cache1.begin();
return _it->get_point();
}
return NULL;
}
if ( !_cache3.empty() && _it == _cache3.end() ) {
if ( !_cache1.empty() ) {
_it = _cache1.begin();
return _it->get_point();
}
return NULL;
}
if ( !_cache1.empty() && _it == _cache1.end() )
return NULL;
return _it->get_point();
}
/*---------------------------------------------------------------------*/
/* check if a file is locked (private) */
/*---------------------------------------------------------------------*/
bool NOMAD::Cache::is_locked ( const std::string & file_name )
{
if ( file_name == _locked_file )
return true;
return ( Cache::_locked_files.find ( file_name ) != Cache::_locked_files.end() );
}
/*---------------------------------------------------------------------*/
/* lock a file (private) */
/*---------------------------------------------------------------------*/
bool NOMAD::Cache::lock ( const std::string & file_name )
{
if ( is_locked ( file_name ) )
return false;
Cache::_locked_files.insert ( file_name );
_locked_file = file_name;
return true;
}
/*---------------------------------------------------------------------*/
/* unlock the locked file (private) */
/*---------------------------------------------------------------------*/
void NOMAD::Cache::unlock ( void )
{
if ( _locked_file.empty() )
return;
std::set::iterator it = Cache::_locked_files.find ( _locked_file );
if ( it != Cache::_locked_files.end() )
_locked_files.erase(it);
_locked_file.clear();
}
/*----------------------------------------------------*/
/* . reads points from a cache file */
/* . fills the set _cache1 */
/* . private method */
/*----------------------------------------------------*/
bool NOMAD::Cache::read_points_from_cache_file ( std::ifstream & fin ,
const int * p_nb_bb_outputs ,
bool display )
{
try {
NOMAD::Clock c;
// the stream is placed at the first point (after the CACHE_FILE_ID tag):
fin.seekg ( sizeof ( NOMAD::CACHE_FILE_ID ) , std::ios::beg );
NOMAD::Cache_File_Point cfp;
NOMAD::Eval_Point * cur;
const NOMAD::Eval_Point * cache_x;
// main loop:
while ( !fin.eof() ) {
// reading of the Cache_File_Point:
if ( !cfp.read ( fin ) ) {
if ( fin.eof() )
break;
return false;
}
// we ignore this cache file point if it has a different
// number of blackbox outputs than *p_nb_bb_outputs:
if ( p_nb_bb_outputs && cfp.get_m() != *p_nb_bb_outputs )
continue;
// creation of the Eval_Point:
cur = new NOMAD::Eval_Point ( cfp , _eval_type );
// we look if the current point is already in cache:
cache_x = find ( *cur );
// the current point is already in cache:
if ( cache_x ) {
update ( get_modifiable_point ( *cache_x ) , *cur );
delete cur;
}
// point not in cache: insertion:
else {
// insertion in _extern_pts:
insert_extern_point ( *cur );
// insertion in _cache1:
NOMAD::Cache_Point cp ( cur );
_cache1.insert ( cp );
cur->set_in_cache ( true );
_sizeof += cur->size_of();
}
} // end of main loop
// display stats on the cache load:
if ( display ) {
_out << "number of points: " << static_cast(_cache1.size()) << std::endl
<< "size : ";
_out.display_size_of ( _sizeof );
_out << std::endl
<< "load time : " << c.get_real_time() << 's' << std::endl;
}
}
catch ( ... ) {
return false;
}
return true;
}
/*---------------------------------------------------------------------*/
/* . load a cache file (fill the set _cache1) */
/* . lock the file 'file_name' */
/* . return false if 'file_name' exists and is not a valid cache file */
/* . return false if 'file_name' is already locked */
/* . return false if the object already locked a file */
/* . return true if this object is already locked with this file */
/* (and just do nothing) */
/* . return true and create a valid cache file if 'file_name' */
/* does not exist */
/* . return false if the previous step did not work */
/* */
/* . parameter p_nb_bb_outputs (default=NULL): to indicate a number of */
/* blackbox outputs; points in file with a different value are */
/* ignored */
/*---------------------------------------------------------------------*/
bool NOMAD::Cache::load ( const std::string & file_name ,
const int * p_nb_bb_outputs ,
bool display )
{
if ( !file_name.empty() && file_name == _locked_file )
return true;
if ( file_name.empty() || !_locked_file.empty() || is_locked(file_name) )
return false;
// the file exists:
if ( NOMAD::check_read_file ( file_name ) ) {
int id;
std::ifstream fin ( file_name.c_str() , std::ios::binary );
fin.read ( (char *) &id , sizeof(int) );
// it is a valid cache file:
if ( !fin.fail() && id == NOMAD::CACHE_FILE_ID ) {
// display:
if ( display )
_out << std::endl
<< NOMAD::open_block ( "loading of \'" + file_name + "\'" );
// read the points:
if ( !read_points_from_cache_file ( fin , p_nb_bb_outputs , display ) ) {
fin.close();
return false; // it is not a valid cache file
}
// lock the file:
lock ( file_name );
fin.close();
if ( display )
_out.close_block();
return true;
}
// it is not a valid cache file:
else {
fin.close();
return false;
}
}
// the file does not exist:
else {
// display:
if ( display )
_out << std::endl << "creating cache file \'" << file_name << "\'" << std::endl;
// create the file as a valid cache file:
std::ofstream fout ( file_name.c_str() , std::ios::binary );
if ( fout.fail() ) {
fout.close();
return false;
}
fout.write ( (char *) &NOMAD::CACHE_FILE_ID , sizeof ( NOMAD::CACHE_FILE_ID ) );
fout.close();
// lock:
lock ( file_name );
}
return true;
}
/*------------------------------------------------------------------*/
/* if overwrite == false: */
/* . write points at the end of the locked cache file */
/* . write only points from _cache2 */
/* . transfer points from _cache2 to _cache3 */
/* else: */
/* . write all points in the locked cache file */
/*------------------------------------------------------------------*/
bool NOMAD::Cache::save ( bool overwrite , bool display )
{
if ( _locked_file.empty() )
return true;
// display:
if ( display )
_out << std::endl << "saving cache file \'" << _locked_file << "\'" << std::endl;
std::ofstream fout;
if ( overwrite ) {
// open:
fout.open ( _locked_file.c_str() , std::ios::binary );
if ( fout.fail() ) {
fout.close();
return false;
}
// cache file tag:
fout.write ( (char *) &NOMAD::CACHE_FILE_ID , sizeof ( NOMAD::CACHE_FILE_ID ) );
// save all cache points:
const NOMAD::Eval_Point * cur = begin();
while ( cur ) {
NOMAD::Cache_File_Point cfp ( *cur );
if ( !cfp.write ( fout ) ) {
fout.close();
return false;
}
cur = next();
}
}
else {
// open and go at the end of the file:
fout.open ( _locked_file.c_str() , std::ios::binary | std::ios::app );
if ( fout.fail() ) {
fout.close();
return false;
}
std::set::iterator it = _cache2.begin();
while ( it != _cache2.end() ) {
// write it->get_point() in 'fout':
NOMAD::Cache_File_Point cfp ( *it->get_point() );
if ( !cfp.write ( fout ) ) {
fout.close();
return false;
}
// transfer the point from _cache2 to _cache3:
NOMAD::Cache_Point cp = *it; // ( it->get_point() );
_cache3.insert ( cp );
_cache2.erase ( it++ );
}
}
// close the file:
fout.close();
return true;
}
/*-----------------------------------------------------------------*/
/* . update a point already in cache from another point with the */
/* same coordinates */
/* . if both points have a different number of blackbox outputs, */
/* they are not comparable and we set cache_x = x */
/* . private method */
/*-----------------------------------------------------------------*/
void NOMAD::Cache::update ( NOMAD::Eval_Point & cache_x ,
const NOMAD::Eval_Point & x ) const
{
const NOMAD::Point & bbo_x = x.get_bb_outputs();
if ( &cache_x == &x ||
!x.is_eval_ok() ||
!cache_x.is_in_cache() ||
bbo_x.empty() ||
cache_x != x )
return;
// check the eval types:
if ( x.get_eval_type () != _eval_type ||
cache_x.get_eval_type() != _eval_type )
throw NOMAD::Cache::Cache_Error ( "Cache.cpp" , __LINE__ ,
"NOMAD::Cache:update(): problem with the eval. types" );
const NOMAD::Point & bbo_cache_x = cache_x.get_bb_outputs();
int m = bbo_cache_x.size();
_sizeof -= cache_x.size_of();
// if the current point could not evaluate and x did,
// or if the number of bb outputs is different, we set cache_x = x:
if ( !cache_x.is_eval_ok() || m != bbo_x.size() ) {
cache_x.set_eval_status ( NOMAD::EVAL_OK );
cache_x.set_bb_output ( bbo_x );
cache_x.set_signature ( x.get_signature () );
cache_x.set_direction ( x.get_direction () );
cache_x.set_mesh_index ( x.get_mesh_index() );
_sizeof += cache_x.size_of();
return;
}
// we complete _bb_outputs:
int c1 = 0;
int c2 = 0;
for ( int i = 0 ; i < m ; ++i ) {
if ( bbo_cache_x[i].is_defined() )
++c1;
if ( bbo_x[i].is_defined() )
++c2;
if ( !bbo_cache_x[i].is_defined() && bbo_x[i].is_defined() )
cache_x.set_bb_output ( i , bbo_x[i] );
}
// the two points are 'eval_ok' and have comparable outputs:
// we select the best as the one with the more defined bb_outputs:
if ( c2 > c1 ) {
cache_x.set_signature ( x.get_signature () );
cache_x.set_direction ( x.get_direction () );
cache_x.set_mesh_index ( x.get_mesh_index() );
}
_sizeof += cache_x.size_of();
}
/*---------------------------------------------------------*/
/* display the list of extern points */
/*---------------------------------------------------------*/
void NOMAD::Cache::display_extern_pts ( const NOMAD::Display & out ) const
{
int nb = static_cast(_extern_pts.size());
int cnt = 0;
std::list::const_iterator it , end = _extern_pts.end();
for ( it = _extern_pts.begin() ; it != end ; ++it ) {
out << "point ";
out.display_int_w ( ++cnt , nb );
out << "/" << nb << ": ";
(*it)->display ( out , false );
out << std::endl;
}
}
/*---------------------------------------------------------*/
/* display */
/*---------------------------------------------------------*/
void NOMAD::Cache::display ( const NOMAD::Display & out ) const
{
out << "number of cache points: " << size() << std::endl
<< "size in memory : ";
out.display_size_of ( _sizeof );
out << std::endl << "cache file : ";
if ( _locked_file.empty() )
out << "-" << std::endl;
else
out << _locked_file << std::endl;
#ifdef DEBUG
int nb = size();
int cnt = 0;
out << NOMAD::open_block ( "cache points" ) << std::endl;
const NOMAD::Eval_Point * cur = begin();
while ( cur ) {
out << "point ";
out.display_int_w ( ++cnt , nb );
out << "/" << nb << ": ";
cur->display ( out , false );
out << std::endl;
cur = next();
}
out.close_block();
// this display is repeated here as there can
// be a lot of points displayed just before:
out << "number of cache points: " << size() << std::endl
<< "size in memory : ";
out.display_size_of ( _sizeof );
out << std::endl << "cache file : ";
if ( _locked_file.empty() )
out << "-";
else
out << _locked_file;
out << std::endl;
#endif
}