Differential Evolution C++ library
C:/dev/de/differentialevolution/de_constraints.hpp
00001 /*
00002  * Copyright (c) 2011 Adrian Michel
00003  * http://www.amichel.com
00004  *
00005  * Permission to use, copy, modify, distribute and sell this 
00006  * software and its documentation for any purpose is hereby 
00007  * granted without fee, provided that both the above copyright 
00008  * notice and this permission notice appear in all copies and in 
00009  * the supporting documentation. 
00010  *  
00011  * This library is distributed in the hope that it will be 
00012  * useful. However, Adrian Michel makes no representations about
00013  * the suitability of this software for any purpose.  It is 
00014  * provided "as is" without any express or implied warranty. 
00015  * 
00016  * Should you find this library useful, please email 
00017  * info@amichel.com with a link or other reference 
00018  * to your work. 
00019 */
00020 
00021 #ifndef DE_CONSTRAINTS_HPP_INCLUDED
00022 #define DE_CONSTRAINTS_HPP_INCLUDED
00023 
00024 // MS compatible compilers support #pragma once
00025 
00026 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
00027 #pragma once
00028 #endif
00029 
00030 #include <set>
00031 #include <boost/tokenizer.hpp>
00032 #include <boost/algorithm/string.hpp>
00033 #include <boost/format.hpp>
00034 #include <boost/make_shared.hpp>
00035 #include <boost/lexical_cast.hpp>
00036 
00037 #include "random_generator.hpp"
00038 #include "de_types.hpp"
00039 
00040 namespace de
00041 {
00042 
00048 class constraints_exception : public exception
00049 {
00050 public:
00058         constraints_exception( const std::string& message )
00059         : exception( message.c_str() )
00060         {
00061         }
00062 };
00063 
00072 class constraint
00073 {
00074 public:
00075         virtual ~constraint(){}
00076 
00085         virtual double get_rand_value() = 0;
00086 
00099         virtual double get_rand_value( double value, double origin ) = 0;
00100 
00108         virtual double min() const = 0;
00109 
00117         virtual double max() const = 0;
00118 
00134         virtual double get_rand_value_in_zone( double origin, double zonePct ) const = 0;
00135 
00144         virtual double get_middle_point() = 0;
00145 };
00146 
00150 typedef boost::shared_ptr< constraint > constraint_ptr;
00151 
00158 class range_constraint : public constraint
00159 {
00160 private:
00161         double m_min;
00162         double m_max;
00163 
00164 public:
00165 
00174         range_constraint( double min, double max )
00175         : m_min( min ), m_max( max )
00176         {
00177                 assert( min <= max );
00178         }
00179 
00187         double min() const { return m_min; }
00188 
00196         double max() const { return m_max; }
00197 
00198 };
00199 
00206 class real_constraint : public range_constraint
00207 {
00208 public:
00209 
00219         real_constraint( double min, double max )
00220         : range_constraint( min, max )
00221         {
00222                 assert( min <= max );
00223         }
00224 
00233         double get_rand_value()
00234         {
00235                 return genrand( range_constraint::min(), range_constraint::max() );
00236         }
00237 
00249         double get_rand_value( double value, double origin )
00250         {
00251                 double ret = value;
00252 
00253                 while( ret < range_constraint::min() )
00254                 {
00255                         ret = range_constraint::min() + genrand() * ( origin - range_constraint::min() );
00256                 }
00257 
00258                 while( ret > range_constraint::max() )
00259                 {
00260                         ret = range_constraint::max() + genrand() * ( origin - range_constraint::max() );
00261                 }
00262 
00263                 return ret;
00264         }
00265 
00266         virtual double get_rand_value_in_zone( double origin, double zonePct ) const
00267         {
00268                 if( origin > max() )
00269                         throw constraints_exception( "origin coordinate > max" );
00270                 if( origin < min() )
00271                         throw constraints_exception( "origin coordinate < min" );
00272 
00273                 if( zonePct > 100.0 )
00274                         throw constraints_exception( "zonePct > 100%" );
00275 
00276                 if( zonePct < 0 )
00277                         throw constraints_exception( "zonePct < 0%" );
00278 
00279                 if( zonePct == 0 )
00280                         throw constraints_exception( "zonePct == 0%" );
00281 
00282                 double zoneSize = ( max() - min() ) * zonePct / 100.0;
00283 
00284                 double _min = std::max( min(), origin - zoneSize/2.0 );
00285                 double _max = std::min( max(), origin + zoneSize/2.0 );
00286 
00287                 return genrand( _min, _max );
00288         }
00289 
00290         virtual double get_middle_point()
00291         {
00292                 return ( max() + min() )/2.0;
00293         }
00294 
00295 };
00296 
00303 class int_constraint : public range_constraint
00304 {
00305 public:
00315         int_constraint( double min, double max )
00316         : range_constraint( min, max )
00317         {
00318                 assert( min <= max );
00319         }
00320 
00329         double get_rand_value()
00330         {
00331                 return genintrand( range_constraint::min(), range_constraint::max() );
00332         }
00333 
00345         double get_rand_value( double value, double origin )
00346         {
00347                 double ret = boost::math::round( value );
00348 
00349                 while( ret < range_constraint::min() )
00350                 {
00351                         ret = range_constraint::min() + genrand() * ( origin - range_constraint::min() );
00352                         ret = boost::math::round( ret );
00353                 }
00354 
00355                 while( ret > range_constraint::max() )
00356                 {
00357                         ret = range_constraint::max() + genrand() * ( origin - range_constraint::max() );
00358                         ret = boost::math::round( ret );
00359                 }
00360 
00361                 return ret;
00362         }
00363 
00364         virtual double get_rand_value_in_zone( double origin, double zonePct ) const
00365         {
00366                 if ( origin > max() )
00367                         throw constraints_exception( "origin coordinate > max" );
00368                 if ( origin < min() )
00369                         throw constraints_exception( "origin coordinate < min" );
00370 
00371                 if ( zonePct > 100.0 )
00372                         throw constraints_exception( "zonePct > 100%" );
00373 
00374                 if( zonePct < 0 )
00375                         throw constraints_exception( "zonePct < 0%" );
00376 
00377                 if( zonePct == 0  )
00378                         throw constraints_exception( "zonePct == 0%" );
00379 
00380                 double zoneSize = ( max() - min() ) * zonePct/100.0;
00381 
00382                 double _min = std::max( min(), origin - zoneSize/2.0 );
00383                 double _max = std::min( max(), origin + zoneSize/2.0 );
00384 
00385                 double val = boost::math::round( genrand( _min, _max ) );
00386 
00387                 for ( ;val < _min || val > _max; val = boost::math::round( genrand( _min, _max ) ) );
00388 
00389                 return val;
00390         }
00391 
00392         virtual double get_middle_point()
00393         {
00394                 return boost::math::round( ( max() - min() )/2.0 );
00395         }
00396 
00397 
00398 };
00399 
00408 class set_constraint : public constraint
00409 {
00410 private:
00411         class unique : public std::unary_function < Double, bool >
00412         {
00413         public:
00414                 bool operator ()( Double d ) const  
00415                 {
00416                         return m_unique.insert( d ).second;
00417                 }
00418 
00419         public:
00420                 double min() const 
00421                 { 
00422                         if( m_unique.size() > 0 )
00423                                 return *m_unique.begin(); 
00424                         else
00425                                 throw constraints_exception( "could not get the min value of an empty set constraint" );
00426                 }
00427 
00428                 double max() const 
00429                 { 
00430                         if( m_unique.size() > 0 )
00431                                 return *m_unique.rbegin(); 
00432                         else
00433                                 throw constraints_exception( "could not get the max value of an empty set constraint" );
00434                 }
00435 
00436         private:
00437                 mutable std::set< Double > m_unique;
00438         };
00439 private:
00440         unique m_unique;
00441         de::DVector m_values;
00442 
00443 public:
00453         set_constraint( const de::DVector& values )
00454         {
00455                 // making sure the values in the "set" are unique
00456                 std::remove_copy_if (values.begin (), values.end (), std::back_inserter( m_values ), m_unique );
00457         }
00458 
00467         void add_value( de::Double value )
00468         {
00469                 if( m_unique( value ) )
00470                         m_values.push_back( value );
00471         }
00472 
00481         virtual double get_rand_value()
00482         {
00483                 de::DVector::size_type index( genintrand( 0, m_values.size() - 1 ) );
00484 
00485                 return m_values[ index ];
00486         }
00487 
00496         virtual double get_rand_value(double value, double origin)
00497         {
00498                 return get_rand_value();
00499         }
00500 
00508         double min() const
00509         {
00510                 return m_unique.min();
00511         }
00512 
00520         double max() const
00521         {
00522                 return m_unique.max();
00523         }
00524 
00525         virtual double get_rand_value_in_zone( double origin, double zonePct ) const
00526         {
00527                 throw constraints_exception( "get_rand_value_in_zone only supported for range constraints" );
00528         }
00529 
00530         virtual double get_middle_point()
00531         {
00532                 throw constraints_exception( "get_middle_point not supported by set constraint" );
00533         }
00534 
00535 };
00536 
00543 class boolean_constraint : public constraint
00544 {
00545 public:
00553         virtual double get_rand_value()
00554         {
00555                 return genrand() < 0.5;
00556         }
00557 
00565         virtual double get_rand_value(double value, double origin)
00566         {
00567                 return get_rand_value();
00568         }
00569 
00577         double min() const
00578         {
00579                 return 0;
00580         }
00581 
00589         double max() const
00590         {
00591                 return 1;
00592         }
00593 
00594         virtual double get_rand_value_in_zone( double origin, double zonePct ) const
00595         {
00596                 throw constraints_exception( "get_rand_value_in_zone only supported for range constraints" );
00597         }
00598 
00599         virtual double get_middle_point()
00600         {
00601                 throw constraints_exception( "get_middle_point not supported by bool constraint" );
00602         }
00603 
00604 };
00605 
00606 typedef std::vector< constraint_ptr > constraints_base;
00607 
00616 class constraints : public constraints_base
00617 {
00618 private:
00619         typedef boost::char_separator< char > separator;
00620         typedef boost::tokenizer< separator > tokenizer;
00621         
00622 
00623 public:
00635         constraints( size_t varCount, double defMin, double defMax )
00636         : constraints_base( varCount, boost::make_shared< real_constraint >( defMin, defMax ) )
00637         {
00638         }
00639 
00666         constraints( const std::vector< std::string >& str, size_t var_count , double def_min, double def_max )
00667         : constraints_base( var_count, boost::make_shared< real_constraint >( def_min, def_max ) )
00668         {
00669                 for( std::vector< std::string >::size_type i = 0; i < str.size(); ++i )
00670                 {
00671                         tokenizer tokens( str[ i ], separator( ";," ) );
00672 
00673                         std::string type;
00674                         double _min;
00675                         double _max;
00676 
00677                         size_t count( 0 );
00678 
00679                         for( tokenizer::const_iterator j = tokens.begin(); j != tokens.end(); ++j, ++count )
00680                         {
00681                                 const std::string token( boost::trim_copy( *j ) );
00682 
00683                                 try
00684                                 {
00685                                         switch( count )
00686                                         {
00687                                                 case 0:
00688                                                         type = token;
00689                                                         break;
00690                                                 case 1:
00691                                                         _min = boost::lexical_cast< double >( token.c_str() );
00692                                                         break;
00693                                                 case 2:
00694                                                         _max = boost::lexical_cast< double >( token.c_str() );
00695                                                         break;
00696                                                 default:
00697                                                         // too many fields
00698                                                         throw constraints_exception( ( boost::format( "wrong variable format in \"%1%\" - too many fields" ) % str[ i ] ).str() );
00699 
00700                                         }
00701                                 }
00702                                 catch( const boost::bad_lexical_cast& )
00703                                 {
00704                                         throw constraints_exception( ( boost::format( "wrong floating point number format: %1%") % token ).str() );
00705                                 }
00706                         }
00707 
00708                         // too few fields
00709                         if( count < 3 )
00710                                 throw constraints_exception( ( boost::format( "wrong variable format in \"%1%\" - too few fields" ) % str[ i ] ).str() );
00711 
00712                         if( i < var_count )
00713                                 constraints_base::at( i ) = str_to_constraint( type, _min, _max );
00714                         else
00715                                 constraints_base::push_back( str_to_constraint( type, _min, _max ) );
00716                 }
00717         }
00718 
00727         double get_rand_value( size_t index )
00728         {
00729                 if( index < constraints_base::size() )
00730                         return (*this)[ index ]->get_rand_value();
00731                 else
00732                         throw constraints_exception( ( boost::format( "invalid constraint index: %1%, higher than max number of constraints: %2%" ) % index % constraints_base::size() ).str() );
00733         }
00734 
00747         double get_rand_value( size_t index, double value, double origin )
00748         {
00749                 if( index < constraints_base::size() )
00750                         return (*this)[ index ]->get_rand_value( value, origin );
00751                 else
00752                         throw constraints_exception( ( boost::format( "invalid constraint index: %1%, higher than max number of constraints: %2%" ) % index % constraints_base::size() ).str() );
00753         }
00754 
00768         DVectorPtr get_square_zone_rand_values( const DVectorPtr origin, double sidePct ) const
00769         {
00770                 assert( origin );
00771                 assert( sidePct > 0 && sidePct <= 100 );
00772 
00773                 if( origin->size() == constraints_base::size() )
00774                 {
00775                         DVectorPtr square( boost::make_shared< DVector >( origin->size() ) );
00776 
00777                         for( constraints_base::size_type n = 0; n < constraints_base::size(); ++n )
00778                                 (*square)[ n ] = (*this)[ n ]->get_rand_value_in_zone( (*origin)[ n ], sidePct );
00779 
00780                         return square;
00781 
00782                 }
00783                 else
00784                         throw constraints_exception( "The origin vector must have the same number of elements as there are constraints" );
00785         }
00786 
00787         DVectorPtr get_middle_point()
00788         {
00789                 DVectorPtr r( boost::make_shared< DVector >( constraints_base::size() ) );
00790 
00791                 for( constraints_base::size_type n = 0; n < constraints_base::size(); ++n )
00792                         (*r)[ n ] = (*this)[ n ]->get_middle_point();
00793 
00794                 return r;
00795 
00796         }
00797 
00806         DVectorPtr get_rand_values() const
00807         {
00808                 DVectorPtr r( boost::make_shared< DVector >( constraints_base::size() ) );
00809 
00810                 for( constraints_base::size_type n = 0; n < constraints_base::size(); ++n )
00811                         (*r)[ n ] = (*this)[ n ]->get_rand_value();
00812 
00813                 return r;
00814         }
00815 
00816 
00817 private:
00818         constraint_ptr str_to_constraint( const std::string& type, double min, double max )
00819         {
00820                 if( boost::to_lower_copy( type ) == "real" )
00821                         return boost::make_shared< real_constraint >( min, max );
00822                 else if( boost::to_lower_copy( type ) == "int" || boost::to_lower_copy( type ) == "integer" )
00823                         return boost::make_shared< int_constraint >( min, max );
00824                 else
00825                         throw constraints_exception( ( boost::format( "invalid constraint type \"%1%\"" ) % type ).str() );
00826         }
00827 };
00828 
00829 typedef boost::shared_ptr< constraints > constraints_ptr;
00830 
00831 }
00832 
00833 #endif //DE_CONSTRAINTS_HPP_INCLUDED