k-dot-p 0.1

continuous_alloy_optimizer_1d.c++

Go to the documentation of this file.
00001 /*
00002  *Optimizes a material according to some number.
00003 
00004 
00005  Copyright (C) 2010 Joseph Pingenot
00006 
00007  This program is free software: you can redistribute it and/or modify
00008  it under the terms of the GNU Affero General Public License as published by
00009  the Free Software Foundation, either version 3 of the License, or
00010  (at your option) any later version.
00011 
00012  This program is distributed in the hope that it will be useful,
00013  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  GNU Affero General Public License for more details.
00016 
00017  You should have received a copy of the GNU Affero General Public License
00018  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 
00020 */
00021 
00022 #include <gsl/gsl_errno.h>
00023 #include <libmetacalc/continuous_alloy_optimizer_1d.h++>
00024 #include <libmodelxx/cartesian_grid.h++>
00025 #include <libmodelxx/randomness.h++>
00026 #include <libkdotp/gcc_hints.h>
00027 #include <libmodelxx/function_specification.h++>
00028 
00029 #define CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN 73
00030 
00031 #define EPSILON 1e-9
00032 
00033 using namespace kdotp::libmodelxx::grid;
00034 using namespace kdotp::libmodelxx::structure;
00035 
00036 namespace kdotp {
00037   namespace libmetacalc {
00038     namespace optimizer {
00039 
00040       continuous_alloy_optimizer_1d
00041       ::continuous_alloy_optimizer_1d(
00042            kdotp::libmodelxx::structure::material* marker_material,
00043            const char* _mat1,
00044            const char* _mat0,
00045            kdotp::libmodelxx::structure::generic_structure* s,
00046            set_point_callback _set_point,
00047            unsigned _blocksize,
00048            enum goal _goal,
00049            GError** err,
00050            void* privdat,
00051            struct matdb *_mdb,
00052            double _minx,
00053            double _maxx,
00054            long unsigned int rng_seed,
00055            double starting_stepsize,
00056            unsigned _N_blocks,
00057            GList* assignments,
00058            struct ::kdotp::libmodelxx::postprocessing::function_exp *assignment_expr,
00059            GHashTable *global_symbols
00060            )
00061   : mat1(NULL),
00062     mat0(NULL),
00063     current_assignments(NULL),
00064     material_is_optimum(NULL),
00065     genstruct(s),
00066     call_set_point_callback(_set_point),
00067     blocksize(_blocksize),
00068     points_in_block(NULL),
00069     point_start(NULL),
00070     previous_value(0),
00071     current_block(NULL),
00072     goal(_goal),
00073     seed(rng_seed),
00074     stepsize(starting_stepsize),
00075     mdb(_mdb),
00076     minx(_minx),
00077     maxx(_maxx)
00078       {
00079   cartesian_grid<material*, 1, 1>* matgrid = (cartesian_grid<material*, 1, 1>*)(genstruct->material_grid);
00080   /*Assign material strings.*/
00081   GString *str = g_string_new(_mat1);
00082   mat1=str->str;
00083   g_string_free(str, FALSE);
00084   str = g_string_new(_mat0);
00085   mat0=str->str;
00086   g_string_free(str, FALSE);
00087   /*Easy way to make this a GList: start off with list=NULL, create a new (start,end) struct
00088    *  on start, save it to the list on end.  leave the started variable as it is.
00089    */
00090   /*If marker_mat_in_grid is NULL, we've not started yet.*/
00091   material** marker_mat_in_grid = NULL;
00092   material* nullmat = NULL;
00093   for(unsigned X=0; X<matgrid->L[0]; X++) {
00094     if((**(matgrid->get_point_r(&X))) == *marker_material) {
00095       /*Set this point to NULL to prevent deleting this material*/
00096       matgrid->set_point_(&X, nullmat);
00097       if(marker_mat_in_grid == NULL) {
00098         start_X = X;
00099         marker_mat_in_grid = matgrid->get_point_r(&X);
00100       }
00101     }else if(marker_mat_in_grid != NULL) {
00102       /*We've stopped*/
00103       stop_X = X;
00104       /*Mark this region with NULL so we don't re-free the material.*/
00105       delete *marker_mat_in_grid;
00106       marker_mat_in_grid = NULL;
00107     }
00108   }
00109   if(marker_mat_in_grid != NULL) {
00110     /*We started but hit the end without switching out of the material. Odd, but solvable.*/
00111     stop_X = matgrid->L[0];
00112     /*Mark this region with NULL so we don't re-free the material.*/
00113     delete *marker_mat_in_grid;
00114   }
00115   /*We could probably do this with rounding, but this only happens once and works.*/
00116   if(_N_blocks > 0) {
00117     N_blocks = (unsigned)_N_blocks;
00118     blocksize = ((stop_X-start_X) - (stop_X-start_X)%N_blocks)/N_blocks;
00119   }else{
00120     N_blocks = ((stop_X-start_X) - (stop_X-start_X)%blocksize)/blocksize;
00121   }
00122   /*Allocate arrays*/
00123   points_in_block = new unsigned[N_blocks];
00124   point_start = new unsigned[N_blocks];
00125   current_assignments = new double[N_blocks];
00126   /*Previous assignments is NULL in order to know that we have no previous assignment or value.
00127    */
00128   material_is_optimum = new short unsigned[2*N_blocks];
00129   /*Map the points to the blocks*/
00130   for(unsigned i=0; i<N_blocks; i++) {
00131     points_in_block[i] = blocksize;
00132   }
00133   unsigned points_remaining = (stop_X - start_X)-blocksize*N_blocks;
00134   if(points_remaining != 0) {
00135     unsigned pos=points_remaining/N_blocks;
00136     for(unsigned pt=0; pt<points_remaining; pt++) {
00137       points_in_block[pos]++;
00138       pos += points_remaining/N_blocks;
00139       if(pos >= N_blocks) pos = pos%N_blocks;
00140     }
00141   }
00142   /*Put in the offsets.*/
00143   unsigned final_point = start_X;
00144   for(unsigned block=0; block<N_blocks; block++) {
00145     point_start[block] = final_point;
00146     final_point += points_in_block[block];
00147   }
00148 
00149   /*allocate and then seed the random number generator*/
00150   GError *e = NULL;
00151   gsl_rng_env_setup();
00152   rng = gsl_rng_alloc(gsl_rng_default);
00153   if(rng == NULL) {
00154     *err = g_error_new(CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 0, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::continuous_alloy_optimizer_1d(constructor): Failed to set up GSL random number generator.");
00155     return;
00156   }
00157   /*Set the seed randomly if it's not already set*/
00158   if(rng_seed == 0) {
00159     /*A seed value of 0 is special to GSL, so don't allow a seed of 0. Loop until it's not, or an error has occurred*/
00160     seed=0;
00161     while((seed == 0) && (e == NULL)) {
00162       seed = kdotp::libmodelxx::randomness::get_urandom_unsigned_long_int(&e);
00163     }
00164     if(e != NULL) {
00165       g_propagate_prefixed_error(err, e, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::continuous_alloy_optimizer_1d(constructor): Failed to get random seed:\n\t");
00166       return;
00167     }
00168     fprintf(stderr, "rng_seed was zero, so got new one (seed=%lu rng_seed=%lu)\n", seed, rng_seed);
00169   }else{
00170     fprintf(stderr, "rng_seed was non-zero, so keeping it (seed=%lu rng_seed=%lu)\n", seed, rng_seed);
00171   }
00172   fprintf(stderr, "\n\nseed is %lu\n\n", seed);
00173   /*We now seed the rng*/
00174   gsl_rng_set(rng, seed);
00175   /*Begin initialization.*/
00176   initialize(privdat);
00177   /*At this point, we should be set up.
00178    *The user should perform their first calculation, and call us back using one of the do_*_step funx.*/
00179       }
00180 
00181       void continuous_alloy_optimizer_1d::initialize(void* privdat) {
00182   if(current_block != NULL) {
00183     delete current_block;
00184     current_block = NULL;
00185   }
00186   reset_optimum_array();
00187   randomize_array(current_assignments, N_blocks);
00188   set_material_from_current_assignments(privdat);
00189       }
00190 
00191       void continuous_alloy_optimizer_1d::initialize(const short unsigned* state, void* privdat) {
00192   if(current_block != NULL) {
00193     delete current_block;
00194     current_block = NULL;
00195   }
00196   reset_optimum_array();
00197   for(unsigned i=0; i<N_blocks; i++) current_assignments[i] = state[i];
00198   set_material_from_current_assignments(privdat);
00199       }
00200 
00201       continuous_alloy_optimizer_1d::~continuous_alloy_optimizer_1d() {
00202   if(mat1 != NULL) g_free(mat1);
00203   if(mat0 != NULL) g_free(mat0);
00204   if(current_assignments != NULL) delete[] current_assignments;
00205   if(material_is_optimum != NULL) delete[] material_is_optimum;
00206   if(points_in_block != NULL) delete[] points_in_block;
00207   if(current_block != NULL) delete current_block;
00208       }
00209 
00210       /**Does the first step in the algorithm.
00211        *\param value the value that we're tracking
00212        *\param privdat pointer to data for the set_point callback.
00213        *\return 0 if we've not converged, or 1 if we've converged.
00214        */
00215       int continuous_alloy_optimizer_1d::do_first_step(double value, void* privdat) {
00216   /*There's no way we can yet be converged.  We don't even know if this
00217    *  point is optimal.  This is our first point.*/
00218   previous_value=value;
00219   current_block = new unsigned;
00220   double sign;
00221   *current_block = get_random_unoptimized_block(&sign);
00222   /*Save this block's current composition.*/
00223   prev_x = get_x(*current_block);
00224   step_block(*current_block, privdat);
00225   return 0;
00226       }
00227       /**Does the later steps in the algorithm
00228        *\param value the value that we're tracking
00229        *\param privdat pointer to data for the set_point callback.
00230        *\return 0 if we've not converged, or 1 if we've converged.
00231        */
00232       int continuous_alloy_optimizer_1d::do_subsequent_step(double value, void* privdat) {
00233   if(is_improved(value)) {
00234     /*This is an improved point!*/
00235     /*We don't know right now if any other points are optimized.*/
00236     reset_optimum_array();
00237     /*This direction (+ or -) succeeded, so the *other* direction will fail and therefore is
00238      *  already optimized.
00239      *So set that the *other* direction is optimized.
00240      */
00241     set_block_as_optimum((*current_block + N_blocks)%(2*N_blocks));
00242     previous_value = value;
00243     fprintf(stderr, "RESET ARRAY: previous_value=%g (value=%g)\n", previous_value, value);
00244   }else{
00245     /*Choosing this point worsened the structure.  Go back, set this as optimized, and choose a new point.*/
00246     /*Algorithm to get sign without a branch:
00247      *1) Get the real block
00248      *2) Get the offset between the signed and real block.  This will either be 0 or N_blocks
00249      *3) Divide by N_blocks/2 (maps to range [0, 2])
00250      *4) Subtract 1 (maps to range [-1, 1])
00251      *\note that we need -1 times this, to undo the previous step
00252      *\note that we now reset the block to its previous x instead of adding and then subtracting
00253      */
00254     set_block(*current_block, privdat, prev_x);
00255     fprintf(stderr, "REVERT BLOCK %u: previous_value=%g (value=%g)\n", *current_block, previous_value, value);
00256     /**We know that the block that just got tested was already optimal.
00257      *\note that we don't know whether the other direction (+ or -) is optimized!  */
00258     set_block_as_optimum(*current_block);
00259     /*Note that we don't set the previous value to this current value;
00260      *it's not an improvement!
00261      */
00262   }
00263   /*Check to see if they're all optimized.*/
00264   if(N_optimum == 2*N_blocks) {
00265     fprintf(stderr, "ALPHA IS LOCAL OPTIMUM: N_optimum=%u 2*N_blocks=%u\n", N_optimum, 2*N_blocks);
00266     return 1;
00267   }else{
00268     fprintf(stderr, "ALPHA IS (NOT) LOCAL OPTIMUM: N_optimum=%u 2*N_blocks=%u\n", N_optimum, 2*N_blocks);
00269   }
00270   /*Get the randomly-selected new block.*/
00271   double sign;
00272   bool success;
00273   do {
00274     *current_block = get_random_unoptimized_block(&sign);
00275     prev_x = get_x(*current_block);
00276     /*Set the block.*/
00277     success = step_block(*current_block, privdat);
00278     if(!success) {
00279       /*If we failed, this block is optimum!*/
00280       set_block_as_optimum(*current_block);
00281       /*We may have finished optimizing!*/
00282       if(N_optimum == 2*N_blocks) {
00283         fprintf(stderr, "stepping block %u failed, and ALPHA IS LOCAL OPTIMUM: N_optimum=%u 2*N_blocks=%u\n",  *current_block, N_optimum, 2*N_blocks);
00284         return 1;
00285       }else{
00286         fprintf(stderr, "stepping block %u failed, but ALPHA IS (NOT) LOCAL OPTIMUM: N_optimum=%u 2*N_blocks=%u\n", *current_block, N_optimum, 2*N_blocks);
00287       }
00288     }
00289   }while(!success);
00290   return 0;
00291       }
00292       /**Does any step in the algorithm, but checks to see if we've taken the first step yet
00293        *\param value the value that we're tracking
00294        *\param privdat pointer to data for the set_point callback.
00295        *\return 0 if we've not converged, or 1 if we've converged.
00296        */
00297       int continuous_alloy_optimizer_1d::do_generic_step(double value, void* privdat) {
00298   if(current_block == NULL) {
00299     return do_first_step(value, privdat);
00300   }
00301   return do_subsequent_step(value, privdat);
00302       }
00303 
00304       /*This performs the calculation without stepping through manually.
00305        *At each step, get_value is called with privdat
00306        *\param get_value callback to use after setting the materials.
00307        *\param privdat private data for the callback
00308        *\param step_fraction when we're optimized at the current stepsize, we multiply the stepsize by this factor to get the new stepsize
00309        *\param min_stepsize minimum stepsize to iterate over.  When stepsize gets below this value, we stop.
00310        *\return NULL if we converged; GError* if there was an error.
00311        *\note that if NaN is returned, we stop and return an error
00312        *\note that initialize() should have been called before calling this function, either directly or implicitly in the constructor.
00313        */
00314       GError* continuous_alloy_optimizer_1d::do_optimization(double (*get_value)(void* privdat, unsigned assignment, long unsigned iteration), void* get_value_privdat, void* set_point_privdat, double step_fraction, double min_stepsize) {
00315   long unsigned iteration=0;
00316   do {
00317     double value = get_value(get_value_privdat, 0, iteration++);
00318     if(unlikely(isnan(value))) {
00319       return g_error_new(CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::do_optimization: Got NaN (%g) from get_value callback in the %lu%s iteration.  Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th");
00320     }else if(unlikely(isinf(value))) {
00321       return g_error_new(CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::do_optimization: Got inf (%g) from get_value callback in the %lu%s iteration.  Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th");
00322     }
00323     do_first_step(value, set_point_privdat);
00324     do {
00325       value = get_value(get_value_privdat, 0, iteration++);
00326       if(unlikely(isnan(value))) {
00327         return g_error_new(CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::do_optimization: Got NaN (%g) from get_value callback in the %lu%s iteration.  Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th");
00328       }else if(unlikely(isinf(value))) {
00329         return g_error_new(CONTINUOUS_ALLOY_OPTIMIZER_1D_GERROR_DOMAIN, 101, "kdotp::libkdotp::optimizer::continuous_alloy_optimizer_1d.c::do_optimization: Got inf (%g) from get_value callback in the %lu%s iteration.  Iteration has been halted.\n", value, iteration, (iteration%10==1&&iteration!=11)?"st":(iteration%10==2&&iteration!=12)?"nd":(iteration%10==3&&iteration!=13)?"rd":"th");
00330       }
00331     }while(!(do_subsequent_step(value, set_point_privdat)));
00332     /*We can't optimize anymore at this step size.*/
00333     stepsize *= step_fraction;
00334     /*Free the current_block so it'll be reallocated after first iteration
00335      *Then reset our optimized steps..
00336      */
00337     delete current_block;
00338     reset_optimum_array();
00339   }while(stepsize >= min_stepsize);
00340   return NULL;
00341       }
00342 
00343     }
00344   }
00345 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines